diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml deleted file mode 100644 index 5d4fe58..0000000 --- a/.gitea/workflows/release.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Squawk Release workflow -run-name: ${{ gitea.actor }} is running Squawk Release workflow on release ${{ gitea.event.release.tag_name }} -on: - release: - types: [published] - -jobs: - Archlinux: - runs-on: archlinux - steps: - - name: Download the release tarball - run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz - - - name: Calculate SHA256 for the tarball - run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV - - - name: Unarchive tarball - run: tar -xvzf tarball.tar.gz - - - name: Clone the AUR repository - run: | - echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key - chmod 600 key - GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/squawk.git aur - chmod 777 -R aur - cd aur - git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }} - git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }} - - - - name: Copy PKGBUILD to the directory - run: cp squawk/packaging/Archlinux/PKGBUILD aur/ - - - name: Put SHA256 sum to PKGBUILD file, and generate .SRCINFO - working-directory: aur - run: | - sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD - sudo -u build makepkg --printsrcinfo > .SRCINFO - - - name: Commit package to aur - working-directory: aur - run: | - git add PKGBUILD .SRCINFO - git commit -m "${{ gitea.event.release.body//\"/\\\" }}" - GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push diff --git a/.gitmodules b/.gitmodules index 448dae5..bbe5364 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,3 @@ [submodule "external/qxmpp"] path = external/qxmpp url = https://github.com/qxmpp-project/qxmpp.git -[submodule "external/storage"] - path = external/storage - url = https://git.macaw.me/blue/storage -[submodule "external/lmdbal"] - path = external/lmdbal - url = gitea@git.macaw.me:blue/lmdbal.git diff --git a/.uncrustify.cfg b/.uncrustify.cfg deleted file mode 100644 index c333a42..0000000 --- a/.uncrustify.cfg +++ /dev/null @@ -1,3640 +0,0 @@ -# Uncrustify_d-0.77.1_f - -# -# General options -# - -# The type of line endings. -# -# Default: auto -newlines = lf # lf/crlf/cr/auto - -# The original size of tabs in the input. -# -# Default: 8 -input_tab_size = 8 # unsigned number - -# The size of tabs in the output (only used if align_with_tabs=true). -# -# Default: 8 -output_tab_size = 8 # unsigned number - -# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). -# -# Default: 92 -string_escape_char = 92 # unsigned number - -# Alternate string escape char (usually only used for Pawn). -# Only works right before the quote char. -string_escape_char2 = 0 # unsigned number - -# Replace tab characters found in string literals with the escape sequence \t -# instead. -string_replace_tab_chars = false # true/false - -# Allow interpreting '>=' and '>>=' as part of a template in code like -# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. -# Improvements to template detection may make this option obsolete. -tok_split_gte = false # true/false - -# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multi-line macros). -disable_processing_nl_cont = false # true/false - -# Specify the marker used in comments to disable processing of part of the -# file. -# -# Default: *INDENT-OFF* -disable_processing_cmt = " *INDENT-OFF*" # string - -# Specify the marker used in comments to (re)enable processing in a file. -# -# Default: *INDENT-ON* -enable_processing_cmt = " *INDENT-ON*" # string - -# Enable parsing of digraphs. -enable_digraphs = false # true/false - -# Option to allow both disable_processing_cmt and enable_processing_cmt -# strings, if specified, to be interpreted as ECMAScript regular expressions. -# If true, a regex search will be performed within comments according to the -# specified patterns in order to disable/enable processing. -processing_cmt_as_regex = false # true/false - -# Add or remove the UTF-8 BOM (recommend 'remove'). -utf8_bom = ignore # ignore/add/remove/force/not_defined - -# If the file contains bytes with values between 128 and 255, but is not -# UTF-8, then output as UTF-8. -utf8_byte = false # true/false - -# Force the output encoding to UTF-8. -utf8_force = false # true/false - -# -# Spacing options -# - -# Add or remove space around non-assignment symbolic operators ('+', '/', '%', -# '<<', and so forth). -sp_arith = force # ignore/add/remove/force/not_defined - -# Add or remove space around arithmetic operators '+' and '-'. -# -# Overrides sp_arith. -sp_arith_additive = force # ignore/add/remove/force/not_defined - -# Add or remove space around assignment operator '=', '+=', etc. -sp_assign = force # ignore/add/remove/force/not_defined - -# Add or remove space around '=' in C++11 lambda capture specifications. -# -# Overrides sp_assign. -sp_cpp_lambda_assign = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the capture specification of a C++11 lambda when -# an argument list is present, as in '[] (int x){ ... }'. -sp_cpp_lambda_square_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space after the capture specification of a C++11 lambda with -# no argument list is present, as in '[] { ... }'. -sp_cpp_lambda_square_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space after the opening parenthesis and before the closing -# parenthesis of a argument list of a C++11 lambda, as in -# '[]( int x ){ ... }'. -sp_cpp_lambda_argument_list = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the argument list of a C++11 lambda, as in -# '[](int x) { ... }'. -sp_cpp_lambda_paren_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between a lambda body and its call operator of an -# immediately invoked lambda, as in '[]( ... ){ ... } ( ... )'. -sp_cpp_lambda_fparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space around assignment operator '=' in a prototype. -# -# If set to ignore, use sp_assign. -sp_assign_default = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before assignment operator '=', '+=', etc. -# -# Overrides sp_assign. -sp_before_assign = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after assignment operator '=', '+=', etc. -# -# Overrides sp_assign. -sp_after_assign = ignore # ignore/add/remove/force/not_defined - -# Add or remove space in 'enum {'. -# -# Default: add -sp_enum_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space in 'NS_ENUM ('. -sp_enum_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space around assignment '=' in enum. -sp_enum_assign = force # ignore/add/remove/force/not_defined - -# Add or remove space before assignment '=' in enum. -# -# Overrides sp_enum_assign. -sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after assignment '=' in enum. -# -# Overrides sp_enum_assign. -sp_enum_after_assign = ignore # ignore/add/remove/force/not_defined - -# Add or remove space around assignment ':' in enum. -sp_enum_colon = add # ignore/add/remove/force/not_defined - -# Add or remove space around preprocessor '##' concatenation operator. -# -# Default: add -sp_pp_concat = add # ignore/add/remove/force/not_defined - -# Add or remove space after preprocessor '#' stringify operator. -# Also affects the '#@' charizing operator. -sp_pp_stringify = remove # ignore/add/remove/force/not_defined - -# Add or remove space before preprocessor '#' stringify operator -# as in '#define x(y) L#y'. -sp_before_pp_stringify = ignore # ignore/add/remove/force/not_defined - -# Add or remove space around boolean operators '&&' and '||'. -sp_bool = force # ignore/add/remove/force/not_defined - -# Add or remove space around compare operator '<', '>', '==', etc. -sp_compare = force # ignore/add/remove/force/not_defined - -# Add or remove space inside '(' and ')'. -sp_inside_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. -sp_paren_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. -sp_cparen_oparen = remove # ignore/add/remove/force/not_defined - -# Whether to balance spaces inside nested parentheses. -sp_balance_nested_parens = false # true/false - -# Add or remove space between ')' and '{'. -sp_paren_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between nested braces, i.e. '{{' vs. '{ {'. -sp_brace_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove space before pointer star '*'. -sp_before_ptr_star = remove # ignore/add/remove/force/not_defined - -# Add or remove space before pointer star '*' that isn't followed by a -# variable name. If set to ignore, sp_before_ptr_star is used instead. -sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a qualifier and a pointer star '*' that isn't -# followed by a variable name, as in '(char const *)'. If set to ignore, -# sp_before_ptr_star is used instead. -sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between pointer stars '*', as in 'int ***a;'. -sp_between_ptr_star = remove # ignore/add/remove/force/not_defined - -# Add or remove space after pointer star '*', if followed by a word. -# -# Overrides sp_type_func. -sp_after_ptr_star = force # ignore/add/remove/force/not_defined - -# Add or remove space after pointer caret '^', if followed by a word. -sp_after_ptr_block_caret = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after pointer star '*', if followed by a qualifier. -sp_after_ptr_star_qualifier = force # ignore/add/remove/force/not_defined - -# Add or remove space after a pointer star '*', if followed by a function -# prototype or function definition. -# -# Overrides sp_after_ptr_star and sp_type_func. -sp_after_ptr_star_func = force # ignore/add/remove/force/not_defined - -# Add or remove space after a pointer star '*' in the trailing return of a -# function prototype or function definition. -sp_after_ptr_star_trailing = force # ignore/add/remove/force/not_defined - -# Add or remove space between the pointer star '*' and the name of the variable -# in a function pointer definition. -sp_ptr_star_func_var = force # ignore/add/remove/force/not_defined - -# Add or remove space between the pointer star '*' and the name of the type -# in a function pointer type definition. -sp_ptr_star_func_type = force # ignore/add/remove/force/not_defined - -# Add or remove space after a pointer star '*', if followed by an open -# parenthesis, as in 'void* (*)()'. -sp_ptr_star_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space before a pointer star '*', if followed by a function -# prototype or function definition. If set to ignore, sp_before_ptr_star is -# used instead. -sp_before_ptr_star_func = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a qualifier and a pointer star '*' followed by -# the name of the function in a function prototype or definition, as in -# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead. -sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before a pointer star '*' in the trailing return of a -# function prototype or function definition. -sp_before_ptr_star_trailing = remove # ignore/add/remove/force/not_defined - -# Add or remove space between a qualifier and a pointer star '*' in the -# trailing return of a function prototype or function definition, as in -# 'auto foo() -> char const *'. -sp_qualifier_ptr_star_trailing = remove # ignore/add/remove/force/not_defined - -# Add or remove space before a reference sign '&'. -sp_before_byref = remove # ignore/add/remove/force/not_defined - -# Add or remove space before a reference sign '&' that isn't followed by a -# variable name. If set to ignore, sp_before_byref is used instead. -sp_before_unnamed_byref = remove # ignore/add/remove/force/not_defined - -# Add or remove space after reference sign '&', if followed by a word. -# -# Overrides sp_type_func. -sp_after_byref = force # ignore/add/remove/force/not_defined - -# Add or remove space after a reference sign '&', if followed by a function -# prototype or function definition. -# -# Overrides sp_after_byref and sp_type_func. -sp_after_byref_func = force # ignore/add/remove/force/not_defined - -# Add or remove space before a reference sign '&', if followed by a function -# prototype or function definition. -sp_before_byref_func = remove # ignore/add/remove/force/not_defined - -# Add or remove space after a reference sign '&', if followed by an open -# parenthesis, as in 'char& (*)()'. -sp_byref_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between type and word. In cases where total removal of -# whitespace would be a syntax error, a value of 'remove' is treated the same -# as 'force'. -# -# This also affects some other instances of space following a type that are -# not covered by other options; for example, between the return type and -# parenthesis of a function type template argument, between the type and -# parenthesis of an array parameter, or between 'decltype(...)' and the -# following word. -# -# Default: force -sp_after_type = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'decltype(...)' and word, -# brace or function call. -sp_after_decltype = ignore # ignore/add/remove/force/not_defined - -# (D) Add or remove space before the parenthesis in the D constructs -# 'template Foo(' and 'class Foo('. -sp_before_template_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'template' and '<'. -# If set to ignore, sp_before_angle is used. -sp_template_angle = force # ignore/add/remove/force/not_defined - -# Add or remove space before '<'. -sp_before_angle = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside '<' and '>'. -sp_inside_angle = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside '<>'. -sp_inside_angle_empty = remove # ignore/add/remove/force/not_defined - -# Add or remove space between '>' and ':'. -sp_angle_colon = remove # ignore/add/remove/force/not_defined - -# Add or remove space after '>'. -sp_after_angle = force # ignore/add/remove/force/not_defined - -# Add or remove space between '>' and '(' as found in 'new List(foo);'. -sp_angle_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between '>' and '()' as found in 'new List();'. -sp_angle_paren_empty = remove # ignore/add/remove/force/not_defined - -# Add or remove space between '>' and a word as in 'List m;' or -# 'template static ...'. -sp_angle_word = force # ignore/add/remove/force/not_defined - -# Add or remove space between '>' and '>' in '>>' (template stuff). -# -# Default: add -sp_angle_shift = remove # ignore/add/remove/force/not_defined - -# (C++11) Permit removal of the space between '>>' in 'foo >'. Note -# that sp_angle_shift cannot remove the space without this option. -sp_permit_cpp11_shift = true # true/false - -# Add or remove space before '(' of control statements ('if', 'for', 'switch', -# 'while', etc.). -sp_before_sparen = force # ignore/add/remove/force/not_defined - -# Add or remove space inside '(' and ')' of control statements other than -# 'for'. -sp_inside_sparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space after '(' of control statements other than 'for'. -# -# Overrides sp_inside_sparen. -sp_inside_sparen_open = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before ')' of control statements other than 'for'. -# -# Overrides sp_inside_sparen. -sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside '(' and ')' of 'for' statements. -sp_inside_for = remove # ignore/add/remove/force/not_defined - -# Add or remove space after '(' of 'for' statements. -# -# Overrides sp_inside_for. -sp_inside_for_open = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before ')' of 'for' statements. -# -# Overrides sp_inside_for. -sp_inside_for_close = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between '((' or '))' of control statements. -sp_sparen_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space after ')' of control statements. -sp_after_sparen = force # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and '{' of control statements. -sp_sparen_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'do' and '{'. -sp_do_brace_open = force # ignore/add/remove/force/not_defined - -# Add or remove space between '}' and 'while'. -sp_brace_close_while = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'while' and '('. Overrides sp_before_sparen. -sp_while_paren_open = force # ignore/add/remove/force/not_defined - -# (D) Add or remove space between 'invariant' and '('. -sp_invariant_paren = force # ignore/add/remove/force/not_defined - -# (D) Add or remove space after the ')' in 'invariant (C) c'. -sp_after_invariant_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space before empty statement ';' on 'if', 'for' and 'while'. -sp_special_semi = remove # ignore/add/remove/force/not_defined - -# Add or remove space before ';'. -# -# Default: remove -sp_before_semi = remove # ignore/add/remove/force/not_defined - -# Add or remove space before ';' in non-empty 'for' statements. -sp_before_semi_for = remove # ignore/add/remove/force/not_defined - -# Add or remove space before a semicolon of an empty left part of a for -# statement, as in 'for ( ; ; )'. -sp_before_semi_for_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between the semicolons of an empty middle part of a for -# statement, as in 'for ( ; ; )'. -sp_between_semi_for_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after ';', except when followed by a comment. -# -# Default: add -sp_after_semi = add # ignore/add/remove/force/not_defined - -# Add or remove space after ';' in non-empty 'for' statements. -# -# Default: force -sp_after_semi_for = force # ignore/add/remove/force/not_defined - -# Add or remove space after the final semicolon of an empty part of a for -# statement, as in 'for ( ; ; )'. -sp_after_semi_for_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before '[' (except '[]'). -sp_before_square = remove # ignore/add/remove/force/not_defined - -# Add or remove space before '[' for a variable definition. -# -# Default: remove -sp_before_vardef_square = remove # ignore/add/remove/force/not_defined - -# Add or remove space before '[' for asm block. -sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before '[]'. -sp_before_squares = remove # ignore/add/remove/force/not_defined - -# Add or remove space before C++17 structured bindings. -sp_cpp_before_struct_binding = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside a non-empty '[' and ']'. -sp_inside_square = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside '[]'. -sp_inside_square_empty = remove # ignore/add/remove/force/not_defined - -# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and -# ']'. If set to ignore, sp_inside_square is used. -sp_inside_square_oc_array = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. -sp_after_comma = force # ignore/add/remove/force/not_defined - -# Add or remove space before ',', i.e. 'a,b' vs. 'a ,b'. -# -# Default: remove -sp_before_comma = remove # ignore/add/remove/force/not_defined - -# (C#, Vala) Add or remove space between ',' and ']' in multidimensional array type -# like 'int[,,]'. -sp_after_mdatype_commas = ignore # ignore/add/remove/force/not_defined - -# (C#, Vala) Add or remove space between '[' and ',' in multidimensional array type -# like 'int[,,]'. -sp_before_mdatype_commas = ignore # ignore/add/remove/force/not_defined - -# (C#, Vala) Add or remove space between ',' in multidimensional array type -# like 'int[,,]'. -sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between an open parenthesis and comma, -# i.e. '(,' vs. '( ,'. -# -# Default: force -sp_paren_comma = force # ignore/add/remove/force/not_defined - -# Add or remove space between a type and ':'. -sp_type_colon = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the variadic '...' when preceded by a -# non-punctuator. -# The value REMOVE will be overridden with FORCE -sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before the variadic '...' when preceded by a -# non-punctuator. -# The value REMOVE will be overridden with FORCE -sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a type and '...'. -sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a '*' and '...'. -sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and '...'. -sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between '&&' and '...'. -sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and a qualifier such as 'const'. -sp_paren_qualifier = force # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and 'noexcept'. -sp_paren_noexcept = force # ignore/add/remove/force/not_defined - -# Add or remove space after class ':'. -sp_after_class_colon = force # ignore/add/remove/force/not_defined - -# Add or remove space before class ':'. -sp_before_class_colon = force # ignore/add/remove/force/not_defined - -# Add or remove space after class constructor ':'. -# -# Default: add -sp_after_constr_colon = add # ignore/add/remove/force/not_defined - -# Add or remove space before class constructor ':'. -# -# Default: add -sp_before_constr_colon = remove # ignore/add/remove/force/not_defined - -# Add or remove space before case ':'. -# -# Default: remove -sp_before_case_colon = remove # ignore/add/remove/force/not_defined - -# Add or remove space between 'operator' and operator sign. -sp_after_operator = force # ignore/add/remove/force/not_defined - -# Add or remove space between the operator symbol and the open parenthesis, as -# in 'operator ++('. -sp_after_operator_sym = force # ignore/add/remove/force/not_defined - -# Overrides sp_after_operator_sym when the operator has no arguments, as in -# 'operator *()'. -sp_after_operator_sym_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or -# '(int)a' vs. '(int) a'. -sp_after_cast = remove # ignore/add/remove/force/not_defined - -# Add or remove spaces inside cast parentheses. -sp_inside_paren_cast = remove # ignore/add/remove/force/not_defined - -# Add or remove space between the type and open parenthesis in a C++ cast, -# i.e. 'int(exp)' vs. 'int (exp)'. -sp_cpp_cast_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between 'sizeof' and '('. -sp_sizeof_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between 'sizeof' and '...'. -sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'sizeof...' and '('. -sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between '...' and a parameter pack. -sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a parameter pack and '...'. -sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'decltype' and '('. -sp_decltype_paren = ignore # ignore/add/remove/force/not_defined - -# (Pawn) Add or remove space after the tag keyword. -sp_after_tag = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside enum '{' and '}'. -sp_inside_braces_enum = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside struct/union '{' and '}'. -sp_inside_braces_struct = remove # ignore/add/remove/force/not_defined - -# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' -sp_inside_braces_oc_dict = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after open brace in an unnamed temporary -# direct-list-initialization -# if statement is a brace_init_lst -# works only if sp_brace_brace is set to ignore. -sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before close brace in an unnamed temporary -# direct-list-initialization -# if statement is a brace_init_lst -# works only if sp_brace_brace is set to ignore. -sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside an unnamed temporary direct-list-initialization -# if statement is a brace_init_lst -# works only if sp_brace_brace is set to ignore -# works only if sp_before_type_brace_init_lst_close is set to ignore. -sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside '{' and '}'. -sp_inside_braces = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside '{}'. -sp_inside_braces_empty = remove # ignore/add/remove/force/not_defined - -# Add or remove space around trailing return operator '->'. -sp_trailing_return = return # ignore/add/remove/force/not_defined - -# Add or remove space between return type and function name. A minimum of 1 -# is forced except for pointer return types. -sp_type_func = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between type and open brace of an unnamed temporary -# direct-list-initialization. -sp_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '(' on function declaration. -sp_func_proto_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '()' on function declaration -# without parameters. -sp_func_proto_paren_empty = force # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '(' with a typedef specifier. -sp_func_type_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between alias name and '(' of a non-pointer function type typedef. -sp_func_def_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '()' on function definition -# without parameters. -sp_func_def_paren_empty = force # ignore/add/remove/force/not_defined - -# Add or remove space inside empty function '()'. -# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_fparens = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside function '(' and ')'. -sp_inside_fparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside user functor '(' and ')'. -sp_func_call_user_inside_rparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside empty functor '()'. -# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_rparens = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside functor '(' and ')'. -sp_inside_rparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space inside the first parentheses in a function type, as in -# 'void (*x)(...)'. -sp_inside_tparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space between the ')' and '(' in a function type, as in -# 'void (*x)(...)'. -sp_after_tparen_close = force # ignore/add/remove/force/not_defined - -# Add or remove space between ']' and '(' when part of a function call. -sp_square_fparen = remove # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and '{' of function. -sp_fparen_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and '{' of a function call in object -# initialization. -# -# Overrides sp_fparen_brace. -sp_fparen_brace_initializer = force # ignore/add/remove/force/not_defined - -# (Java) Add or remove space between ')' and '{{' of double brace initializer. -sp_fparen_dbrace = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '(' on function calls. -sp_func_call_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between function name and '()' on function calls without -# parameters. If set to ignore (the default), sp_func_call_paren is used. -sp_func_call_paren_empty = remove # ignore/add/remove/force/not_defined - -# Add or remove space between the user function name and '(' on function -# calls. You need to set a keyword to be a user function in the config file, -# like: -# set func_call_user tr _ i18n -sp_func_call_user_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside user function '(' and ')'. -sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between nested parentheses with user functions, -# i.e. '((' vs. '( ('. -sp_func_call_user_paren_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a constructor/destructor and the open -# parenthesis. -sp_func_class_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between a constructor without parameters or destructor -# and '()'. -sp_func_class_paren_empty = force # ignore/add/remove/force/not_defined - -# Add or remove space after 'return'. -# -# Default: force -sp_return = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'return' and '('. -sp_return_paren = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'return' and '{'. -sp_return_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between '__attribute__' and '('. -sp_attribute_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. -sp_defined_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'throw' and '(' in 'throw (something)'. -sp_throw_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'throw' and anything other than '(' as in -# '@throw [...];'. -sp_after_throw = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'catch' and '(' in 'catch (something) { }'. -# If set to ignore, sp_before_sparen is used. -sp_catch_paren = force # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between '@catch' and '(' -# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. -sp_oc_catch_paren = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before Objective-C protocol list -# as in '@protocol Protocol' or '@interface MyClass : NSObject'. -sp_before_oc_proto_list = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between class name and '(' -# in '@interface className(categoryName):BaseClass' -sp_oc_classname_paren = ignore # ignore/add/remove/force/not_defined - -# (D) Add or remove space between 'version' and '(' -# in 'version (something) { }'. If set to ignore, sp_before_sparen is used. -sp_version_paren = ignore # ignore/add/remove/force/not_defined - -# (D) Add or remove space between 'scope' and '(' -# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. -sp_scope_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'super' and '(' in 'super (something)'. -# -# Default: remove -sp_super_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between 'this' and '(' in 'this (something)'. -# -# Default: remove -sp_this_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove space between a macro name and its definition. -sp_macro = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a macro function ')' and its definition. -sp_macro_func = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between 'else' and '{' if on the same line. -sp_else_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between '}' and 'else' if on the same line. -sp_brace_else = force # ignore/add/remove/force/not_defined - -# Add or remove space between '}' and the name of a typedef on the same line. -sp_brace_typedef = force # ignore/add/remove/force/not_defined - -# Add or remove space before the '{' of a 'catch' statement, if the '{' and -# 'catch' are on the same line, as in 'catch (decl) {'. -sp_catch_brace = force # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' -# and '@catch' are on the same line, as in '@catch (decl) {'. -# If set to ignore, sp_catch_brace is used. -sp_oc_catch_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between '}' and 'catch' if on the same line. -sp_brace_catch = force # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between '}' and '@catch' if on the same line. -# If set to ignore, sp_brace_catch is used. -sp_oc_brace_catch = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'finally' and '{' if on the same line. -sp_finally_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between '}' and 'finally' if on the same line. -sp_brace_finally = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'try' and '{' if on the same line. -sp_try_brace = force # ignore/add/remove/force/not_defined - -# Add or remove space between get/set and '{' if on the same line. -sp_getset_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between a variable and '{' for C++ uniform -# initialization. -sp_word_brace_init_lst = remove # ignore/add/remove/force/not_defined - -# Add or remove space between a variable and '{' for a namespace. -# -# Default: add -sp_word_brace_ns = add # ignore/add/remove/force/not_defined - -# Add or remove space before the '::' operator. -sp_before_dc = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the '::' operator. -sp_after_dc = remove # ignore/add/remove/force/not_defined - -# (D) Add or remove around the D named array initializer ':' operator. -sp_d_array_colon = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the '!' (not) unary operator. -# -# Default: remove -sp_not = remove # ignore/add/remove/force/not_defined - -# Add or remove space between two '!' (not) unary operators. -# If set to ignore, sp_not will be used. -sp_not_not = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the '~' (invert) unary operator. -# -# Default: remove -sp_inv = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the '&' (address-of) unary operator. This does not -# affect the spacing after a '&' that is part of a type. -# -# Default: remove -sp_addr = remove # ignore/add/remove/force/not_defined - -# Add or remove space around the '.' or '->' operators. -# -# Default: remove -sp_member = remove # ignore/add/remove/force/not_defined - -# Add or remove space after the '*' (dereference) unary operator. This does -# not affect the spacing after a '*' that is part of a type. -# -# Default: remove -sp_deref = remove # ignore/add/remove/force/not_defined - -# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. -# -# Default: remove -sp_sign = remove # ignore/add/remove/force/not_defined - -# Add or remove space between '++' and '--' the word to which it is being -# applied, as in '(--x)' or 'y++;'. -# -# Default: remove -sp_incdec = remove # ignore/add/remove/force/not_defined - -# Add or remove space before a backslash-newline at the end of a line. -# -# Default: add -sp_before_nl_cont = add # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' -# or '+(int) bar;'. -sp_after_oc_scope = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the colon in message specs, -# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. -sp_after_oc_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before the colon in message specs, -# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. -sp_before_oc_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the colon in immutable dictionary expression -# 'NSDictionary *test = @{@"foo" :@"bar"};'. -sp_after_oc_dict_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before the colon in immutable dictionary expression -# 'NSDictionary *test = @{@"foo" :@"bar"};'. -sp_before_oc_dict_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the colon in message specs, -# i.e. '[object setValue:1];' vs. '[object setValue: 1];'. -sp_after_send_oc_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before the colon in message specs, -# i.e. '[object setValue:1];' vs. '[object setValue :1];'. -sp_before_send_oc_colon = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the (type) in message specs, -# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. -sp_after_oc_type = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after the first (type) in message specs, -# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. -sp_after_oc_return_type = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between '@selector' and '(', -# i.e. '@selector(msgName)' vs. '@selector (msgName)'. -# Also applies to '@protocol()' constructs. -sp_after_oc_at_sel = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between '@selector(x)' and the following word, -# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. -sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space inside '@selector' parentheses, -# i.e. '@selector(foo)' vs. '@selector( foo )'. -# Also applies to '@protocol()' constructs. -sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space before a block pointer caret, -# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. -sp_before_oc_block_caret = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after a block pointer caret, -# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. -sp_after_oc_block_caret = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between the receiver and selector in a message, -# as in '[receiver selector ...]'. -sp_after_oc_msg_receiver = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space after '@property'. -sp_after_oc_property = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove space between '@synchronized' and the open parenthesis, -# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. -sp_after_oc_synchronized = ignore # ignore/add/remove/force/not_defined - -# Add or remove space around the ':' in 'b ? t : f'. -sp_cond_colon = force # ignore/add/remove/force/not_defined - -# Add or remove space before the ':' in 'b ? t : f'. -# -# Overrides sp_cond_colon. -sp_cond_colon_before = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the ':' in 'b ? t : f'. -# -# Overrides sp_cond_colon. -sp_cond_colon_after = ignore # ignore/add/remove/force/not_defined - -# Add or remove space around the '?' in 'b ? t : f'. -sp_cond_question = force # ignore/add/remove/force/not_defined - -# Add or remove space before the '?' in 'b ? t : f'. -# -# Overrides sp_cond_question. -sp_cond_question_before = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the '?' in 'b ? t : f'. -# -# Overrides sp_cond_question. -sp_cond_question_after = ignore # ignore/add/remove/force/not_defined - -# In the abbreviated ternary form '(a ?: b)', add or remove space between '?' -# and ':'. -# -# Overrides all other sp_cond_* options. -sp_cond_ternary_short = ignore # ignore/add/remove/force/not_defined - -# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make -# sense here. -sp_case_label = force # ignore/add/remove/force/not_defined - -# (D) Add or remove space around the D '..' operator. -sp_range = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after ':' in a Java/C++11 range-based 'for', -# as in 'for (Type var : expr)'. -sp_after_for_colon = force # ignore/add/remove/force/not_defined - -# Add or remove space before ':' in a Java/C++11 range-based 'for', -# as in 'for (Type var : expr)'. -sp_before_for_colon = force # ignore/add/remove/force/not_defined - -# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. -sp_extern_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the opening of a C++ comment, as in '// A'. -sp_cmt_cpp_start = remove # ignore/add/remove/force/not_defined - -# remove space after the '//' and the pvs command '-V1234', -# only works with sp_cmt_cpp_start set to add or force. -sp_cmt_cpp_pvs = false # true/false - -# remove space after the '//' and the command 'lint', -# only works with sp_cmt_cpp_start set to add or force. -sp_cmt_cpp_lint = false # true/false - -# Add or remove space in a C++ region marker comment, as in '// BEGIN'. -# A region marker is defined as a comment which is not preceded by other text -# (i.e. the comment is the first non-whitespace on the line), and which starts -# with either 'BEGIN' or 'END'. -# -# Overrides sp_cmt_cpp_start. -sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined - -# If true, space added with sp_cmt_cpp_start will be added after Doxygen -# sequences like '///', '///<', '//!' and '//!<'. -sp_cmt_cpp_doxygen = false # true/false - -# If true, space added with sp_cmt_cpp_start will be added after Qt translator -# or meta-data comments like '//:', '//=', and '//~'. -sp_cmt_cpp_qttr = false # true/false - -# Add or remove space between #else or #endif and a trailing comment. -sp_endif_cmt = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after 'new', 'delete' and 'delete[]'. -sp_after_new = force # ignore/add/remove/force/not_defined - -# Add or remove space between 'new' and '(' in 'new()'. -sp_between_new_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space between ')' and type in 'new(foo) BAR'. -sp_after_newop_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space inside parentheses of the new operator -# as in 'new(foo) BAR'. -sp_inside_newop_paren = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after the open parenthesis of the new operator, -# as in 'new(foo) BAR'. -# -# Overrides sp_inside_newop_paren. -sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before the close parenthesis of the new operator, -# as in 'new(foo) BAR'. -# -# Overrides sp_inside_newop_paren. -sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined - -# Add or remove space before a trailing comment. -sp_before_tr_cmt = ignore # ignore/add/remove/force/not_defined - -# Number of spaces before a trailing comment. -sp_num_before_tr_cmt = 0 # unsigned number - -# Add or remove space before an embedded comment. -# -# Default: force -sp_before_emb_cmt = force # ignore/add/remove/force/not_defined - -# Number of spaces before an embedded comment. -# -# Default: 1 -sp_num_before_emb_cmt = 1 # unsigned number - -# Add or remove space after an embedded comment. -# -# Default: force -sp_after_emb_cmt = force # ignore/add/remove/force/not_defined - -# Number of spaces after an embedded comment. -# -# Default: 1 -sp_num_after_emb_cmt = 1 # unsigned number - -# (Java) Add or remove space between an annotation and the open parenthesis. -sp_annotation_paren = ignore # ignore/add/remove/force/not_defined - -# If true, vbrace tokens are dropped to the previous token and skipped. -sp_skip_vbrace_tokens = false # true/false - -# Add or remove space after 'noexcept'. -sp_after_noexcept = ignore # ignore/add/remove/force/not_defined - -# Add or remove space after '_'. -sp_vala_after_translation = ignore # ignore/add/remove/force/not_defined - -# If true, a is inserted after #define. -force_tab_after_define = false # true/false - -# -# Indenting options -# - -# The number of columns to indent per level. Usually 2, 3, 4, or 8. -# -# Default: 8 -indent_columns = 4 # unsigned number - -# Whether to ignore indent for the first continuation line. Subsequent -# continuation lines will still be indented to match the first. -indent_ignore_first_continue = false # true/false - -# The continuation indent. If non-zero, this overrides the indent of '(', '[' -# and '=' continuation indents. Negative values are OK; negative value is -# absolute and not increased for each '(' or '[' level. -# -# For FreeBSD, this is set to 4. -# Requires indent_ignore_first_continue=false. -indent_continue = 0 # number - -# The continuation indent, only for class header line(s). If non-zero, this -# overrides the indent of 'class' continuation indents. -# Requires indent_ignore_first_continue=false. -indent_continue_class_head = 0 # unsigned number - -# Whether to indent empty lines (i.e. lines which contain only spaces before -# the newline character). -indent_single_newlines = false # true/false - -# The continuation indent for func_*_param if they are true. If non-zero, this -# overrides the indent. -indent_param = 0 # unsigned number - -# How to use tabs when indenting code. -# -# 0: Spaces only -# 1: Indent with tabs to brace level, align with spaces (default) -# 2: Indent and align with tabs, using spaces when not on a tabstop -# -# Default: 1 -indent_with_tabs = 0 # unsigned number - -# Whether to indent comments that are not at a brace level with tabs on a -# tabstop. Requires indent_with_tabs=2. If false, will use spaces. -indent_cmt_with_tabs = false # true/false - -# Whether to indent strings broken by '\' so that they line up. -indent_align_string = true # true/false - -# The number of spaces to indent multi-line XML strings. -# Requires indent_align_string=true. -indent_xml_string = 2 # unsigned number - -# Spaces to indent '{' from level. -indent_brace = 0 # unsigned number - -# Whether braces are indented to the body level. -indent_braces = false # true/false - -# Whether to disable indenting function braces if indent_braces=true. -indent_braces_no_func = false # true/false - -# Whether to disable indenting class braces if indent_braces=true. -indent_braces_no_class = false # true/false - -# Whether to disable indenting struct braces if indent_braces=true. -indent_braces_no_struct = false # true/false - -# Whether to indent based on the size of the brace parent, -# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. -indent_brace_parent = false # true/false - -# Whether to indent based on the open parenthesis instead of the open brace -# in '({\n'. -indent_paren_open_brace = false # true/false - -# (C#) Whether to indent the brace of a C# delegate by another level. -indent_cs_delegate_brace = false # true/false - -# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by -# another level. -indent_cs_delegate_body = false # true/false - -# Whether to indent the body of a 'namespace'. -indent_namespace = false # true/false - -# Whether to indent only the first namespace, and not any nested namespaces. -# Requires indent_namespace=true. -indent_namespace_single_indent = false # true/false - -# The number of spaces to indent a namespace block. -# If set to zero, use the value indent_columns -indent_namespace_level = 0 # unsigned number - -# If the body of the namespace is longer than this number, it won't be -# indented. Requires indent_namespace=true. 0 means no limit. -indent_namespace_limit = 0 # unsigned number - -# Whether to indent only in inner namespaces (nested in other namespaces). -# Requires indent_namespace=true. -indent_namespace_inner_only = false # true/false - -# Whether the 'extern "C"' body is indented. -indent_extern = false # true/false - -# Whether the 'class' body is indented. -indent_class = true # true/false - -# Whether to ignore indent for the leading base class colon. -indent_ignore_before_class_colon = false # true/false - -# Additional indent before the leading base class colon. -# Negative values decrease indent down to the first column. -# Requires indent_ignore_before_class_colon=false and a newline break before -# the colon (see pos_class_colon and nl_class_colon) -indent_before_class_colon = 0 # number - -# Whether to indent the stuff after a leading base class colon. -indent_class_colon = false # true/false - -# Whether to indent based on a class colon instead of the stuff after the -# colon. Requires indent_class_colon=true. -indent_class_on_colon = false # true/false - -# Whether to ignore indent for a leading class initializer colon. -indent_ignore_before_constr_colon = false # true/false - -# Whether to indent the stuff after a leading class initializer colon. -indent_constr_colon = true # true/false - -# Virtual indent from the ':' for leading member initializers. -# -# Default: 2 -indent_ctor_init_leading = 2 # unsigned number - -# Virtual indent from the ':' for following member initializers. -# -# Default: 2 -indent_ctor_init_following = 2 # unsigned number - -# Additional indent for constructor initializer list. -# Negative values decrease indent down to the first column. -indent_ctor_init = 0 # number - -# Whether to indent 'if' following 'else' as a new block under the 'else'. -# If false, 'else\nif' is treated as 'else if' for indenting purposes. -indent_else_if = false # true/false - -# Amount to indent variable declarations after a open brace. -# -# <0: Relative -# >=0: Absolute -indent_var_def_blk = 0 # number - -# Whether to indent continued variable declarations instead of aligning. -indent_var_def_cont = true # true/false - -# How to indent continued shift expressions ('<<' and '>>'). -# Set align_left_shift=false when using this. -# 0: Align shift operators instead of indenting them (default) -# 1: Indent by one level -# -1: Preserve original indentation -indent_shift = 0 # number - -# Whether to force indentation of function definitions to start in column 1. -indent_func_def_force_col1 = false # true/false - -# Whether to indent continued function call parameters one indent level, -# rather than aligning parameters under the open parenthesis. -indent_func_call_param = true # true/false - -# Whether to indent continued function definition parameters one indent level, -# rather than aligning parameters under the open parenthesis. -indent_func_def_param = false # true/false - -# for function definitions, only if indent_func_def_param is false -# Allows to align params when appropriate and indent them when not -# behave as if it was true if paren position is more than this value -# if paren position is more than the option value -indent_func_def_param_paren_pos_threshold = 0 # unsigned number - -# Whether to indent continued function call prototype one indent level, -# rather than aligning parameters under the open parenthesis. -indent_func_proto_param = true # true/false - -# Whether to indent continued function call declaration one indent level, -# rather than aligning parameters under the open parenthesis. -indent_func_class_param = true # true/false - -# Whether to indent continued class variable constructors one indent level, -# rather than aligning parameters under the open parenthesis. -indent_func_ctor_var_param = true # true/false - -# Whether to indent continued template parameter list one indent level, -# rather than aligning parameters under the open parenthesis. -indent_template_param = true # true/false - -# Double the indent for indent_func_xxx_param options. -# Use both values of the options indent_columns and indent_param. -indent_func_param_double = false # true/false - -# Indentation column for standalone 'const' qualifier on a function -# prototype. -indent_func_const = 0 # unsigned number - -# Indentation column for standalone 'throw' qualifier on a function -# prototype. -indent_func_throw = 0 # unsigned number - -# How to indent within a macro followed by a brace on the same line -# This allows reducing the indent in macros that have (for example) -# `do { ... } while (0)` blocks bracketing them. -# -# true: add an indent for the brace on the same line as the macro -# false: do not add an indent for the brace on the same line as the macro -# -# Default: true -indent_macro_brace = true # true/false - -# The number of spaces to indent a continued '->' or '.'. -# Usually set to 0, 1, or indent_columns. -indent_member = 0 # unsigned number - -# Whether lines broken at '.' or '->' should be indented by a single indent. -# The indent_member option will not be effective if this is set to true. -indent_member_single = true # true/false - -# Spaces to indent single line ('//') comments on lines before code. -indent_single_line_comments_before = 0 # unsigned number - -# Spaces to indent single line ('//') comments on lines after code. -indent_single_line_comments_after = 0 # unsigned number - -# When opening a paren for a control statement (if, for, while, etc), increase -# the indent level by this value. Negative values decrease the indent level. -indent_sparen_extra = 0 # number - -# Whether to indent trailing single line ('//') comments relative to the code -# instead of trying to keep the same absolute column. -indent_relative_single_line_comments = false # true/false - -# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. -# It might be wise to choose the same value for the option indent_case_brace. -indent_switch_case = indent_columns # unsigned number - -# Spaces to indent the body of a 'switch' before any 'case'. -# Usually the same as indent_columns or indent_switch_case. -indent_switch_body = indent_columns # unsigned number - -# Whether to ignore indent for '{' following 'case'. -indent_ignore_case_brace = false # true/false - -# Spaces to indent '{' from 'case'. By default, the brace will appear under -# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. -# It might be wise to choose the same value for the option indent_switch_case. -indent_case_brace = indent_columns # number - -# indent 'break' with 'case' from 'switch'. -indent_switch_break_with_case = false # true/false - -# Whether to indent preprocessor statements inside of switch statements. -# -# Default: true -indent_switch_pp = false # true/false - -# Spaces to shift the 'case' line, without affecting any other lines. -# Usually 0. -indent_case_shift = 0 # unsigned number - -# Whether to align comments before 'case' with the 'case'. -# -# Default: true -indent_case_comment = true # true/false - -# Whether to indent comments not found in first column. -# -# Default: true -indent_comment = true # true/false - -# Whether to indent comments found in first column. -indent_col1_comment = false # true/false - -# Whether to indent multi string literal in first column. -indent_col1_multi_string_literal = true # true/false - -# Align comments on adjacent lines that are this many columns apart or less. -# -# Default: 3 -indent_comment_align_thresh = 3 # unsigned number - -# Whether to ignore indent for goto labels. -indent_ignore_label = false # true/false - -# How to indent goto labels. Requires indent_ignore_label=false. -# -# >0: Absolute column where 1 is the leftmost column -# <=0: Subtract from brace indent -# -# Default: 1 -indent_label = 1 # number - -# How to indent access specifiers that are followed by a -# colon. -# -# >0: Absolute column where 1 is the leftmost column -# <=0: Subtract from brace indent -# -# Default: 1 -indent_access_spec = 1 # number - -# Whether to indent the code after an access specifier by one level. -# If true, this option forces 'indent_access_spec=0'. -indent_access_spec_body = false # true/false - -# If an open parenthesis is followed by a newline, whether to indent the next -# line so that it lines up after the open parenthesis (not recommended). -indent_paren_nl = false # true/false - -# How to indent a close parenthesis after a newline. -# -# 0: Indent to body level (default) -# 1: Align under the open parenthesis -# 2: Indent to the brace level -# -1: Preserve original indentation -indent_paren_close = 2 # number - -# Whether to indent the open parenthesis of a function definition, -# if the parenthesis is on its own line. -indent_paren_after_func_def = false # true/false - -# Whether to indent the open parenthesis of a function declaration, -# if the parenthesis is on its own line. -indent_paren_after_func_decl = false # true/false - -# Whether to indent the open parenthesis of a function call, -# if the parenthesis is on its own line. -indent_paren_after_func_call = false # true/false - -# How to indent a comma when inside braces. -# 0: Indent by one level (default) -# 1: Align under the open brace -# -1: Preserve original indentation -indent_comma_brace = 0 # number - -# How to indent a comma when inside parentheses. -# 0: Indent by one level (default) -# 1: Align under the open parenthesis -# -1: Preserve original indentation -indent_comma_paren = 0 # number - -# How to indent a Boolean operator when inside parentheses. -# 0: Indent by one level (default) -# 1: Align under the open parenthesis -# -1: Preserve original indentation -indent_bool_paren = 0 # number - -# Whether to ignore the indentation of a Boolean operator when outside -# parentheses. -indent_ignore_bool = false # true/false - -# Whether to ignore the indentation of an arithmetic operator. -indent_ignore_arith = false # true/false - -# Whether to indent a semicolon when inside a for parenthesis. -# If true, aligns under the open for parenthesis. -indent_semicolon_for_paren = false # true/false - -# Whether to ignore the indentation of a semicolon outside of a 'for' -# statement. -indent_ignore_semicolon = false # true/false - -# Whether to align the first expression to following ones -# if indent_bool_paren=1. -indent_first_bool_expr = false # true/false - -# Whether to align the first expression to following ones -# if indent_semicolon_for_paren=true. -indent_first_for_expr = false # true/false - -# If an open square is followed by a newline, whether to indent the next line -# so that it lines up after the open square (not recommended). -indent_square_nl = false # true/false - -# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. -indent_preserve_sql = false # true/false - -# Whether to ignore the indentation of an assignment operator. -indent_ignore_assign = false # true/false - -# Whether to align continued statements at the '='. If false or if the '=' is -# followed by a newline, the next line is indent one tab. -# -# Default: true -indent_align_assign = true # true/false - -# If true, the indentation of the chunks after a '=' sequence will be set at -# LHS token indentation column before '='. -indent_off_after_assign = false # true/false - -# Whether to align continued statements at the '('. If false or the '(' is -# followed by a newline, the next line indent is one tab. -# -# Default: true -indent_align_paren = true # true/false - -# (OC) Whether to indent Objective-C code inside message selectors. -indent_oc_inside_msg_sel = false # true/false - -# (OC) Whether to indent Objective-C blocks at brace level instead of usual -# rules. -indent_oc_block = false # true/false - -# (OC) Indent for Objective-C blocks in a message relative to the parameter -# name. -# -# =0: Use indent_oc_block rules -# >0: Use specified number of spaces to indent -indent_oc_block_msg = 0 # unsigned number - -# (OC) Minimum indent for subsequent parameters -indent_oc_msg_colon = 0 # unsigned number - -# (OC) Whether to prioritize aligning with initial colon (and stripping spaces -# from lines, if necessary). -# -# Default: true -indent_oc_msg_prioritize_first_colon = true # true/false - -# (OC) Whether to indent blocks the way that Xcode does by default -# (from the keyword if the parameter is on its own line; otherwise, from the -# previous indentation level). Requires indent_oc_block_msg=true. -indent_oc_block_msg_xcode_style = false # true/false - -# (OC) Whether to indent blocks from where the brace is, relative to a -# message keyword. Requires indent_oc_block_msg=true. -indent_oc_block_msg_from_keyword = false # true/false - -# (OC) Whether to indent blocks from where the brace is, relative to a message -# colon. Requires indent_oc_block_msg=true. -indent_oc_block_msg_from_colon = false # true/false - -# (OC) Whether to indent blocks from where the block caret is. -# Requires indent_oc_block_msg=true. -indent_oc_block_msg_from_caret = false # true/false - -# (OC) Whether to indent blocks from where the brace caret is. -# Requires indent_oc_block_msg=true. -indent_oc_block_msg_from_brace = false # true/false - -# When indenting after virtual brace open and newline add further spaces to -# reach this minimum indent. -indent_min_vbrace_open = 0 # unsigned number - -# Whether to add further spaces after regular indent to reach next tabstop -# when indenting after virtual brace open and newline. -indent_vbrace_open_on_tabstop = false # true/false - -# How to indent after a brace followed by another token (not a newline). -# true: indent all contained lines to match the token -# false: indent all contained lines to match the brace -# -# Default: true -indent_token_after_brace = true # true/false - -# Whether to indent the body of a C++11 lambda. -indent_cpp_lambda_body = true # true/false - -# How to indent compound literals that are being returned. -# true: add both the indent from return & the compound literal open brace -# (i.e. 2 indent levels) -# false: only indent 1 level, don't add the indent for the open brace, only -# add the indent for the return. -# -# Default: true -indent_compound_literal_return = true # true/false - -# (C#) Whether to indent a 'using' block if no braces are used. -# -# Default: true -indent_using_block = true # true/false - -# How to indent the continuation of ternary operator. -# -# 0: Off (default) -# 1: When the `if_false` is a continuation, indent it under the `if_true` branch -# 2: When the `:` is a continuation, indent it under `?` -indent_ternary_operator = 1 # unsigned number - -# Whether to indent the statements inside ternary operator. -indent_inside_ternary_operator = false # true/false - -# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. -indent_off_after_return = false # true/false - -# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. -indent_off_after_return_new = false # true/false - -# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. -indent_single_after_return = false # true/false - -# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they -# have their own indentation). -indent_ignore_asm_block = false # true/false - -# Don't indent the close parenthesis of a function definition, -# if the parenthesis is on its own line. -donot_indent_func_def_close_paren = false # true/false - -# -# Newline adding and removing options -# - -# Whether to collapse empty blocks between '{' and '}' except for functions. -# Use nl_collapse_empty_body_functions to specify how empty function braces -# should be formatted. -nl_collapse_empty_body = true # true/false - -# Whether to collapse empty blocks between '{' and '}' for functions only. -# If true, overrides nl_inside_empty_func. -nl_collapse_empty_body_functions = true # true/false - -# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. -nl_assign_leave_one_liners = true # true/false - -# Don't split one-line braced statements inside a 'class xx { }' body. -nl_class_leave_one_liners = false # true/false - -# Don't split one-line enums, as in 'enum foo { BAR = 15 };' -nl_enum_leave_one_liners = false # true/false - -# Don't split one-line get or set functions. -nl_getset_leave_one_liners = false # true/false - -# (C#) Don't split one-line property get or set functions. -nl_cs_property_leave_one_liners = false # true/false - -# Don't split one-line function definitions, as in 'int foo() { return 0; }'. -# might modify nl_func_type_name -nl_func_leave_one_liners = true # true/false - -# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. -nl_cpp_lambda_leave_one_liners = true # true/false - -# Don't split one-line if/else statements, as in 'if(...) b++;'. -nl_if_leave_one_liners = false # true/false - -# Don't split one-line while statements, as in 'while(...) b++;'. -nl_while_leave_one_liners = false # true/false - -# Don't split one-line do statements, as in 'do { b++; } while(...);'. -nl_do_leave_one_liners = false # true/false - -# Don't split one-line for statements, as in 'for(...) b++;'. -nl_for_leave_one_liners = false # true/false - -# (OC) Don't split one-line Objective-C messages. -nl_oc_msg_leave_one_liner = false # true/false - -# (OC) Add or remove newline between method declaration and '{'. -nl_oc_mdef_brace = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove newline between Objective-C block signature and '{'. -nl_oc_block_brace = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove blank line before '@interface' statement. -nl_oc_before_interface = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove blank line before '@implementation' statement. -nl_oc_before_implementation = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove blank line before '@end' statement. -nl_oc_before_end = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove newline between '@interface' and '{'. -nl_oc_interface_brace = ignore # ignore/add/remove/force/not_defined - -# (OC) Add or remove newline between '@implementation' and '{'. -nl_oc_implementation_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newlines at the start of the file. -nl_start_of_file = remove # ignore/add/remove/force/not_defined - -# The minimum number of newlines at the start of the file (only used if -# nl_start_of_file is 'add' or 'force'). -nl_start_of_file_min = 0 # unsigned number - -# Add or remove newline at the end of the file. -nl_end_of_file = remove # ignore/add/remove/force/not_defined - -# The minimum number of newlines at the end of the file (only used if -# nl_end_of_file is 'add' or 'force'). -nl_end_of_file_min = 0 # unsigned number - -# Add or remove newline between '=' and '{'. -nl_assign_brace = remove # ignore/add/remove/force/not_defined - -# (D) Add or remove newline between '=' and '['. -nl_assign_square = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between '[]' and '{'. -nl_tsquare_brace = remove # ignore/add/remove/force/not_defined - -# (D) Add or remove newline after '= ['. Will also affect the newline before -# the ']'. -nl_after_square_assign = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between a function call's ')' and '{', as in -# 'list_for_each(item, &list) { }'. -nl_fcall_brace = force # ignore/add/remove/force/not_defined - -# Add or remove newline between 'enum' and '{'. -nl_enum_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'enum' and 'class'. -nl_enum_class = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'enum class' and the identifier. -nl_enum_class_identifier = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'enum class' type and ':'. -nl_enum_identifier_colon = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'enum class identifier :' and type. -nl_enum_colon_type = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'struct and '{'. -nl_struct_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'union' and '{'. -nl_union_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'if' and '{'. -nl_if_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and 'else'. -nl_brace_else = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'else if' and '{'. If set to ignore, -# nl_if_brace is used instead. -nl_elseif_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'else' and '{'. -nl_else_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'else' and 'if'. -nl_else_if = remove # ignore/add/remove/force/not_defined - -# Add or remove newline before 'if'/'else if' closing parenthesis. -nl_before_if_closing_paren = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and 'finally'. -nl_brace_finally = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'finally' and '{'. -nl_finally_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'try' and '{'. -nl_try_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between get/set and '{'. -nl_getset_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'for' and '{'. -nl_for_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline before the '{' of a 'catch' statement, as in -# 'catch (decl) {'. -nl_catch_brace = remove # ignore/add/remove/force/not_defined - -# (OC) Add or remove newline before the '{' of a '@catch' statement, as in -# '@catch (decl) {'. If set to ignore, nl_catch_brace is used. -nl_oc_catch_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and 'catch'. -nl_brace_catch = remove # ignore/add/remove/force/not_defined - -# (OC) Add or remove newline between '}' and '@catch'. If set to ignore, -# nl_brace_catch is used. -nl_oc_brace_catch = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and ']'. -nl_brace_square = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and ')' in a function invocation. -nl_brace_fparen = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'while' and '{'. -nl_while_brace = remove # ignore/add/remove/force/not_defined - -# (D) Add or remove newline between 'scope (x)' and '{'. -nl_scope_brace = ignore # ignore/add/remove/force/not_defined - -# (D) Add or remove newline between 'unittest' and '{'. -nl_unittest_brace = ignore # ignore/add/remove/force/not_defined - -# (D) Add or remove newline between 'version (x)' and '{'. -nl_version_brace = ignore # ignore/add/remove/force/not_defined - -# (C#) Add or remove newline between 'using' and '{'. -nl_using_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between two open or close braces. Due to general -# newline/brace handling, REMOVE may not work. -nl_brace_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'do' and '{'. -nl_do_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between '}' and 'while' of 'do' statement. -nl_brace_while = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'switch' and '{'. -nl_switch_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'synchronized' and '{'. -nl_synchronized_brace = remove # ignore/add/remove/force/not_defined - -# Add a newline between ')' and '{' if the ')' is on a different line than the -# if/for/etc. -# -# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and -# nl_catch_brace. -nl_multi_line_cond = false # true/false - -# Add a newline after '(' if an if/for/while/switch condition spans multiple -# lines -nl_multi_line_sparen_open = force # ignore/add/remove/force/not_defined - -# Add a newline before ')' if an if/for/while/switch condition spans multiple -# lines. Overrides nl_before_if_closing_paren if both are specified. -nl_multi_line_sparen_close = force # ignore/add/remove/force/not_defined - -# Force a newline in a define after the macro name for multi-line defines. -nl_multi_line_define = false # true/false - -# Whether to add a newline before 'case', and a blank line before a 'case' -# statement that follows a ';' or '}'. -nl_before_case = false # true/false - -# Whether to add a newline after a 'case' statement. -nl_after_case = true # true/false - -# Add or remove newline between a case ':' and '{'. -# -# Overrides nl_after_case. -nl_case_colon_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between ')' and 'throw'. -nl_before_throw = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'namespace' and '{'. -nl_namespace_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template class. -nl_template_class = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template class declaration. -# -# Overrides nl_template_class. -nl_template_class_decl = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<>' of a specialized class declaration. -# -# Overrides nl_template_class_decl. -nl_template_class_decl_special = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template class definition. -# -# Overrides nl_template_class. -nl_template_class_def = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<>' of a specialized class definition. -# -# Overrides nl_template_class_def. -nl_template_class_def_special = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template function. -nl_template_func = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template function -# declaration. -# -# Overrides nl_template_func. -nl_template_func_decl = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<>' of a specialized function -# declaration. -# -# Overrides nl_template_func_decl. -nl_template_func_decl_special = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template function -# definition. -# -# Overrides nl_template_func. -nl_template_func_def = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<>' of a specialized function -# definition. -# -# Overrides nl_template_func_def. -nl_template_func_def_special = force # ignore/add/remove/force/not_defined - -# Add or remove newline after 'template<...>' of a template variable. -nl_template_var = force # ignore/add/remove/force/not_defined - -# Add or remove newline between 'template<...>' and 'using' of a templated -# type alias. -nl_template_using = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between 'class' and '{'. -nl_class_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline before or after (depending on pos_class_comma, -# may not be IGNORE) each',' in the base class list. -nl_class_init_args = force # ignore/add/remove/force/not_defined - -# Add or remove newline after each ',' in the constructor member -# initialization. Related to nl_constr_colon, pos_constr_colon and -# pos_constr_comma. -nl_constr_init_args = force # ignore/add/remove/force/not_defined - -# Add or remove newline before first element, after comma, and after last -# element, in 'enum'. -nl_enum_own_lines = add # ignore/add/remove/force/not_defined - -# Add or remove newline between return type and function name in a function -# definition. -# might be modified by nl_func_leave_one_liners -nl_func_type_name = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between return type and function name inside a class -# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name -# is used instead. -nl_func_type_name_class = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between class specification and '::' -# in 'void A::f() { }'. Only appears in separate member implementation (does -# not appear with in-line implementation). -nl_func_class_scope = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between function scope and name, as in -# 'void A :: f() { }'. -nl_func_scope_name = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between return type and function name in a prototype. -nl_func_proto_type_name = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between a function name and the opening '(' in the -# declaration. -nl_func_paren = remove # ignore/add/remove/force/not_defined - -# Overrides nl_func_paren for functions with no parameters. -nl_func_paren_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between a function name and the opening '(' in the -# definition. -nl_func_def_paren = remove # ignore/add/remove/force/not_defined - -# Overrides nl_func_def_paren for functions with no parameters. -nl_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between a function name and the opening '(' in the -# call. -nl_func_call_paren = remove # ignore/add/remove/force/not_defined - -# Overrides nl_func_call_paren for functions with no parameters. -nl_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline after '(' in a function declaration. -nl_func_decl_start = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline after '(' in a function definition. -nl_func_def_start = ignore # ignore/add/remove/force/not_defined - -# Overrides nl_func_decl_start when there is only one parameter. -nl_func_decl_start_single = ignore # ignore/add/remove/force/not_defined - -# Overrides nl_func_def_start when there is only one parameter. -nl_func_def_start_single = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after '(' in a function declaration if '(' and ')' -# are in different lines. If false, nl_func_decl_start is used instead. -nl_func_decl_start_multi_line = true # true/false - -# Whether to add a newline after '(' in a function definition if '(' and ')' -# are in different lines. If false, nl_func_def_start is used instead. -nl_func_def_start_multi_line = true # true/false - -# Add or remove newline after each ',' in a function declaration. -nl_func_decl_args = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline after each ',' in a function definition. -nl_func_def_args = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline after each ',' in a function call. -nl_func_call_args = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after each ',' in a function declaration if '(' -# and ')' are in different lines. If false, nl_func_decl_args is used instead. -nl_func_decl_args_multi_line = true # true/false - -# Whether to add a newline after each ',' in a function definition if '(' -# and ')' are in different lines. If false, nl_func_def_args is used instead. -nl_func_def_args_multi_line = true # true/false - -# Add or remove newline before the ')' in a function declaration. -nl_func_decl_end = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline before the ')' in a function definition. -nl_func_def_end = ignore # ignore/add/remove/force/not_defined - -# Overrides nl_func_decl_end when there is only one parameter. -nl_func_decl_end_single = ignore # ignore/add/remove/force/not_defined - -# Overrides nl_func_def_end when there is only one parameter. -nl_func_def_end_single = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline before ')' in a function declaration if '(' and ')' -# are in different lines. If false, nl_func_decl_end is used instead. -nl_func_decl_end_multi_line = true # true/false - -# Whether to add a newline before ')' in a function definition if '(' and ')' -# are in different lines. If false, nl_func_def_end is used instead. -nl_func_def_end_multi_line = true # true/false - -# Add or remove newline between '()' in a function declaration. -nl_func_decl_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between '()' in a function definition. -nl_func_def_empty = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between '()' in a function call. -nl_func_call_empty = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after '(' in a function call, -# has preference over nl_func_call_start_multi_line. -nl_func_call_start = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline before ')' in a function call. -nl_func_call_end = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after '(' in a function call if '(' and ')' are in -# different lines. -nl_func_call_start_multi_line = true # true/false - -# Whether to add a newline after each ',' in a function call if '(' and ')' -# are in different lines. -nl_func_call_args_multi_line = true # true/false - -# Whether to add a newline before ')' in a function call if '(' and ')' are in -# different lines. -nl_func_call_end_multi_line = true # true/false - -# Whether to respect nl_func_call_XXX option in case of closure args. -nl_func_call_args_multi_line_ignore_closures = false # true/false - -# Whether to add a newline after '<' of a template parameter list. -nl_template_start = false # true/false - -# Whether to add a newline after each ',' in a template parameter list. -nl_template_args = false # true/false - -# Whether to add a newline before '>' of a template parameter list. -nl_template_end = false # true/false - -# (OC) Whether to put each Objective-C message parameter on a separate line. -# See nl_oc_msg_leave_one_liner. -nl_oc_msg_args = false # true/false - -# (OC) Minimum number of Objective-C message parameters before applying nl_oc_msg_args. -nl_oc_msg_args_min_params = 0 # unsigned number - -# (OC) Max code width of Objective-C message before applying nl_oc_msg_args. -nl_oc_msg_args_max_code_width = 0 # unsigned number - -# Add or remove newline between function signature and '{'. -nl_fdef_brace = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline between function signature and '{', -# if signature ends with ')'. Overrides nl_fdef_brace. -nl_fdef_brace_cond = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline before '{' opening brace -nl_before_opening_brace_func_class_def = force # ignore/add/remove/force/not_defined - -# Add or remove newline between C++11 lambda signature and '{'. -nl_cpp_ldef_brace = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'return' and the return expression. -nl_return_expr = remove # ignore/add/remove/force/not_defined - -# Add or remove newline between 'throw' and the throw expression. -nl_throw_expr = remove # ignore/add/remove/force/not_defined - -# Whether to add a newline after semicolons, except in 'for' statements. -nl_after_semicolon = true # true/false - -# (Java) Add or remove newline between the ')' and '{{' of the double brace -# initializer. -nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after the type in an unnamed temporary -# direct-list-initialization, better: -# before a direct-list-initialization. -nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline after the open brace in an unnamed temporary -# direct-list-initialization. -nl_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline before the close brace in an unnamed temporary -# direct-list-initialization. -nl_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined - -# Whether to add a newline before '{'. -nl_before_brace_open = false # true/false - -# Whether to add a newline after '{'. -nl_after_brace_open = false # true/false - -# Whether to add a newline between the open brace and a trailing single-line -# comment. Requires nl_after_brace_open=true. -nl_after_brace_open_cmt = false # true/false - -# Whether to add a newline after a virtual brace open with a non-empty body. -# These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open = false # true/false - -# Whether to add a newline after a virtual brace open with an empty body. -# These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open_empty = false # true/false - -# Whether to add a newline after '}'. Does not apply if followed by a -# necessary ';'. -nl_after_brace_close = true # true/false - -# Whether to add a newline after a virtual brace close, -# as in 'if (foo) a++; return;'. -nl_after_vbrace_close = true # true/false - -# Add or remove newline between the close brace and identifier, -# as in 'struct { int a; } b;'. Affects enumerations, unions and -# structures. If set to ignore, uses nl_after_brace_close. -nl_brace_struct_var = ignore # ignore/add/remove/force/not_defined - -# Whether to alter newlines in '#define' macros. -nl_define_macro = false # true/false - -# Whether to alter newlines between consecutive parenthesis closes. The number -# of closing parentheses in a line will depend on respective open parenthesis -# lines. -nl_squeeze_paren_close = false # true/false - -# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and -# '#endif'. Does not affect top-level #ifdefs. -nl_squeeze_ifdef = false # true/false - -# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. -nl_squeeze_ifdef_top_level = false # true/false - -# Add or remove blank line before 'if'. -nl_before_if = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'if' statement. Add/Force work only if the -# next token is not a closing brace. -nl_after_if = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line before 'for'. -nl_before_for = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'for' statement. -nl_after_for = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line before 'while'. -nl_before_while = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'while' statement. -nl_after_while = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line before 'switch'. -nl_before_switch = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'switch' statement. -nl_after_switch = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line before 'synchronized'. -nl_before_synchronized = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'synchronized' statement. -nl_after_synchronized = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line before 'do'. -nl_before_do = ignore # ignore/add/remove/force/not_defined - -# Add or remove blank line after 'do/while' statement. -nl_after_do = ignore # ignore/add/remove/force/not_defined - -# Ignore nl_before_{if,for,switch,do,synchronized} if the control -# statement is immediately after a case statement. -# if nl_before_{if,for,switch,do} is set to remove, this option -# does nothing. -nl_before_ignore_after_case = false # true/false - -# Whether to put a blank line before 'return' statements, unless after an open -# brace. -nl_before_return = false # true/false - -# Whether to put a blank line after 'return' statements, unless followed by a -# close brace. -nl_after_return = false # true/false - -# Whether to put a blank line before a member '.' or '->' operators. -nl_before_member = ignore # ignore/add/remove/force/not_defined - -# (Java) Whether to put a blank line after a member '.' or '->' operators. -nl_after_member = ignore # ignore/add/remove/force/not_defined - -# Whether to double-space commented-entries in 'struct'/'union'/'enum'. -nl_ds_struct_enum_cmt = false # true/false - -# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. -# (Lower priority than eat_blanks_before_close_brace.) -nl_ds_struct_enum_close_brace = false # true/false - -# Add or remove newline before or after (depending on pos_class_colon) a class -# colon, as in 'class Foo : public Bar'. -nl_class_colon = ignore # ignore/add/remove/force/not_defined - -# Add or remove newline around a class constructor colon. The exact position -# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. -nl_constr_colon = force # ignore/add/remove/force/not_defined - -# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' -# into a single line. If true, prevents other brace newline rules from turning -# such code into four lines. If true, it also preserves one-liner namespaces. -nl_namespace_two_to_one_liner = false # true/false - -# Whether to remove a newline in simple unbraced if statements, turning them -# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. -nl_create_if_one_liner = false # true/false - -# Whether to remove a newline in simple unbraced for statements, turning them -# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. -nl_create_for_one_liner = false # true/false - -# Whether to remove a newline in simple unbraced while statements, turning -# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. -nl_create_while_one_liner = false # true/false - -# Whether to collapse a function definition whose body (not counting braces) -# is only one line so that the entire definition (prototype, braces, body) is -# a single line. -nl_create_func_def_one_liner = false # true/false - -# Whether to split one-line simple list definitions into three lines by -# adding newlines, as in 'int a[12] = { 0 };'. -nl_create_list_one_liner = false # true/false - -# Whether to split one-line simple unbraced if statements into two lines by -# adding a newline, as in 'if(b) i++;'. -nl_split_if_one_liner = false # true/false - -# Whether to split one-line simple unbraced for statements into two lines by -# adding a newline, as in 'for (...) stmt;'. -nl_split_for_one_liner = false # true/false - -# Whether to split one-line simple unbraced while statements into two lines by -# adding a newline, as in 'while (expr) stmt;'. -nl_split_while_one_liner = false # true/false - -# Don't add a newline before a cpp-comment in a parameter list of a function -# call. -donot_add_nl_before_cpp_comment = false # true/false - -# -# Blank line options -# - -# The maximum number of consecutive newlines (3 = 2 blank lines). -nl_max = 2 # unsigned number - -# The maximum number of consecutive newlines in a function. -nl_max_blank_in_func = 2 # unsigned number - -# The number of newlines inside an empty function body. -# This option overrides eat_blanks_after_open_brace and -# eat_blanks_before_close_brace, but is ignored when -# nl_collapse_empty_body_functions=true -nl_inside_empty_func = 0 # unsigned number - -# The number of newlines before a function prototype. -nl_before_func_body_proto = 0 # unsigned number - -# The number of newlines before a multi-line function definition. Where -# applicable, this option is overridden with eat_blanks_after_open_brace=true -nl_before_func_body_def = 0 # unsigned number - -# The number of newlines before a class constructor/destructor prototype. -nl_before_func_class_proto = 0 # unsigned number - -# The number of newlines before a class constructor/destructor definition. -nl_before_func_class_def = 0 # unsigned number - -# The number of newlines after a function prototype. -nl_after_func_proto = 0 # unsigned number - -# The number of newlines after a function prototype, if not followed by -# another function prototype. -nl_after_func_proto_group = 2 # unsigned number - -# The number of newlines after a class constructor/destructor prototype. -nl_after_func_class_proto = 0 # unsigned number - -# The number of newlines after a class constructor/destructor prototype, -# if not followed by another constructor/destructor prototype. -nl_after_func_class_proto_group = 2 # unsigned number - -# Whether one-line method definitions inside a class body should be treated -# as if they were prototypes for the purposes of adding newlines. -# -# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def -# and nl_before_func_class_def for one-liners. -nl_class_leave_one_liner_groups = false # true/false - -# The number of newlines after '}' of a multi-line function body. -nl_after_func_body = 2 # unsigned number - -# The number of newlines after '}' of a multi-line function body in a class -# declaration. Also affects class constructors/destructors. -# -# Overrides nl_after_func_body. -nl_after_func_body_class = 2 # unsigned number - -# The number of newlines after '}' of a single line function body. Also -# affects class constructors/destructors. -# -# Overrides nl_after_func_body and nl_after_func_body_class. -nl_after_func_body_one_liner = 2 # unsigned number - -# The number of newlines before a block of typedefs. If nl_after_access_spec -# is non-zero, that option takes precedence. -# -# 0: No change (default). -nl_typedef_blk_start = 0 # unsigned number - -# The number of newlines after a block of typedefs. -# -# 0: No change (default). -nl_typedef_blk_end = 0 # unsigned number - -# The maximum number of consecutive newlines within a block of typedefs. -# -# 0: No change (default). -nl_typedef_blk_in = 0 # unsigned number - -# The minimum number of blank lines after a block of variable definitions -# at the top of a function body. If any preprocessor directives appear -# between the opening brace of the function and the variable block, then -# it is considered as not at the top of the function.Newlines are added -# before trailing preprocessor directives, if any exist. -# -# 0: No change (default). -nl_var_def_blk_end_func_top = 0 # unsigned number - -# The minimum number of empty newlines before a block of variable definitions -# not at the top of a function body. If nl_after_access_spec is non-zero, -# that option takes precedence. Newlines are not added at the top of the -# file or just after an opening brace. Newlines are added above any -# preprocessor directives before the block. -# -# 0: No change (default). -nl_var_def_blk_start = 0 # unsigned number - -# The minimum number of empty newlines after a block of variable definitions -# not at the top of a function body. Newlines are not added if the block -# is at the bottom of the file or just before a preprocessor directive. -# -# 0: No change (default). -nl_var_def_blk_end = 0 # unsigned number - -# The maximum number of consecutive newlines within a block of variable -# definitions. -# -# 0: No change (default). -nl_var_def_blk_in = 0 # unsigned number - -# The minimum number of newlines before a multi-line comment. -# Doesn't apply if after a brace open or another multi-line comment. -nl_before_block_comment = 0 # unsigned number - -# The minimum number of newlines before a single-line C comment. -# Doesn't apply if after a brace open or other single-line C comments. -nl_before_c_comment = 0 # unsigned number - -# The minimum number of newlines before a CPP comment. -# Doesn't apply if after a brace open or other CPP comments. -nl_before_cpp_comment = 0 # unsigned number - -# Whether to force a newline after a multi-line comment. -nl_after_multiline_comment = false # true/false - -# Whether to force a newline after a label's colon. -nl_after_label_colon = false # true/false - -# The number of newlines before a struct definition. -nl_before_struct = 0 # unsigned number - -# The number of newlines after '}' or ';' of a struct/enum/union definition. -nl_after_struct = 0 # unsigned number - -# The number of newlines before a class definition. -nl_before_class = 0 # unsigned number - -# The number of newlines after '}' or ';' of a class definition. -nl_after_class = 0 # unsigned number - -# The number of newlines before a namespace. -nl_before_namespace = 0 # unsigned number - -# The number of newlines after '{' of a namespace. This also adds newlines -# before the matching '}'. -# -# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if -# applicable, otherwise no change. -# -# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. -nl_inside_namespace = 0 # unsigned number - -# The number of newlines after '}' of a namespace. -nl_after_namespace = 0 # unsigned number - -# The number of newlines before an access specifier label. This also includes -# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count -# if after a brace open. -# -# 0: No change (default). -nl_before_access_spec = 2 # unsigned number - -# The number of newlines after an access specifier label. This also includes -# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count -# if after a brace open. -# -# 0: No change (default). -# -# Overrides nl_typedef_blk_start and nl_var_def_blk_start. -nl_after_access_spec = 1 # unsigned number - -# The number of newlines between a function definition and the function -# comment, as in '// comment\n void foo() {...}'. -# -# 0: No change (default). -nl_comment_func_def = 0 # unsigned number - -# The number of newlines after a try-catch-finally block that isn't followed -# by a brace close. -# -# 0: No change (default). -nl_after_try_catch_finally = 0 # unsigned number - -# (C#) The number of newlines before and after a property, indexer or event -# declaration. -# -# 0: No change (default). -nl_around_cs_property = 0 # unsigned number - -# (C#) The number of newlines between the get/set/add/remove handlers. -# -# 0: No change (default). -nl_between_get_set = 0 # unsigned number - -# (C#) Add or remove newline between property and the '{'. -nl_property_brace = ignore # ignore/add/remove/force/not_defined - -# Whether to remove blank lines after '{'. -eat_blanks_after_open_brace = true # true/false - -# Whether to remove blank lines before '}'. -eat_blanks_before_close_brace = true # true/false - -# How aggressively to remove extra newlines not in preprocessor. -# -# 0: No change (default) -# 1: Remove most newlines not handled by other config -# 2: Remove all newlines and reformat completely by config -nl_remove_extra_newlines = 2 # unsigned number - -# (Java) Add or remove newline after an annotation statement. Only affects -# annotations that are after a newline. -nl_after_annotation = ignore # ignore/add/remove/force/not_defined - -# (Java) Add or remove newline between two annotations. -nl_between_annotation = ignore # ignore/add/remove/force/not_defined - -# The number of newlines before a whole-file #ifdef. -# -# 0: No change (default). -nl_before_whole_file_ifdef = 0 # unsigned number - -# The number of newlines after a whole-file #ifdef. -# -# 0: No change (default). -nl_after_whole_file_ifdef = 0 # unsigned number - -# The number of newlines before a whole-file #endif. -# -# 0: No change (default). -nl_before_whole_file_endif = 0 # unsigned number - -# The number of newlines after a whole-file #endif. -# -# 0: No change (default). -nl_after_whole_file_endif = 0 # unsigned number - -# -# Positioning options -# - -# The position of arithmetic operators in wrapped expressions. -pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of assignment in wrapped expressions. Do not affect '=' -# followed by '{'. -pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of Boolean operators in wrapped expressions. -pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of comparison operators in wrapped expressions. -pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of conditional operators, as in the '?' and ':' of -# 'expr ? stmt : stmt', in wrapped expressions. -pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of the comma in wrapped expressions. -pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of the comma in enum entries. -pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of the comma in the base class list if there is more than one -# line. Affects nl_class_init_args. -pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of the comma in the constructor initialization list. -# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. -pos_constr_comma = trail_force # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of trailing/leading class colon, between class and base class -# list. Affects nl_class_colon. -pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of colons between constructor and member initialization. -# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. -pos_constr_colon = trail_force # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# The position of shift operators in wrapped expressions. -pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force - -# -# Line splitting options -# - -# Try to limit code width to N columns. -code_width = 0 # unsigned number - -# Whether to fully split long 'for' statements at semi-colons. -ls_for_split_full = false # true/false - -# Whether to fully split long function prototypes/calls at commas. -# The option ls_code_width has priority over the option ls_func_split_full. -ls_func_split_full = false # true/false - -# Whether to split lines as close to code_width as possible and ignore some -# groupings. -# The option ls_code_width has priority over the option ls_func_split_full. -ls_code_width = false # true/false - -# -# Code alignment options (not left column spaces/tabs) -# - -# Whether to keep non-indenting tabs. -align_keep_tabs = false # true/false - -# Whether to use tabs for aligning. -align_with_tabs = false # true/false - -# Whether to bump out to the next tab when aligning. -align_on_tabstop = false # true/false - -# Whether to right-align numbers. -align_number_right = false # true/false - -# Whether to keep whitespace not required for alignment. -align_keep_extra_space = false # true/false - -# Whether to align variable definitions in prototypes and functions. -align_func_params = false # true/false - -# The span for aligning parameter definitions in function on parameter name. -# -# 0: Don't align (default). -align_func_params_span = 0 # unsigned number - -# The threshold for aligning function parameter definitions. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_func_params_thresh = 0 # number - -# The gap for aligning function parameter definitions. -align_func_params_gap = 0 # unsigned number - -# The span for aligning constructor value. -# -# 0: Don't align (default). -align_constr_value_span = 0 # unsigned number - -# The threshold for aligning constructor value. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_constr_value_thresh = 0 # number - -# The gap for aligning constructor value. -align_constr_value_gap = 0 # unsigned number - -# Whether to align parameters in single-line functions that have the same -# name. The function names must already be aligned with each other. -align_same_func_call_params = false # true/false - -# The span for aligning function-call parameters for single line functions. -# -# 0: Don't align (default). -align_same_func_call_params_span = 0 # unsigned number - -# The threshold for aligning function-call parameters for single line -# functions. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_same_func_call_params_thresh = 0 # number - -# The span for aligning variable definitions. -# -# 0: Don't align (default). -align_var_def_span = 0 # unsigned number - -# How to consider (or treat) the '*' in the alignment of variable definitions. -# -# 0: Part of the type 'void * foo;' (default) -# 1: Part of the variable 'void *foo;' -# 2: Dangling 'void *foo;' -# Dangling: the '*' will not be taken into account when aligning. -align_var_def_star_style = 0 # unsigned number - -# How to consider (or treat) the '&' in the alignment of variable definitions. -# -# 0: Part of the type 'long & foo;' (default) -# 1: Part of the variable 'long &foo;' -# 2: Dangling 'long &foo;' -# Dangling: the '&' will not be taken into account when aligning. -align_var_def_amp_style = 0 # unsigned number - -# The threshold for aligning variable definitions. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_var_def_thresh = 0 # number - -# The gap for aligning variable definitions. -align_var_def_gap = 0 # unsigned number - -# Whether to align the colon in struct bit fields. -align_var_def_colon = false # true/false - -# The gap for aligning the colon in struct bit fields. -align_var_def_colon_gap = 0 # unsigned number - -# Whether to align any attribute after the variable name. -align_var_def_attribute = false # true/false - -# Whether to align inline struct/enum/union variable definitions. -align_var_def_inline = false # true/false - -# The span for aligning on '=' in assignments. -# -# 0: Don't align (default). -align_assign_span = 0 # unsigned number - -# The span for aligning on '=' in function prototype modifier. -# -# 0: Don't align (default). -align_assign_func_proto_span = 0 # unsigned number - -# The threshold for aligning on '=' in assignments. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_assign_thresh = 0 # number - -# Whether to align on the left most assignment when multiple -# definitions are found on the same line. -# Depends on 'align_assign_span' and 'align_assign_thresh' settings. -align_assign_on_multi_var_defs = false # true/false - -# The span for aligning on '{' in braced init list. -# -# 0: Don't align (default). -align_braced_init_list_span = 0 # unsigned number - -# The threshold for aligning on '{' in braced init list. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_braced_init_list_thresh = 0 # number - -# How to apply align_assign_span to function declaration "assignments", i.e. -# 'virtual void foo() = 0' or '~foo() = {default|delete}'. -# -# 0: Align with other assignments (default) -# 1: Align with each other, ignoring regular assignments -# 2: Don't align -align_assign_decl_func = 0 # unsigned number - -# The span for aligning on '=' in enums. -# -# 0: Don't align (default). -align_enum_equ_span = 0 # unsigned number - -# The threshold for aligning on '=' in enums. -# Use a negative number for absolute thresholds. -# -# 0: no limit (default). -align_enum_equ_thresh = 0 # number - -# The span for aligning class member definitions. -# -# 0: Don't align (default). -align_var_class_span = 0 # unsigned number - -# The threshold for aligning class member definitions. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_var_class_thresh = 0 # number - -# The gap for aligning class member definitions. -align_var_class_gap = 0 # unsigned number - -# The span for aligning struct/union member definitions. -# -# 0: Don't align (default). -align_var_struct_span = 0 # unsigned number - -# The threshold for aligning struct/union member definitions. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_var_struct_thresh = 0 # number - -# The gap for aligning struct/union member definitions. -align_var_struct_gap = 0 # unsigned number - -# The span for aligning struct initializer values. -# -# 0: Don't align (default). -align_struct_init_span = 0 # unsigned number - -# The span for aligning single-line typedefs. -# -# 0: Don't align (default). -align_typedef_span = 0 # unsigned number - -# The minimum space between the type and the synonym of a typedef. -align_typedef_gap = 0 # unsigned number - -# How to align typedef'd functions with other typedefs. -# -# 0: Don't mix them at all (default) -# 1: Align the open parenthesis with the types -# 2: Align the function type name with the other type names -align_typedef_func = 0 # unsigned number - -# How to consider (or treat) the '*' in the alignment of typedefs. -# -# 0: Part of the typedef type, 'typedef int * pint;' (default) -# 1: Part of type name: 'typedef int *pint;' -# 2: Dangling: 'typedef int *pint;' -# Dangling: the '*' will not be taken into account when aligning. -align_typedef_star_style = 0 # unsigned number - -# How to consider (or treat) the '&' in the alignment of typedefs. -# -# 0: Part of the typedef type, 'typedef int & intref;' (default) -# 1: Part of type name: 'typedef int &intref;' -# 2: Dangling: 'typedef int &intref;' -# Dangling: the '&' will not be taken into account when aligning. -align_typedef_amp_style = 0 # unsigned number - -# The span for aligning comments that end lines. -# -# 0: Don't align (default). -align_right_cmt_span = 0 # unsigned number - -# Minimum number of columns between preceding text and a trailing comment in -# order for the comment to qualify for being aligned. Must be non-zero to have -# an effect. -align_right_cmt_gap = 0 # unsigned number - -# If aligning comments, whether to mix with comments after '}' and #endif with -# less than three spaces before the comment. -align_right_cmt_mix = false # true/false - -# Whether to only align trailing comments that are at the same brace level. -align_right_cmt_same_level = false # true/false - -# Minimum column at which to align trailing comments. Comments which are -# aligned beyond this column, but which can be aligned in a lesser column, -# may be "pulled in". -# -# 0: Ignore (default). -align_right_cmt_at_col = 0 # unsigned number - -# The span for aligning function prototypes. -# -# 0: Don't align (default). -align_func_proto_span = 0 # unsigned number - -# How to consider (or treat) the '*' in the alignment of function prototypes. -# -# 0: Part of the type 'void * foo();' (default) -# 1: Part of the function 'void *foo();' -# 2: Dangling 'void *foo();' -# Dangling: the '*' will not be taken into account when aligning. -align_func_proto_star_style = 0 # unsigned number - -# How to consider (or treat) the '&' in the alignment of function prototypes. -# -# 0: Part of the type 'long & foo();' (default) -# 1: Part of the function 'long &foo();' -# 2: Dangling 'long &foo();' -# Dangling: the '&' will not be taken into account when aligning. -align_func_proto_amp_style = 0 # unsigned number - -# The threshold for aligning function prototypes. -# Use a negative number for absolute thresholds. -# -# 0: No limit (default). -align_func_proto_thresh = 0 # number - -# Minimum gap between the return type and the function name. -align_func_proto_gap = 0 # unsigned number - -# Whether to align function prototypes on the 'operator' keyword instead of -# what follows. -align_on_operator = false # true/false - -# Whether to mix aligning prototype and variable declarations. If true, -# align_var_def_XXX options are used instead of align_func_proto_XXX options. -align_mix_var_proto = false # true/false - -# Whether to align single-line functions with function prototypes. -# Uses align_func_proto_span. -align_single_line_func = false # true/false - -# Whether to align the open brace of single-line functions. -# Requires align_single_line_func=true. Uses align_func_proto_span. -align_single_line_brace = false # true/false - -# Gap for align_single_line_brace. -align_single_line_brace_gap = 0 # unsigned number - -# (OC) The span for aligning Objective-C message specifications. -# -# 0: Don't align (default). -align_oc_msg_spec_span = 0 # unsigned number - -# Whether and how to align backslashes that split a macro onto multiple lines. -# This will not work right if the macro contains a multi-line comment. -# -# 0: Do nothing (default) -# 1: Align the backslashes in the column at the end of the longest line -# 2: Align with the backslash that is farthest to the left, or, if that -# backslash is farther left than the end of the longest line, at the end of -# the longest line -# 3: Align with the backslash that is farthest to the right -align_nl_cont = 0 # unsigned number - -# Whether to align macro functions and variables together. -align_pp_define_together = false # true/false - -# The span for aligning on '#define' bodies. -# -# =0: Don't align (default) -# >0: Number of lines (including comments) between blocks -align_pp_define_span = 0 # unsigned number - -# The minimum space between label and value of a preprocessor define. -align_pp_define_gap = 0 # unsigned number - -# Whether to align lines that start with '<<' with previous '<<'. -# -# Default: true -align_left_shift = true # true/false - -# Whether to align comma-separated statements following '<<' (as used to -# initialize Eigen matrices). -align_eigen_comma_init = false # true/false - -# Whether to align text after 'asm volatile ()' colons. -align_asm_colon = false # true/false - -# (OC) Span for aligning parameters in an Objective-C message call -# on the ':'. -# -# 0: Don't align. -align_oc_msg_colon_span = 0 # unsigned number - -# (OC) Whether to always align with the first parameter, even if it is too -# short. -align_oc_msg_colon_first = false # true/false - -# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration -# on the ':'. -align_oc_decl_colon = false # true/false - -# (OC) Whether to not align parameters in an Objectve-C message call if first -# colon is not on next line of the message call (the same way Xcode does -# alignment) -align_oc_msg_colon_xcode_like = false # true/false - -# -# Comment modification options -# - -# Try to wrap comments at N columns. -cmt_width = 0 # unsigned number - -# How to reflow comments. -# -# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) -# 1: No touching at all -# 2: Full reflow (enable cmt_indent_multi for indent with line wrapping due to cmt_width) -cmt_reflow_mode = 0 # unsigned number - -# Path to a file that contains regular expressions describing patterns for -# which the end of one line and the beginning of the next will be folded into -# the same sentence or paragraph during full comment reflow. The regular -# expressions are described using ECMAScript syntax. The syntax for this -# specification is as follows, where "..." indicates the custom regular -# expression and "n" indicates the nth end_of_prev_line_regex and -# beg_of_next_line_regex regular expression pair: -# -# end_of_prev_line_regex[1] = "...$" -# beg_of_next_line_regex[1] = "^..." -# end_of_prev_line_regex[2] = "...$" -# beg_of_next_line_regex[2] = "^..." -# . -# . -# . -# end_of_prev_line_regex[n] = "...$" -# beg_of_next_line_regex[n] = "^..." -# -# Note that use of this option overrides the default reflow fold regular -# expressions, which are internally defined as follows: -# -# end_of_prev_line_regex[1] = "[\w,\]\)]$" -# beg_of_next_line_regex[1] = "^[\w,\[\(]" -# end_of_prev_line_regex[2] = "\.$" -# beg_of_next_line_regex[2] = "^[A-Z]" -cmt_reflow_fold_regex_file = "" # string - -# Whether to indent wrapped lines to the start of the encompassing paragraph -# during full comment reflow (cmt_reflow_mode = 2). Overrides the value -# specified by cmt_sp_after_star_cont. -# -# Note that cmt_align_doxygen_javadoc_tags overrides this option for -# paragraphs associated with javadoc tags -cmt_reflow_indent_to_paragraph_start = false # true/false - -# Whether to convert all tabs to spaces in comments. If false, tabs in -# comments are left alone, unless used for indenting. -cmt_convert_tab_to_spaces = false # true/false - -# Whether to apply changes to multi-line comments, including cmt_width, -# keyword substitution and leading chars. -# -# Default: true -cmt_indent_multi = true # true/false - -# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.) -# and corresponding fields such that groups of consecutive block tags, -# parameter names, and descriptions align with one another. Overrides that -# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may -# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2 -# in order to achieve the desired alignment for line-wrapping. -cmt_align_doxygen_javadoc_tags = false # true/false - -# The number of spaces to insert after the star and before doxygen -# javadoc-style tags (@param, @return, etc). Requires enabling -# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the -# cmt_sp_after_star_cont. -# -# Default: 1 -cmt_sp_before_doxygen_javadoc_tags = 1 # unsigned number - -# Whether to change trailing, single-line c-comments into cpp-comments. -cmt_trailing_single_line_c_to_cpp = false # true/false - -# Whether to group c-comments that look like they are in a block. -cmt_c_group = false # true/false - -# Whether to put an empty '/*' on the first line of the combined c-comment. -cmt_c_nl_start = false # true/false - -# Whether to add a newline before the closing '*/' of the combined c-comment. -cmt_c_nl_end = false # true/false - -# Whether to change cpp-comments into c-comments. -cmt_cpp_to_c = false # true/false - -# Whether to group cpp-comments that look like they are in a block. Only -# meaningful if cmt_cpp_to_c=true. -cmt_cpp_group = false # true/false - -# Whether to put an empty '/*' on the first line of the combined cpp-comment -# when converting to a c-comment. -# -# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. -cmt_cpp_nl_start = false # true/false - -# Whether to add a newline before the closing '*/' of the combined cpp-comment -# when converting to a c-comment. -# -# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. -cmt_cpp_nl_end = false # true/false - -# Whether to put a star on subsequent comment lines. -cmt_star_cont = false # true/false - -# The number of spaces to insert at the start of subsequent comment lines. -cmt_sp_before_star_cont = 0 # unsigned number - -# The number of spaces to insert after the star on subsequent comment lines. -cmt_sp_after_star_cont = 0 # unsigned number - -# For multi-line comments with a '*' lead, remove leading spaces if the first -# and last lines of the comment are the same length. -# -# Default: true -cmt_multi_check_last = true # true/false - -# For multi-line comments with a '*' lead, remove leading spaces if the first -# and last lines of the comment are the same length AND if the length is -# bigger as the first_len minimum. -# -# Default: 4 -cmt_multi_first_len_minimum = 4 # unsigned number - -# Path to a file that contains text to insert at the beginning of a file if -# the file doesn't start with a C/C++ comment. If the inserted text contains -# '$(filename)', that will be replaced with the current file's name. -cmt_insert_file_header = "" # string - -# Path to a file that contains text to insert at the end of a file if the -# file doesn't end with a C/C++ comment. If the inserted text contains -# '$(filename)', that will be replaced with the current file's name. -cmt_insert_file_footer = "" # string - -# Path to a file that contains text to insert before a function definition if -# the function isn't preceded by a C/C++ comment. If the inserted text -# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be -# replaced with, respectively, the name of the function, the javadoc '@param' -# and '@return' stuff, or the name of the class to which the member function -# belongs. -cmt_insert_func_header = "" # string - -# Path to a file that contains text to insert before a class if the class -# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', -# that will be replaced with the class name. -cmt_insert_class_header = "" # string - -# Path to a file that contains text to insert before an Objective-C message -# specification, if the method isn't preceded by a C/C++ comment. If the -# inserted text contains '$(message)' or '$(javaparam)', these will be -# replaced with, respectively, the name of the function, or the javadoc -# '@param' and '@return' stuff. -cmt_insert_oc_msg_header = "" # string - -# Whether a comment should be inserted if a preprocessor is encountered when -# stepping backwards from a function name. -# -# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and -# cmt_insert_class_header. -cmt_insert_before_preproc = false # true/false - -# Whether a comment should be inserted if a function is declared inline to a -# class definition. -# -# Applies to cmt_insert_func_header. -# -# Default: true -cmt_insert_before_inlines = true # true/false - -# Whether a comment should be inserted if the function is a class constructor -# or destructor. -# -# Applies to cmt_insert_func_header. -cmt_insert_before_ctor_dtor = false # true/false - -# -# Code modifying options (non-whitespace) -# - -# Add or remove braces on a single-line 'do' statement. -mod_full_brace_do = ignore # ignore/add/remove/force/not_defined - -# Add or remove braces on a single-line 'for' statement. -mod_full_brace_for = ignore # ignore/add/remove/force/not_defined - -# (Pawn) Add or remove braces on a single-line function definition. -mod_full_brace_function = ignore # ignore/add/remove/force/not_defined - -# Add or remove braces on a single-line 'if' statement. Braces will not be -# removed if the braced statement contains an 'else'. -mod_full_brace_if = ignore # ignore/add/remove/force/not_defined - -# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either -# have, or do not have, braces. Overrides mod_full_brace_if. -# -# 0: Don't override mod_full_brace_if -# 1: Add braces to all blocks if any block needs braces and remove braces if -# they can be removed from all blocks -# 2: Add braces to all blocks if any block already has braces, regardless of -# whether it needs them -# 3: Add braces to all blocks if any block needs braces and remove braces if -# they can be removed from all blocks, except if all blocks have braces -# despite none needing them -mod_full_brace_if_chain = 0 # unsigned number - -# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. -# If true, mod_full_brace_if_chain will only remove braces from an 'if' that -# does not have an 'else if' or 'else'. -mod_full_brace_if_chain_only = false # true/false - -# Add or remove braces on single-line 'while' statement. -mod_full_brace_while = ignore # ignore/add/remove/force/not_defined - -# Add or remove braces on single-line 'using ()' statement. -mod_full_brace_using = ignore # ignore/add/remove/force/not_defined - -# Don't remove braces around statements that span N newlines -mod_full_brace_nl = 0 # unsigned number - -# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks -# which span multiple lines. -# -# Affects: -# mod_full_brace_for -# mod_full_brace_if -# mod_full_brace_if_chain -# mod_full_brace_if_chain_only -# mod_full_brace_while -# mod_full_brace_using -# -# Does not affect: -# mod_full_brace_do -# mod_full_brace_function -mod_full_brace_nl_block_rem_mlcond = false # true/false - -# Add or remove unnecessary parentheses on 'return' statement. -mod_paren_on_return = ignore # ignore/add/remove/force/not_defined - -# Add or remove unnecessary parentheses on 'throw' statement. -mod_paren_on_throw = ignore # ignore/add/remove/force/not_defined - -# (Pawn) Whether to change optional semicolons to real semicolons. -mod_pawn_semicolon = false # true/false - -# Whether to fully parenthesize Boolean expressions in 'while' and 'if' -# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. -mod_full_paren_if_bool = false # true/false - -# Whether to fully parenthesize Boolean expressions after '=' -# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'. -mod_full_paren_assign_bool = false # true/false - -# Whether to fully parenthesize Boolean expressions after '=' -# statement, as in 'return a && b > c;' => 'return (a && (b > c));'. -mod_full_paren_return_bool = false # true/false - -# Whether to remove superfluous semicolons. -mod_remove_extra_semicolon = false # true/false - -# Whether to remove duplicate include. -mod_remove_duplicate_include = false # true/false - -# the following options (mod_XX_closebrace_comment) use different comment, -# depending of the setting of the next option. -# false: Use the c comment (default) -# true : Use the cpp comment -mod_add_force_c_closebrace_comment = false # true/false - -# If a function body exceeds the specified number of newlines and doesn't have -# a comment after the close brace, a comment will be added. -mod_add_long_function_closebrace_comment = 0 # unsigned number - -# If a namespace body exceeds the specified number of newlines and doesn't -# have a comment after the close brace, a comment will be added. -mod_add_long_namespace_closebrace_comment = 0 # unsigned number - -# If a class body exceeds the specified number of newlines and doesn't have a -# comment after the close brace, a comment will be added. -mod_add_long_class_closebrace_comment = 0 # unsigned number - -# If a switch body exceeds the specified number of newlines and doesn't have a -# comment after the close brace, a comment will be added. -mod_add_long_switch_closebrace_comment = 0 # unsigned number - -# If an #ifdef body exceeds the specified number of newlines and doesn't have -# a comment after the #endif, a comment will be added. -mod_add_long_ifdef_endif_comment = 0 # unsigned number - -# If an #ifdef or #else body exceeds the specified number of newlines and -# doesn't have a comment after the #else, a comment will be added. -mod_add_long_ifdef_else_comment = 0 # unsigned number - -# Whether to take care of the case by the mod_sort_xx options. -mod_sort_case_sensitive = false # true/false - -# Whether to sort consecutive single-line 'import' statements. -mod_sort_import = false # true/false - -# (C#) Whether to sort consecutive single-line 'using' statements. -mod_sort_using = false # true/false - -# Whether to sort consecutive single-line '#include' statements (C/C++) and -# '#import' statements (Objective-C). Be aware that this has the potential to -# break your code if your includes/imports have ordering dependencies. -mod_sort_include = false # true/false - -# Whether to prioritize '#include' and '#import' statements that contain -# filename without extension when sorting is enabled. -mod_sort_incl_import_prioritize_filename = false # true/false - -# Whether to prioritize '#include' and '#import' statements that does not -# contain extensions when sorting is enabled. -mod_sort_incl_import_prioritize_extensionless = false # true/false - -# Whether to prioritize '#include' and '#import' statements that contain -# angle over quotes when sorting is enabled. -mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false - -# Whether to ignore file extension in '#include' and '#import' statements -# for sorting comparison. -mod_sort_incl_import_ignore_extension = false # true/false - -# Whether to group '#include' and '#import' statements when sorting is enabled. -mod_sort_incl_import_grouping_enabled = false # true/false - -# Whether to move a 'break' that appears after a fully braced 'case' before -# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. -mod_move_case_break = false # true/false - -# Whether to move a 'return' that appears after a fully braced 'case' before -# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'. -mod_move_case_return = false # true/false - -# Add or remove braces around a fully braced case statement. Will only remove -# braces if there are no variable declarations in the block. -mod_case_brace = ignore # ignore/add/remove/force/not_defined - -# Whether to remove a void 'return;' that appears as the last statement in a -# function. -mod_remove_empty_return = false # true/false - -# Add or remove the comma after the last value of an enumeration. -mod_enum_last_comma = ignore # ignore/add/remove/force/not_defined - -# Syntax to use for infinite loops. -# -# 0: Leave syntax alone (default) -# 1: Rewrite as `for(;;)` -# 2: Rewrite as `while(true)` -# 3: Rewrite as `do`...`while(true);` -# 4: Rewrite as `while(1)` -# 5: Rewrite as `do`...`while(1);` -# -# Infinite loops that do not already match one of these syntaxes are ignored. -# Other options that affect loop formatting will be applied after transforming -# the syntax. -mod_infinite_loop = 0 # unsigned number - -# Add or remove the 'int' keyword in 'int short'. -mod_int_short = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'short int'. -mod_short_int = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'int long'. -mod_int_long = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'long int'. -mod_long_int = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'int signed'. -mod_int_signed = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'signed int'. -mod_signed_int = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'int unsigned'. -mod_int_unsigned = ignore # ignore/add/remove/force/not_defined - -# Add or remove the 'int' keyword in 'unsigned int'. -mod_unsigned_int = ignore # ignore/add/remove/force/not_defined - -# If there is a situation where mod_int_* and mod_*_int would result in -# multiple int keywords, whether to keep the rightmost int (the default) or the -# leftmost int. -mod_int_prefer_int_on_left = false # true/false - -# (OC) Whether to organize the properties. If true, properties will be -# rearranged according to the mod_sort_oc_property_*_weight factors. -mod_sort_oc_properties = false # true/false - -# (OC) Weight of a class property modifier. -mod_sort_oc_property_class_weight = 0 # number - -# (OC) Weight of 'atomic' and 'nonatomic'. -mod_sort_oc_property_thread_safe_weight = 0 # number - -# (OC) Weight of 'readwrite' when organizing properties. -mod_sort_oc_property_readwrite_weight = 0 # number - -# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', -# 'weak', 'strong') when organizing properties. -mod_sort_oc_property_reference_weight = 0 # number - -# (OC) Weight of getter type ('getter=') when organizing properties. -mod_sort_oc_property_getter_weight = 0 # number - -# (OC) Weight of setter type ('setter=') when organizing properties. -mod_sort_oc_property_setter_weight = 0 # number - -# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', -# 'null_resettable') when organizing properties. -mod_sort_oc_property_nullability_weight = 0 # number - -# -# Preprocessor options -# - -# How to use tabs when indenting preprocessor code. -# -# -1: Use 'indent_with_tabs' setting (default) -# 0: Spaces only -# 1: Indent with tabs to brace level, align with spaces -# 2: Indent and align with tabs, using spaces when not on a tabstop -# -# Default: -1 -pp_indent_with_tabs = -1 # number - -# Add or remove indentation of preprocessor directives inside #if blocks -# at brace level 0 (file-level). -pp_indent = ignore # ignore/add/remove/force/not_defined - -# Whether to indent #if/#else/#endif at the brace level. If false, these are -# indented from column 1. -pp_indent_at_level = false # true/false - -# Whether to indent #if/#else/#endif at the parenthesis level if the brace -# level is 0. If false, these are indented from column 1. -pp_indent_at_level0 = false # true/false - -# Specifies the number of columns to indent preprocessors per level -# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies -# the number of columns to indent preprocessors per level -# at brace level > 0 (function-level). -# -# Default: 1 -pp_indent_count = 1 # unsigned number - -# Add or remove space after # based on pp level of #if blocks. -pp_space_after = ignore # ignore/add/remove/force/not_defined - -# Sets the number of spaces per level added with pp_space_after. -pp_space_count = 0 # unsigned number - -# The indent for '#region' and '#endregion' in C# and '#pragma region' in -# C/C++. Negative values decrease indent down to the first column. -pp_indent_region = 0 # number - -# Whether to indent the code between #region and #endregion. -pp_region_indent_code = false # true/false - -# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when -# not at file-level. Negative values decrease indent down to the first column. -# -# =0: Indent preprocessors using output_tab_size -# >0: Column at which all preprocessors will be indented -pp_indent_if = 0 # number - -# Whether to indent the code between #if, #else and #endif. -pp_if_indent_code = false # true/false - -# Whether to indent the body of an #if that encompasses all the code in the file. -pp_indent_in_guard = false # true/false - -# Whether to indent '#define' at the brace level. If false, these are -# indented from column 1. -pp_define_at_level = false # true/false - -# Whether to indent '#include' at the brace level. -pp_include_at_level = false # true/false - -# Whether to ignore the '#define' body while formatting. -pp_ignore_define_body = false # true/false - -# An offset value that controls the indentation of the body of a multiline #define. -# 'body' refers to all the lines of a multiline #define except the first line. -# Requires 'pp_ignore_define_body = false'. -# -# <0: Absolute column: the body indentation starts off at the specified column -# (ex. -3 ==> the body is indented starting from column 3) -# >=0: Relative to the column of the '#' of '#define' -# (ex. 3 ==> the body is indented starting 3 columns at the right of '#') -# -# Default: 8 -pp_multiline_define_body_indent = 8 # number - -# Whether to indent case statements between #if, #else, and #endif. -# Only applies to the indent of the preprocessor that the case statements -# directly inside of. -# -# Default: true -pp_indent_case = true # true/false - -# Whether to indent whole function definitions between #if, #else, and #endif. -# Only applies to the indent of the preprocessor that the function definition -# is directly inside of. -# -# Default: true -pp_indent_func_def = true # true/false - -# Whether to indent extern C blocks between #if, #else, and #endif. -# Only applies to the indent of the preprocessor that the extern block is -# directly inside of. -# -# Default: true -pp_indent_extern = true # true/false - -# How to indent braces directly inside #if, #else, and #endif. -# Requires pp_if_indent_code=true and only applies to the indent of the -# preprocessor that the braces are directly inside of. -# 0: No extra indent -# 1: Indent by one level -# -1: Preserve original indentation -# -# Default: 1 -pp_indent_brace = 1 # number - -# Whether to print warning messages for unbalanced #if and #else blocks. -# This will print a message in the following cases: -# - if an #ifdef block ends on a different indent level than -# where it started from. Example: -# -# #ifdef TEST -# int i; -# { -# int j; -# #endif -# -# - an #elif/#else block ends on a different indent level than -# the corresponding #ifdef block. Example: -# -# #ifdef TEST -# int i; -# #else -# } -# int j; -# #endif -pp_warn_unbalanced_if = false # true/false - -# -# Sort includes options -# - -# The regex for include category with priority 0. -include_category_0 = "" # string - -# The regex for include category with priority 1. -include_category_1 = "" # string - -# The regex for include category with priority 2. -include_category_2 = "" # string - -# -# Use or Do not Use options -# - -# true: indent_func_call_param will be used (default) -# false: indent_func_call_param will NOT be used -# -# Default: true -use_indent_func_call_param = true # true/false - -# The value of the indentation for a continuation line is calculated -# differently if the statement is: -# - a declaration: your case with QString fileName ... -# - an assignment: your case with pSettings = new QSettings( ... -# -# At the second case the indentation value might be used twice: -# - at the assignment -# - at the function call (if present) -# -# To prevent the double use of the indentation value, use this option with the -# value 'true'. -# -# true: indent_continue will be used only once -# false: indent_continue will be used every time (default) -# -# Requires indent_ignore_first_continue=false. -use_indent_continue_only_once = false # true/false - -# The indentation can be: -# - after the assignment, at the '[' character -# - at the beginning of the lambda body -# -# true: indentation will be at the beginning of the lambda body -# false: indentation will be after the assignment (default) -indent_cpp_lambda_only_once = false # true/false - -# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the -# historic behavior, but is probably not the desired behavior, so this is off -# by default. -use_sp_after_angle_always = false # true/false - -# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, -# this tries to format these so that they match Qt's normalized form (i.e. the -# result of QMetaObject::normalizedSignature), which can slightly improve the -# performance of the QObject::connect call, rather than how they would -# otherwise be formatted. -# -# See options_for_QT.cpp for details. -# -# Default: true -use_options_overriding_for_qt_macros = true # true/false - -# If true: the form feed character is removed from the list of whitespace -# characters. See https://en.cppreference.com/w/cpp/string/byte/isspace. -use_form_feed_no_more_as_whitespace_character = false # true/false - -# -# Warn levels - 1: error, 2: warning (default), 3: note -# - -# (C#) Warning is given if doing tab-to-\t replacement and we have found one -# in a C# verbatim string literal. -# -# Default: 2 -warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number - -# Limit the number of loops. -# Used by uncrustify.cpp to exit from infinite loop. -# 0: no limit. -debug_max_number_of_loops = 0 # number - -# Set the number of the line to protocol; -# Used in the function prot_the_line if the 2. parameter is zero. -# 0: nothing protocol. -debug_line_number_to_protocol = 0 # number - -# Set the number of second(s) before terminating formatting the current file, -# 0: no timeout. -# only for linux -debug_timeout = 0 # number - -# Set the number of characters to be printed if the text is too long, -# 0: do not truncate. -debug_truncate = 0 # unsigned number - -# sort (or not) the tracking info. -# -# Default: true -debug_sort_the_tracks = true # true/false - -# decode (or not) the flags as a new line. -# only if the -p option is set. -debug_decode_the_flags = false # true/false - -# insert the number of the line at the beginning of each line -set_numbering_for_html_output = false # true/false - -# Meaning of the settings: -# Ignore - do not do any changes -# Add - makes sure there is 1 or more space/brace/newline/etc -# Force - makes sure there is exactly 1 space/brace/newline/etc, -# behaves like Add in some contexts -# Remove - removes space/brace/newline/etc -# -# -# - Token(s) can be treated as specific type(s) with the 'set' option: -# `set tokenType tokenString [tokenString...]` -# -# Example: -# `set BOOL __AND__ __OR__` -# -# tokenTypes are defined in src/token_enum.h, use them without the -# 'CT_' prefix: 'CT_BOOL' => 'BOOL' -# -# -# - Token(s) can be treated as type(s) with the 'type' option. -# `type tokenString [tokenString...]` -# -# Example: -# `type int c_uint_8 Rectangle` -# -# This can also be achieved with `set TYPE int c_uint_8 Rectangle` -# -# -# To embed whitespace in tokenStrings use the '\' escape character, or quote -# the tokenStrings. These quotes are supported: "'` -# -# -# - Support for the auto detection of languages through the file ending can be -# added using the 'file_ext' command. -# `file_ext langType langString [langString..]` -# -# Example: -# `file_ext CPP .ch .cxx .cpp.in` -# -# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use -# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' -# -# -# - Custom macro-based indentation can be set up using 'macro-open', -# 'macro-else' and 'macro-close'. -# `(macro-open | macro-else | macro-close) tokenString` -# -# Example: -# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` -# `macro-open BEGIN_MESSAGE_MAP` -# `macro-close END_MESSAGE_MAP` -# -# -# option(s) with 'not default' value: 0 -# diff --git a/CHANGELOG.md b/CHANGELOG.md index 44c00ff..bf90231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,98 +1,19 @@ # Changelog -## Squawk 0.2.4 (UNRELEASED) -### Bug fixes -- messages to the mucs get sent once again - -### Improvements -- it's possible to build against Qt 6 now, Qt6 is the default -- got rid of deprecated SimpleCrypt library -- look up for proper stanzaID - -## Squawk 0.2.3 (February 04, 2024) -### Bug fixes -- "Add contact" and "Join conference" menu are enabled once again (pavavno)! -- availability is now read from the same section of config file it was stored -- automatic avatars (if a contact doesn't have one) get generated once again - -### Improvements -- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues -- all of the expandable roster items now get saved between launches -- settings file on the disk is not rewritten every roster element expansion or collapse -- removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar) -- vcard window now is Info system and it can display more information -- reduced vcard request spam in MUCs - -### New features -- now you can enable tray icon from settings! -- there is a job queue now, this allowes to spread a bit the spam on the server at connection time -- squawk now queries clients of it's peers, you can see what programs other people use - -## Squawk 0.2.2 (May 05, 2022) -### Bug fixes -- now when you remove an account it actually gets removed -- segfault on uninitialized Availability in some rare occasions -- fixed crash when you open a dialog with someone that has only error messages in archive -- message height is now calculated correctly on Chinese and Japanese paragraphs -- the app doesn't crash on SIGINT anymore - -### Improvements -- there is a way to disable an account and it wouldn't connect when you change availability -- if you cancel password query an account becomes inactive and doesn't annoy you anymore -- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password -- if left the password field empty and chose KWallet as a storage Squawk will try to get that password from KWallet before asking you to input it -- accounts now connect to the server asynchronously - if one is stopped on password prompt another is connecting -- actualized translations, added English localization file - -### New features -- new "About" window with links, license, gratitudes -- if the authentication failed Squawk will ask again for your password and login -- now there is an amount of unread messages showing on top of Squawk launcher icon -- notifications now have buttons to open a conversation or to mark that message as read - -## Squawk 0.2.1 (Apr 02, 2022) -### Bug fixes -- build in release mode now no longer spams warnings -- build now correctly installs all build plugin libs -- a bug where the correction message was received, the indication was on but the text didn't actually change -- message body now doesn't intecept context menu from the whole message -- message input now doesn't increase font when you remove everything from it - -### Improvements -- reduced amount of places where platform specific path separator is used -- now message input is automatically focused when you open a dialog or a room -- what() method on unhandled exception now actually tells what happened - -### New features -- the settings are here! You con config different stuff from there -- now it's possible to set up different qt styles from settings -- if you have KConfig nad KConfigWidgets packages installed - you can choose from global color schemes -- it's possible now to choose a folder where squawk is going to store downloaded files -- now you can correct your message - -## Squawk 0.2.0 (Jan 10, 2022) +## Squawk 0.2.0 (Unreleased) ### Bug fixes - carbon copies switches on again after reconnection - requesting the history of the current chat after reconnection - global availability (in drop down list) gets restored after reconnection - status icon in active chat changes when presence of the pen pal changes - infinite progress when open the dialogue with something that has no history to show -- fallback icons for buttons, when no supported theme is installed (shunf4) -- better handling messages with no id (shunf4) -- removed dependency: uuid, now it's on Qt (shunf4) -- better requesting latest history (shunf4) ### Improvements - slightly reduced the traffic on the startup by not requesting history of all MUCs -- completely rewritten message feed, now it works way faster and looks cooler +- completely rewritten message feed, now it works way faster - OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager - show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed - once uploaded local files don't get second time uploaded - the remote URL is reused -- way better compilation time (vae) - -### New features -- pasting images from clipboard to attachment (shunf4) -- possible compilation for windows and macOS (shunf4) ## Squawk 0.1.5 (Jul 29, 2020) ### Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d764ac..8632b38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,8 @@ -cmake_minimum_required(VERSION 3.16) -project(squawk VERSION 0.2.4 LANGUAGES CXX) +cmake_minimum_required(VERSION 3.4) +project(squawk VERSION 0.1.6 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) -cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0079 NEW) -cmake_policy(SET CMP0167 NEW) set(CMAKE_CXX_STANDARD 17) set(CMAKE_AUTOMOC ON) @@ -29,50 +27,47 @@ add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG}) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}) option(SYSTEM_QXMPP "Use system qxmpp lib" ON) -option(SYSTEM_LMDBAL "Use system lmdbal lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) -option(WITH_KCONFIG "Build KConfig support module" ON) -option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out # Dependencies -## Qt, detect and prefer Qt6 if available -find_package(Qt6 QUIET CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) -if (Qt6_FOUND) - set(QT_VERSION_MAJOR 6) -else() - find_package(Qt5 REQUIRED CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) - set(QT_VERSION_MAJOR 5) -endif() - -message(STATUS "Building against Qt${QT_VERSION_MAJOR}") - +## Qt +find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED) find_package(Boost COMPONENTS) -target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) -## OMEMO -if (WITH_OMEMO) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(OMEMO libomemo-c) - if (OMEMO_FOUND) - target_compile_definitions(squawk PRIVATE WITH_OMEMO) - message("Building with support of OMEMO") - else () - message("libomemo-c package wasn't found, trying to build without OMEMO support") - set(WITH_OMEMO OFF) - endif () +target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5Widgets_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5DBus_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5Gui_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5Xml_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5Network_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt5Core_INCLUDE_DIRS}) + +## QXmpp +if (SYSTEM_QXMPP) + find_package(QXmpp CONFIG) + + if (NOT QXmpp_FOUND) + set(SYSTEM_QXMPP OFF) + message("QXmpp package wasn't found, trying to build with bundled QXmpp") else () - message("PKG_CONFIG module wasn't found, can not check libomemo-c support, trying to build without OMEMO support") - set(WITH_OMEMO OFF) + message("Building with system QXmpp") endif () endif () +if (NOT SYSTEM_QXMPP) + target_link_libraries(squawk PRIVATE qxmpp) + add_subdirectory(external/qxmpp) +else () + target_link_libraries(squawk PRIVATE QXmpp::QXmpp) +endif () + ## KIO if (WITH_KIO) - find_package(KF${QT_VERSION_MAJOR}KIO CONFIG) + find_package(KF5KIO CONFIG) - if (NOT KF${QT_VERSION_MAJOR}KIO_FOUND) + if (NOT KF5KIO_FOUND) set(WITH_KIO OFF) message("KIO package wasn't found, KIO support modules wouldn't be built") else () @@ -83,9 +78,9 @@ endif () ## KWallet if (WITH_KWALLET) - find_package(KF${QT_VERSION_MAJOR}Wallet CONFIG) + find_package(KF5Wallet CONFIG) - if (NOT KF${QT_VERSION_MAJOR}Wallet_FOUND) + if (NOT KF5Wallet_FOUND) set(WITH_KWALLET OFF) message("KWallet package wasn't found, KWallet support module wouldn't be built") else () @@ -94,107 +89,24 @@ if (WITH_KWALLET) endif () endif () -## KConfig -if (WITH_KCONFIG) - find_package(KF${QT_VERSION_MAJOR}Config CONFIG) - if (NOT KF${QT_VERSION_MAJOR}Config_FOUND) - set(WITH_KCONFIG OFF) - message("KConfig package wasn't found, KConfig support modules wouldn't be built") - else() - find_package(KF${QT_VERSION_MAJOR}ConfigWidgets CONFIG) - if (NOT KF${QT_VERSION_MAJOR}ConfigWidgets_FOUND) - set(WITH_KCONFIG OFF) - message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built") - else() - target_compile_definitions(squawk PRIVATE WITH_KCONFIG) - message("Building with support of KConfig") - message("Building with support of KConfigWidgets") - endif() - endif() -endif() +## Signal (TODO) +# find_package(Signal REQUIRED) -## QXmpp -if (SYSTEM_QXMPP) - if (WITH_OMEMO) - find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG COMPONENTS Omemo) - else () - find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG) - endif () +## LMDB +find_package(LMDB REQUIRED) - if (NOT QXmppQt${QT_VERSION_MAJOR}_FOUND) - set(SYSTEM_QXMPP OFF) - message("QXmpp package wasn't found, trying to build with bundled QXmpp") - else () - message("Building with system QXmpp") - endif () -endif () #it's endif() + if() and not else() because I want it to have a fallback behaviour -if (NOT SYSTEM_QXMPP) #we can fail finding system QXmpp and this way we'll check bundled before failing completely - message("Building with bundled QXmpp") - - target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base) - target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/client) - target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) - - if (WITH_OMEMO) - target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) - target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo) - set(BUILD_OMEMO ON) - set(BUILD_TESTS OFF) - else () - set(BUILD_OMEMO OFF) - endif () - add_subdirectory(external/qxmpp) - add_library(QXmpp::QXmpp ALIAS QXmppQt${QT_VERSION_MAJOR}) - if (WITH_OMEMO) - target_include_directories(QXmppOmemoQt${QT_VERSION_MAJOR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) - add_library(QXmpp::Omemo ALIAS QXmppOmemoQt${QT_VERSION_MAJOR}) - endif () -endif () - -## LMDBAL -if (SYSTEM_LMDBAL) - find_package(lmdbalqt${QT_VERSION_MAJOR}) - if (NOT lmdbalqt${QT_VERSION_MAJOR}_FOUND) - set(SYSTEM_LMDBAL OFF) - message("LMDBAL package wasn't found, trying to build with bundled LMDBAL") - else () - message("Building with system LMDBAL") - endif () -else() - message("Building with bundled LMDBAL") - set(BUILD_STATIC ON) - add_subdirectory(external/lmdbal) - add_library(LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} ALIAS LMDBAL) -endif() - -find_package(OpenSSL REQUIRED) - -## Linking -target_link_libraries(squawk - PRIVATE - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Widgets - Qt${QT_VERSION_MAJOR}::DBus - Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Xml - LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} - OpenSSL::Crypto - QXmpp::QXmpp -) - -if (WITH_OMEMO) - target_link_libraries(squawk PRIVATE QXmpp::Omemo) -endif () - -## Link thread libraries on Linux +# Linking +target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml) +target_link_libraries(squawk PRIVATE lmdb) +target_link_libraries(squawk PRIVATE simpleCrypt) +# Link thread libraries on Linux if(UNIX AND NOT APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(squawk PRIVATE Threads::Threads) endif() -## Build type +# Build type if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif () @@ -202,27 +114,15 @@ endif () message("Build type: ${CMAKE_BUILD_TYPE}") if(CMAKE_COMPILER_IS_GNUCXX) - set (COMPILE_OPTIONS -fno-sized-deallocation) # for eliminating _ZdlPvm - if (CMAKE_BUILD_TYPE STREQUAL "Release") - list(APPEND COMPILE_OPTIONS -O3) - endif() - if (CMAKE_BUILD_TYPE STREQUAL Debug) - list(APPEND COMPILE_OPTIONS -g) - list(APPEND COMPILE_OPTIONS -Wall) - list(APPEND COMPILE_OPTIONS -Wextra) - endif() - - message("Compilation options: " ${COMPILE_OPTIONS}) - target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS}) +target_compile_options(squawk PRIVATE + "-Wall;-Wextra" + "$<$:-g>" + "$<$:-O3>" + ) endif(CMAKE_COMPILER_IS_GNUCXX) -# I am not really sure about this solution -# This should enable plugins to be found in path like /usr/lib/squawk instead of just /usr/lib -set(PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/squawk") -add_compile_definitions(PLUGIN_PATH="${PLUGIN_PATH}") - -add_subdirectory(main) add_subdirectory(core) +add_subdirectory(external/simpleCrypt) add_subdirectory(packaging) add_subdirectory(plugins) add_subdirectory(resources) @@ -230,15 +130,13 @@ add_subdirectory(shared) add_subdirectory(translations) add_subdirectory(ui) -## Install the executable +# Install the executable install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) -install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) if (CMAKE_BUILD_TYPE STREQUAL "Release") if (APPLE) add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..." - COMMAND "${Qt${QT_VERSION_MAJOR}Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app" + COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app" ) endif(APPLE) endif() diff --git a/LICENSE.md b/LICENSE.md index 32b38d4..85c7c69 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -595,17 +595,17 @@ pointer to where the full notice is found. Copyright (C) - + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/README.md b/README.md index b65e5e9..e94972f 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,17 @@ [![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) -![Squawk screenshot](https://macaw.me/projects/squawk/0.2.2.png) +![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png) ### Prerequisites -- QT 5 or 6 -- CMake 3.10 or higher +- QT 5.12 *(lower versions might work but it wasn't tested)* +- lmdb +- CMake 3.0 or higher - qxmpp 1.1.0 or higher -- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) for lmdb) - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) -- KDE Frameworks: KConfig (optional) -- KDE Frameworks: KConfigWidgets (optional) -- Boost (just one little hpp from there) -- Imagemagick (for compilation, to rasterize an SVG logo) +- Boost ### Getting @@ -33,49 +30,14 @@ $ pacaur -S squawk ### Building -You can also the repo and build it from source +You can also clone the repo and build it from source Squawk requires Qt with SSL enabled. It uses CMake as build system. Please check the prerequisites and install them before installation. ---- - -There are several ways to build Squawk. The one you need depends on whether you have `qxmpp` and `lmdbal` installed in your system. - -#### Building with system dependencies - -This is the easiest way but it requires you to have `qxmpp` and `lmdbal` installed as system packages. Here is what you do: - -``` -$ git clone https://git.macaw.me/blue/squawk -$ cd squawk -$ mkdir build -$ cd build -$ cmake .. -$ cmake --build . -``` - -#### Building with bundled qxmpp - -If you don't have any of `qxmpp` or `lmdbal` (or both) installed the process is abit mor complicated. -On the configuration stage you need to enable one or both entries in the square brackets, depending on what package your system lacks. - -Here is what you do - -``` -$ git clone --recurse-submodules https://git.macaw.me/blue/squawk -$ cd squawk -$ mkdir build -$ cd build -$ cmake .. [-D SYSTEM_QXMPP=False] [-D SYSTEM_LMDBAL=False] -$ cmake --build . -``` - #### For Windows (Mingw-w64) build -**Building for windows is not mainteined, but was possible in the past, you can try, but it probably won't work** - You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt. The best way to acquire library `lmdb` and `boost` is through Msys2. @@ -84,31 +46,51 @@ First install Msys2, and then install `mingw-w64-x86_64-lmdb` and `mingw-w64-x86 Then you need to provide the cmake cache entry when calling cmake for configuration: +``` +cmake .. -D LMDB_ROOT_DIR:PATH= -D BOOST_ROOT:PATH= +``` + ``: e.g. `C:/msys64/mingw64`. +--- + +There are two ways to build, it depends whether you have qxmpp installed in your system + +#### Building with system qxmpp + +Here is what you do + +``` +$ git clone https://git.macaw.me/blue/squawk +$ cd squawk +$ mkdir build +$ cd build +$ cmake .. [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...] +$ cmake --build . +``` + +#### Building with bundled qxmpp + +Here is what you do + ``` $ git clone --recurse-submodules https://git.macaw.me/blue/squawk $ cd squawk $ mkdir build $ cd build -$ cmake .. -D SYSTEM_QXMPP=False -D SYSTEM_LMDBAL=False -D LMDB_ROOT_DIR:PATH= -D BOOST_ROOT:PATH= +$ cmake .. -D SYSTEM_QXMPP=False [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...] $ cmake --build . ``` -You can always refer to `appveyor.yml` to see how AppVeyor build squawk for windows. +You can always refer to `appveyor.yml` to see how AppVeyor build squawk. ### List of keys -Here is the list of keys you can pass to configuration phase of `cmake ..`: - +Here is the list of keys you can pass to configuration phase of `cmake ..`. - `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`) - `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`) -- `SYSTEM_LMDBAL` - `True` tries to link against `LMDABL` installed in the system, `False` builds bundled `LMDBAL` library (default is `True`) - `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`) - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) -- `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) -- `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`) -- `QT_VERSION_MAJOR` - `6` builds against Qt 6, `5` builds against Qt 6, corresponding version of lmdbal and qxmpp should be installed (default is `6`) ## License diff --git a/appveyor.yml b/appveyor.yml index 30a8125..9b20f3b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -65,7 +65,7 @@ for: - matrix: only: - - image: macOS-Mojave + - image: macOS install: - brew install lmdb imagemagick boost diff --git a/cmake/FindLMDB.cmake b/cmake/FindLMDB.cmake new file mode 100644 index 0000000..d6f2cd3 --- /dev/null +++ b/cmake/FindLMDB.cmake @@ -0,0 +1,52 @@ +#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license +#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code + +# Try to find LMDB headers and library. +# +# Usage of this module as follows: +# +# find_package(LMDB) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# LMDB_ROOT_DIR Set this variable to the root installation of +# LMDB if the module has problems finding the +# proper installation path. +# +# Variables defined by this module: +# +# LMDB_FOUND System has LMDB library/headers. +# LMDB_LIBRARIES The LMDB library. +# LMDB_INCLUDE_DIRS The location of LMDB headers. + +find_path(LMDB_ROOT_DIR + NAMES include/lmdb.h + ) + +find_library(LMDB_LIBRARIES + NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible + HINTS ${LMDB_ROOT_DIR}/lib + ) + +add_library(lmdb UNKNOWN IMPORTED) +set_target_properties(lmdb PROPERTIES + IMPORTED_LOCATION ${LMDB_LIBRARIES} + ) + +find_path(LMDB_INCLUDE_DIRS + NAMES lmdb.h + HINTS ${LMDB_ROOT_DIR}/include + ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LMDB DEFAULT_MSG + LMDB_LIBRARIES + LMDB_INCLUDE_DIRS + ) + +mark_as_advanced( + LMDB_ROOT_DIR + LMDB_LIBRARIES + LMDB_INCLUDE_DIRS +) diff --git a/cmake/FindSignal.cmake b/cmake/FindSignal.cmake new file mode 100644 index 0000000..752fed7 --- /dev/null +++ b/cmake/FindSignal.cmake @@ -0,0 +1,15 @@ +find_path(Signal_INCLUDE_DIR NAMES signal/signal_protocol.h) +find_library(Signal_LIBRARY signal-protocol-c) +mark_as_advanced(Signal_INCLUDE_DIR Signal_LIBRARY) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Signal REQUIRED_VARS Signal_LIBRARY Signal_INCLUDE_DIR) + +if (Signal_FOUND AND NOT TARGET Signal::Signal) + add_library(Signal::Signal UNKNOWN IMPORTED) + set_target_properties(Signal::Signal PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${Signal_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Signal_INCLUDE_DIR}" + ) +endif () diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8baa5ad..9369cb7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -3,32 +3,32 @@ if(WIN32) set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp) endif(WIN32) -set(SOURCE_FILES - account.cpp - adapterfunctions.cpp - conference.cpp - contact.cpp - rosteritem.cpp - ${SIGNALCATCHER_SOURCE} - squawk.cpp -) - -set(HEADER_FILES - account.h - adapterfunctions.h - conference.h - contact.h - rosteritem.h - signalcatcher.h - squawk.h -) - -target_sources(squawk PRIVATE ${SOURCE_FILES}) +target_sources(squawk PRIVATE + account.cpp + account.h + adapterFuctions.cpp + archive.cpp + archive.h + conference.cpp + conference.h + contact.cpp + contact.h + main.cpp + networkaccess.cpp + networkaccess.h + rosteritem.cpp + rosteritem.h + ${SIGNALCATCHER_SOURCE} + signalcatcher.h + squawk.cpp + squawk.h + storage.cpp + storage.h + urlstorage.cpp + urlstorage.h + ) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) add_subdirectory(passwordStorageEngines) -add_subdirectory(components) -add_subdirectory(delayManager) -add_subdirectory(utils) diff --git a/core/account.cpp b/core/account.cpp index e3d14e1..6784674 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -18,20 +18,11 @@ #include "account.h" #include - #include -#include "shared/defines.h" +using namespace Core; -Core::Account::Account( - const QString& p_login, - const QString& p_server, - const QString& p_password, - const QString& p_name, - bool p_active, - NetworkAccess* p_net, - QObject* parent -): +Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent): QObject(parent), name(p_name), archiveQueries(), @@ -39,25 +30,7 @@ Core::Account::Account( config(), presence(), state(Shared::ConnectionState::disconnected), - - mh(new MessageHandler(this)), - rh(new RosterHandler(this)), - vh(new VCardHandler(this)), - dh(new DiscoveryHandler(this)), -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - th(new TrustHandler(this)), -#endif -#ifdef WITH_OMEMO - oh(new OmemoHandler(this)), - om(new QXmppOmemoManager(oh)), -#endif -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - tm(new QXmppTrustManager(th)), - cm(new QXmppCarbonManagerV2()), - psm(new QXmppPubSubManager()), -#else cm(new QXmppCarbonManager()), -#endif am(new QXmppMamManager()), mm(new QXmppMucManager()), bm(new QXmppBookmarkManager()), @@ -68,31 +41,19 @@ Core::Account::Account( rcpm(new QXmppMessageReceiptManager()), reconnectScheduled(false), reconnectTimer(new QTimer), + avatarHash(), + avatarType(), + ownVCardRequestInProgress(false), network(p_net), - delay(nullptr), passwordType(Shared::AccountPassword::plain), - lastError(Error::none), - pepSupport(Shared::Support::unknown), - active(p_active), - notReadyPassword(false), - loadingOmemo(false) + mh(new MessageHandler(this)), + rh(new RosterHandler(this)) { config.setUser(p_login); config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); - // config.setIgnoreSslErrors(true); - // config.setAutoReconnectionEnabled(false); - delay = new DelayManager::Manager(getBareJid()); - QObject::connect(delay, &DelayManager::Manager::gotInfo, this, &Account::infoReady); - QObject::connect(delay, &DelayManager::Manager::gotOwnInfo, this, &Account::infoReady); - - QObject::connect(delay, &DelayManager::Manager::requestOwnVCard, vm, &QXmppVCardManager::requestClientVCard); - QObject::connect(delay, &DelayManager::Manager::requestVCard, vm, &QXmppVCardManager::requestVCard); - - rh->initialize(); - vh->initialize(); - dh->initialize(); + //config.setAutoReconnectionEnabled(false); QObject::connect(&client, &QXmppClient::stateChanged, this, &Account::onClientStateChange); QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived); @@ -101,10 +62,8 @@ Core::Account::Account( client.addExtension(cm); -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) QObject::connect(cm, &QXmppCarbonManager::messageReceived, mh, &MessageHandler::onCarbonMessageReceived); QObject::connect(cm, &QXmppCarbonManager::messageSent, mh, &MessageHandler::onCarbonMessageSent); -#endif client.addExtension(am); @@ -114,61 +73,84 @@ Core::Account::Account( client.addExtension(mm); client.addExtension(bm); + + QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); + //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler + client.addExtension(um); QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); + QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived); + QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived); + QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete); QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); - - client.addExtension(psm); - -#ifdef WITH_OMEMO - QObject::connect(delay, &DelayManager::Manager::requestBundles, oh, &OmemoHandler::requestBundles); - QObject::connect(delay, &DelayManager::Manager::requestOwnBundles, oh, &OmemoHandler::requestOwnBundles); - - QObject::connect(om, &QXmppOmemoManager::deviceAdded, oh, &OmemoHandler::onOmemoDeviceAdded); - - client.addExtension(tm); - client.addExtension(om); - om->setSecurityPolicy(QXmpp::Toakafa); - - if (oh->hasOwnDevice()) { - QXmppTask future = om->load(); - loadingOmemo = true; - future.then(this, [this] (bool result) { - loadingOmemo = false; - if (state == Shared::ConnectionState::scheduled) - client.connectToServer(config, presence); - - if (result) - qDebug() << "successfully loaded OMEMO data for account" << getName(); - else - qDebug() << "couldn't load OMEMO data for account" << getName(); - }); + + + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + name; + QDir dir(path); + + if (!dir.exists()) { + bool res = dir.mkpath(path); + if (!res) { + qDebug() << "Couldn't create a cache directory for account" << name; + throw 22; + } + } + + QFile* avatar = new QFile(path + "/avatar.png"); + QString type = "png"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpg"); + type = "jpg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpeg"); + type = "jpeg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.gif"); + type = "gif"; + } + } + } + + if (avatar->exists()) { + if (avatar->open(QFile::ReadOnly)) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(avatar); + avatarHash = sha1.result(); + avatarType = type; + } + } + if (avatarType.size() != 0) { + presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + presence.setPhotoHash(avatarHash.toUtf8()); + } else { + presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); } -#endif reconnectTimer->setSingleShot(true); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); - if (name == "Test") { - QXmppLogger* logger = new QXmppLogger(this); - logger->setLoggingType(QXmppLogger::SignalLogging); - client.setLogger(logger); - - QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text) { - SHARED_UNUSED(type); - qDebug() << text; - }); - } +// QXmppLogger* logger = new QXmppLogger(this); +// logger->setLoggingType(QXmppLogger::SignalLogging); +// client.setLogger(logger); +// +// QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){ +// qDebug() << text; +// }); } -Core::Account::~Account() { +Account::~Account() +{ if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); @@ -178,85 +160,59 @@ Core::Account::~Account() { QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); - rh->clear(); //conferenses inside of roster handler hold QXmppMuc objects. - //If we destroy QXmppMucManager, then when we will be destroying RosterHandler - //it will try to destory Core::Conference objects - //and inside of those QXmppMuc objects will already be destroyed. - //So, clear will start the destruction from Core::Conference and this way it's not gonna crash - - delete delay; + delete mh; + delete rh; + delete reconnectTimer; delete rcpm; + delete dm; delete um; delete bm; delete mm; delete am; delete cm; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - delete psm; -#endif -#ifdef WITH_OMEMO - delete om; - delete tm; - delete oh; - delete th; -#endif - - delete dh; - delete vh; - delete rh; - delete mh; } -Shared::ConnectionState Core::Account::getState() const { - return state;} +Shared::ConnectionState Core::Account::getState() const +{ + return state; +} -void Core::Account::connect() { +void Core::Account::connect() +{ if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); } if (state == Shared::ConnectionState::disconnected) { - if (notReadyPassword) { - emit needPassword(); - } else { - if (loadingOmemo) { - state = Shared::ConnectionState::scheduled; - emit connectionStateChanged(state); - } else { - client.connectToServer(config, presence); - } - } - + client.connectToServer(config, presence); } else { qDebug("An attempt to connect an account which is already connected, skipping"); } } -void Core::Account::onReconnectTimer() { +void Core::Account::onReconnectTimer() +{ if (reconnectScheduled) { reconnectScheduled = false; connect(); } } -void Core::Account::disconnect() { +void Core::Account::disconnect() +{ if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); } if (state != Shared::ConnectionState::disconnected) { //rh->clearConferences(); - if (state != Shared::ConnectionState::scheduled) { - client.disconnectFromServer(); - } else { - state = Shared::ConnectionState::disconnected; - emit connectionStateChanged(state); - } + client.disconnectFromServer(); } } -void Core::Account::onClientStateChange(QXmppClient::State st) { +void Core::Account::onClientStateChange(QXmppClient::State st) +{ switch (st) { case QXmppClient::ConnectedState: { if (state != Shared::ConnectionState::connected) { @@ -264,29 +220,10 @@ void Core::Account::onClientStateChange(QXmppClient::State st) { Shared::ConnectionState os = state; state = Shared::ConnectionState::connected; if (os == Shared::ConnectionState::connecting) { -#ifdef WITH_OMEMO - if (!oh->hasOwnDevice()) { - qDebug() << "setting up OMEMO data for account" << getName(); - om->changeDeviceLabel(QGuiApplication::applicationDisplayName() + " - " + QSysInfo::productType()); - QXmppTask future = om->setUp(); - future.then(this, [this] (bool result) { - if (result) - qDebug() << "successfully set up OMEMO data for account" << getName(); - else - qDebug() << "couldn't set up OMEMO data for account" << getName(); - - if (state == Shared::ConnectionState::connected) - runDiscoveryService(); - - }); - } else { - runDiscoveryService(); - } -#else - runDiscoveryService(); -#endif + qDebug() << "running service discovery for account" << name; + dm->requestItems(getServer()); + dm->requestInfo(getServer()); } - lastError = Error::none; emit connectionStateChanged(state); } } else { @@ -316,19 +253,49 @@ void Core::Account::onClientStateChange(QXmppClient::State st) { } } -void Core::Account::reconnect() { - if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting - if (state == Shared::ConnectionState::connected) { - reconnectScheduled = true; - reconnectTimer->start(500); - client.disconnectFromServer(); - } else { - qDebug() << "An attempt to reconnect account" << getName() << "which was not connected"; - } +void Core::Account::reconnect() +{ + if (state == Shared::ConnectionState::connected && !reconnectScheduled) { + reconnectScheduled = true; + reconnectTimer->start(500); + client.disconnectFromServer(); + } else { + qDebug() << "An attempt to reconnect account" << getName() << "which was not connected"; } } -Shared::Availability Core::Account::getAvailability() const { +QString Core::Account::getName() const { + return name;} + +QString Core::Account::getLogin() const { + return config.user();} + +QString Core::Account::getPassword() const { + return config.password();} + +QString Core::Account::getServer() const { + return config.domain();} + +Shared::AccountPassword Core::Account::getPasswordType() const { + return passwordType;} + +void Core::Account::setPasswordType(Shared::AccountPassword pt) { + passwordType = pt; } + +void Core::Account::setLogin(const QString& p_login) { + config.setUser(p_login);} + +void Core::Account::setName(const QString& p_name) { + name = p_name;} + +void Core::Account::setPassword(const QString& p_password) { + config.setPassword(p_password);} + +void Core::Account::setServer(const QString& p_server) { + config.setDomain(p_server);} + +Shared::Availability Core::Account::getAvailability() const +{ if (state == Shared::ConnectionState::connected) { QXmppPresence::AvailableStatusType pres = presence.availableStatusType(); return static_cast(pres); //they are compatible; @@ -337,42 +304,58 @@ Shared::Availability Core::Account::getAvailability() const { } } -void Core::Account::setAvailability(Shared::Availability avail) { +void Core::Account::setAvailability(Shared::Availability avail) +{ if (avail == Shared::Availability::offline) { disconnect(); //TODO not sure how to do here - changing state may cause connection or disconnection } else { QXmppPresence::AvailableStatusType pres = static_cast(avail); presence.setAvailableStatusType(pres); - if (state != Shared::ConnectionState::disconnected) + if (state != Shared::ConnectionState::disconnected) { client.setClientPresence(presence); + } } } -void Core::Account::runDiscoveryService() { - qDebug() << "running service discovery for account" << name; - dm->requestItems(getServer()); - dm->requestInfo(getServer()); -} - -void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { +void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) +{ QString id = p_presence.from(); QStringList comps = id.split("/"); QString jid = comps.front().toLower(); QString resource = comps.back(); - if (jid == getBareJid()) { - if (resource == getResource()) + QString myJid = getLogin() + "@" + getServer(); + + if (jid == myJid) { + if (resource == getResource()) { emit availabilityChanged(static_cast(p_presence.availableStatusType())); - - vh->handlePresenceOfMyAccountChange(p_presence); + } else { + if (!ownVCardRequestInProgress) { + switch (p_presence.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (avatarType.size() > 0) { + vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (avatarHash != p_presence.photoHash()) { + vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + } + } + } } else { RosterItem* item = rh->getRosterItem(jid); - if (item != nullptr) { - if (item->isMuc()) //MUC presence is handled by inner muc events - return; - else - item->handlePresence(p_presence); + if (item != 0) { + item->handlePresence(p_presence); } } @@ -380,46 +363,49 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { case QXmppPresence::Error: qDebug() << "An error reported by presence from" << id << p_presence.error().text(); break; - case QXmppPresence::Available: { + case QXmppPresence::Available:{ QDateTime lastInteraction = p_presence.lastUserInteraction(); - if (!lastInteraction.isValid()) + if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTimeUtc(); - + } emit addPresence(jid, resource, { {"lastActivity", lastInteraction}, {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible - {"status", p_presence.statusText()}, - {"client", QVariant::fromValue( - Shared::ClientId( - p_presence.capabilityNode(), - p_presence.capabilityVer().toBase64(), - p_presence.capabilityHash()) - ) - } + {"status", p_presence.statusText()} }); - } break; + } + break; case QXmppPresence::Unavailable: emit removePresence(jid, resource); break; case QXmppPresence::Subscribe: qDebug("xmpp presence \"subscribe\" received, do not yet know what to do, skipping"); - break; case QXmppPresence::Subscribed: qDebug("xmpp presence \"subscribed\" received, do not yet know what to do, skipping"); - break; case QXmppPresence::Unsubscribe: qDebug("xmpp presence \"unsubscribe\" received, do not yet know what to do, skipping"); - break; case QXmppPresence::Unsubscribed: qDebug("xmpp presence \"unsubscribed\" received, do not yet know what to do, skipping"); - break; case QXmppPresence::Probe: qDebug("xmpp presence \"probe\" received, do not yet know what to do, skipping"); break; } } -void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { +QString Core::Account::getResource() const { + return config.resource();} + +void Core::Account::setResource(const QString& p_resource) { + config.setResource(p_resource);} + +QString Core::Account::getFullJid() const { + return getLogin() + "@" + getServer() + "/" + getResource();} + +void Core::Account::sendMessage(const Shared::Message& data) { + mh->sendMessage(data);} + +void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) +{ if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { std::map::const_iterator itr = archiveQueries.find(queryId); if (itr != archiveQueries.end()) { @@ -431,19 +417,21 @@ void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMess sMsg.setState(Shared::Message::State::sent); QString oId = msg.replaceId(); - if (oId.size() > 0) + if (oId.size() > 0) { item->correctMessageInArchive(oId, sMsg); - else + } else { item->addMessageToArchive(sMsg); + } } } } -void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { +void Core::Account::requestArchive(const QString& jid, int count, const QString& before) +{ qDebug() << "An archive request for " << jid << ", before " << before; - RosterItem* item = rh->getRosterItem(jid); + RosterItem* contact = rh->getRosterItem(jid); - if (item == nullptr) { + if (contact == 0) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; emit responseArchive(jid, std::list(), true); return; @@ -451,21 +439,14 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString& if (state != Shared::ConnectionState::connected) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping"; - emit responseArchive(jid, std::list(), false); - return; + emit responseArchive(contact->jid, std::list(), false); } -#ifdef WITH_OMEMO - if (!item->isMuc()) { - Contact* contact = static_cast(item); - if (contact->omemoBundles == Shared::Possible::unknown) - oh->requestBundles(jid); - } -#endif - item->requestHistory(count, before); + contact->requestHistory(count, before); } -void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) { +void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) +{ RosterItem* contact = static_cast(sender()); QString to; @@ -488,14 +469,6 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a query.setAfter(after); } } - if (before.size() == 0 && after.size() == 0) { - // https://xmpp.org/extensions/xep-0313.html#sect-idm46556759682304 - // To request the page at the end of the archive - // (i.e. the most recent messages), include just an - // empty element in the RSM part of the query. - // As defined by RSM, this will return the last page of the archive. - query.setBefore(""); - } qDebug() << "Remote query for" << contact->jid << "from" << after << ", to" << before; } @@ -509,7 +482,8 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a archiveQueries.insert(std::make_pair(q, contact->jid)); } -void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) { +void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) +{ std::map::const_iterator itr = archiveQueries.find(queryId); if (itr != archiveQueries.end()) { QString jid = itr->second; @@ -517,24 +491,24 @@ void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResu RosterItem* ri = rh->getRosterItem(jid); - if (ri != nullptr) { + if (ri != 0) { qDebug() << "Flushing messages for" << jid << ", complete:" << complete; ri->flushMessagesToArchive(complete, resultSetReply.first(), resultSetReply.last()); } } } -void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) { - SHARED_UNUSED(type); +void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) +{ qDebug() << "MAM MESSAGE LOG::"; qDebug() << msg; } -void Core::Account::onClientError(QXmppClient::Error err) { +void Core::Account::onClientError(QXmppClient::Error err) +{ qDebug() << "Error"; QString errorText; QString errorType; - lastError = Error::other; switch (err) { case QXmppClient::SocketError: errorText = client.socketErrorString(); @@ -576,7 +550,6 @@ void Core::Account::onClientError(QXmppClient::Error err) { break; case QXmppStanza::Error::NotAuthorized: errorText = "Authentication error"; - lastError = Error::authentication; break; #if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0) case QXmppStanza::Error::PaymentRequired: @@ -620,9 +593,6 @@ void Core::Account::onClientError(QXmppClient::Error err) { errorText = "Policy violation"; break; #endif - default: - errorText = "Unknown Error"; - break; } errorType = "Client stream error"; @@ -640,18 +610,22 @@ void Core::Account::onClientError(QXmppClient::Error err) { emit error(errorText); } -void Core::Account::subscribeToContact(const QString& jid, const QString& reason) { - if (state == Shared::ConnectionState::connected) +void Core::Account::subscribeToContact(const QString& jid, const QString& reason) +{ + if (state == Shared::ConnectionState::connected) { rm->subscribe(jid, reason); - else + } else { qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping"; + } } -void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) { - if (state == Shared::ConnectionState::connected) +void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) +{ + if (state == Shared::ConnectionState::connected) { rm->unsubscribe(jid, reason); - else + } else { qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping"; + } } void Core::Account::removeContactRequest(const QString& jid) { @@ -660,7 +634,8 @@ void Core::Account::removeContactRequest(const QString& jid) { void Core::Account::addContactRequest(const QString& jid, const QString& name, const QSet& groups) { rh->addContactRequest(jid, name, groups);} -void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) { +void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) +{ Conference* conf = rh->getConference(jid); if (conf == 0) { qDebug() << "An attempt to set auto join to the non existing room" << jid << "of the account" << getName() << ", skipping"; @@ -670,7 +645,8 @@ void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) { conf->setAutoJoin(joined); } -void Core::Account::setRoomJoined(const QString& jid, bool joined) { +void Core::Account::setRoomJoined(const QString& jid, bool joined) +{ Conference* conf = rh->getConference(jid); if (conf == 0) { qDebug() << "An attempt to set joined to the non existing room" << jid << "of the account" << getName() << ", skipping"; @@ -680,149 +656,6 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) { conf->setJoined(joined); } -void Core::Account::setContactEncryption(const QString& jid, Shared::EncryptionProtocol value) { - Contact* cnt = rh->getContact(jid); - if (cnt == nullptr) { - qDebug() << "An attempt to set encryption to the non-existing contact" << jid << "of the account" << getName() << ", skipping"; - return; - } - - cnt->setEncryption(value); -} - - -void Core::Account::discoverInfo(const QString& address, const QString& node) { - if (state == Shared::ConnectionState::connected) { - dm->requestInfo(address, node); - } else { - qDebug() << "An attempt to send a discover info by account" << name << - "sending request to" << address << "about node" << node << - "but the account is not in the connected state, skipping"; - } -} - -void Core::Account::setPepSupport(Shared::Support support) { - if (support != pepSupport) { - pepSupport = support; - emit pepSupportChanged(pepSupport); - } -} - -void Core::Account::handleDisconnection() { - setPepSupport(Shared::Support::unknown); - delay->disconnected(); -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) - cm->setCarbonsEnabled(false); -#endif - rh->handleOffline(); - archiveQueries.clear(); -} - -void Core::Account::onContactHistoryResponse(const std::list& list, bool last) { - RosterItem* contact = static_cast(sender()); - - qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; - if (last) - qDebug() << "The response contains the first accounted message"; - - emit responseArchive(contact->jid, list, last); -} - -bool Core::Account::getActive() const { - return active;} - -void Core::Account::setActive(bool p_active) { - if (active != p_active) { - active = p_active; - - emit changed({{"active", active}}); - } -} - -QString Core::Account::getResource() const { - return config.resource();} - -void Core::Account::setResource(const QString& p_resource) { - config.setResource(p_resource);} - -QString Core::Account::getBareJid() const { - return getLogin() + "@" + getServer();} - -QString Core::Account::getFullJid() const { - return getBareJid() + "/" + getResource();} - -QString Core::Account::getName() const { - return name;} - -QString Core::Account::getLogin() const { - return config.user();} - -QString Core::Account::getPassword() const { - return config.password();} - -QString Core::Account::getServer() const { - return config.domain();} - -Shared::AccountPassword Core::Account::getPasswordType() const { - return passwordType;} - -void Core::Account::setPasswordType(Shared::AccountPassword pt) { - passwordType = pt; } - -void Core::Account::setLogin(const QString& p_login) { - config.setUser(p_login); - delay->setOwnJid(getBareJid()); -} - -void Core::Account::setName(const QString& p_name) { - name = p_name;} - -void Core::Account::setPassword(const QString& p_password) { - config.setPassword(p_password); - notReadyPassword = false; -} - -void Core::Account::setServer(const QString& p_server) { - config.setDomain(p_server); - delay->setOwnJid(getBareJid()); -} - -void Core::Account::sendMessage(const Shared::Message& data) { - mh->sendMessage(data);} - -void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ - mh->requestChangeMessage(jid, messageId, data);} - -void Core::Account::resendMessage(const QString& jid, const QString& id) { - mh->resendMessage(jid, id);} - -void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { - mh->sendMessage(data, false, originalId);} - -void Core::Account::requestInfo(const QString& jid) { - //TODO switch case of what kind of entity this info request is about - //right now it could be only about myself or some contact - delay->getInfo(jid); - //vh->requestVCard(jid); -} - -void Core::Account::updateInfo(const Shared::Info& info) { - //TODO switch case of what kind of entity this info update is about - //right now it could be only about myself - vh->uploadVCard(info.getVCardRef()); - const std::list& keys = info.getActiveKeysRef(); - for (const Shared::KeyInfo& info : keys) { - qDebug() << "An attempt to save key: "; - qDebug() << "id:" << info.id; - qDebug() << "label:" << info.label; - qDebug() << "current device:" << info.currentDevice; - qDebug() << "... but it's not implemented yet, ignoring"; - } -} - -QString Core::Account::getAvatarPath() const { - return vh->getAvatarPath();} - void Core::Account::removeRoomRequest(const QString& jid){ rh->removeRoomRequest(jid);} @@ -835,17 +668,261 @@ void Core::Account::addContactToGroupRequest(const QString& jid, const QString& void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { rh->removeContactFromGroupRequest(jid, groupName);} -void Core::Account::renameContactRequest(const QString& jid, const QString& newName) { +void Core::Account::renameContactRequest(const QString& jid, const QString& newName) +{ Contact* cnt = rh->getContact(jid); - if (cnt == 0) + if (cnt == 0) { qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping"; - else + } else { rm->renameItem(jid, newName); + } } -void Core::Account::invalidatePassword() { - notReadyPassword = true;} +void Core::Account::onVCardReceived(const QXmppVCardIq& card) +{ + QString id = card.from(); + QStringList comps = id.split("/"); + QString jid = comps.front().toLower(); + QString resource(""); + if (comps.size() > 1) { + resource = comps.back(); + } + pendingVCardRequests.erase(id); + RosterItem* item = rh->getRosterItem(jid); + + if (item == 0) { + if (jid == getLogin() + "@" + getServer()) { + onOwnVCardReceived(card); + } else { + qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + } + return; + } + + Shared::VCard vCard = item->handleResponseVCard(card, resource); + + emit receivedVCard(jid, vCard); +} -Core::Account::Error Core::Account::getLastError() const { - return lastError;} +void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) +{ + QByteArray ava = card.photo(); + bool avaChanged = false; + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/"; + if (ava.size() > 0) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(ava); + QString newHash(sha1.result()); + QMimeDatabase db; + QMimeType newType = db.mimeTypeForData(ava); + if (avatarType.size() > 0) { + if (avatarHash != newHash) { + QString oldPath = path + "avatar." + avatarType; + QFile oldAvatar(oldPath); + bool oldToRemove = false; + if (oldAvatar.exists()) { + if (oldAvatar.rename(oldPath + ".bak")) { + oldToRemove = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing"; + } + } + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't save it"; + if (oldToRemove) { + qDebug() << "rolling back to the old avatar"; + if (!oldAvatar.rename(oldPath)) { + qDebug() << "Couldn't roll back to the old avatar in account" << name; + } + } + } + } + } else { + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't save it"; + } + } + } else { + if (avatarType.size() > 0) { + QFile oldAvatar(path + "avatar." + avatarType); + if (!oldAvatar.remove()) { + qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing"; + } else { + avatarType = ""; + avatarHash = ""; + avaChanged = true; + } + } + } + + if (avaChanged) { + QMap change; + if (avatarType.size() > 0) { + presence.setPhotoHash(avatarHash.toUtf8()); + presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + change.insert("avatarPath", path + "avatar." + avatarType); + } else { + presence.setPhotoHash(""); + presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); + change.insert("avatarPath", ""); + } + client.setClientPresence(presence); + emit changed(change); + } + + ownVCardRequestInProgress = false; + + Shared::VCard vCard; + initializeVCard(vCard, card); + + if (avatarType.size() > 0) { + vCard.setAvatarType(Shared::Avatar::valid); + vCard.setAvatarPath(path + "avatar." + avatarType); + } else { + vCard.setAvatarType(Shared::Avatar::empty); + } + + emit receivedVCard(getLogin() + "@" + getServer(), vCard); +} +QString Core::Account::getAvatarPath() const +{ + if (avatarType.size() == 0) { + return ""; + } else { + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType; + } +} + +void Core::Account::requestVCard(const QString& jid) +{ + if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { + qDebug() << "requesting vCard" << jid; + if (jid == getLogin() + "@" + getServer()) { + if (!ownVCardRequestInProgress) { + vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + } else { + vm->requestVCard(jid); + pendingVCardRequests.insert(jid); + } + } +} + +void Core::Account::uploadVCard(const Shared::VCard& card) +{ + QXmppVCardIq iq; + initializeQXmppVCard(iq, card); + + bool avatarChanged = false; + if (card.getAvatarType() != Shared::Avatar::empty) { + QString newPath = card.getAvatarPath(); + QString oldPath = getAvatarPath(); + QByteArray data; + QString type; + if (newPath != oldPath) { + QFile avatar(newPath); + if (!avatar.open(QFile::ReadOnly)) { + qDebug() << "An attempt to upload new vCard to account" << name + << "but it wasn't possible to read file" << newPath + << "which was supposed to be new avatar, uploading old avatar"; + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } else { + data = avatar.readAll(); + avatarChanged = true; + } + } else { + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } + + if (data.size() > 0) { + QMimeDatabase db; + type = db.mimeTypeForData(data).name(); + iq.setPhoto(data); + iq.setPhotoType(type); + } + } + + vm->setClientVCard(iq); + onOwnVCardReceived(iq); +} + +void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) +{ + for (QXmppDiscoveryIq::Item item : items.items()) { + if (item.jid() != getServer()) { + dm->requestInfo(item.jid()); + } + } +} + +void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) +{ + qDebug() << "Discovery info received for account" << name; + if (info.from() == getServer()) { + if (info.features().contains("urn:xmpp:carbons:2")) { + qDebug() << "Enabling carbon copies for account" << name; + cm->setCarbonsEnabled(true); + } + } +} + +void Core::Account::handleDisconnection() +{ + cm->setCarbonsEnabled(false); + rh->handleOffline(); + archiveQueries.clear(); + pendingVCardRequests.clear(); + Shared::VCard vCard; //just to show, that there is now more pending request + for (const QString& jid : pendingVCardRequests) { + emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error + } + pendingVCardRequests.clear(); + ownVCardRequestInProgress = false; +} + +void Core::Account::onContactHistoryResponse(const std::list& list, bool last) +{ + RosterItem* contact = static_cast(sender()); + + qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; + if (last) { + qDebug() << "The response contains the first accounted message"; + } + emit responseArchive(contact->jid, list, last); +} + +void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ + mh->requestChangeMessage(jid, messageId, data);} + +void Core::Account::resendMessage(const QString& jid, const QString& id) { + mh->resendMessage(jid, id);} diff --git a/core/account.h b/core/account.h index 4cd3316..5ba834c 100644 --- a/core/account.h +++ b/core/account.h @@ -15,7 +15,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#pragma once + +#ifndef CORE_ACCOUNT_H +#define CORE_ACCOUNT_H #include #include @@ -27,14 +29,9 @@ #include #include -#include #include -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) -#include -#else #include -#endif #include #include #include @@ -42,58 +39,32 @@ #include #include #include +#include #include #include -#include -#include -#include -#include -#include +#include "shared/shared.h" #include "contact.h" #include "conference.h" -#include -#include +#include "networkaccess.h" #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" -#include "handlers/vcardhandler.h" -#include "handlers/discoveryhandler.h" -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - #include - #ifdef WITH_OMEMO - #include - #include "handlers/omemohandler.h" - #endif - #include "handlers/trusthandler.h" -#endif +namespace Core +{ -namespace Core { - -class Account : public QObject { +class Account : public QObject +{ Q_OBJECT friend class MessageHandler; friend class RosterHandler; - friend class VCardHandler; - friend class DiscoveryHandler; -#ifdef WITH_OMEMO - friend class OmemoHandler; - friend class TrustHandler; -#endif public: - enum class Error { - authentication, - other, - none - }; - Account( const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, - bool p_active, NetworkAccess* p_net, QObject* parent = 0); ~Account(); @@ -105,12 +76,8 @@ public: QString getPassword() const; QString getResource() const; QString getAvatarPath() const; - QString getBareJid() const; - QString getFullJid() const; Shared::Availability getAvailability() const; Shared::AccountPassword getPasswordType() const; - Error getLastError() const; - bool getActive() const; void setName(const QString& p_name); void setLogin(const QString& p_login); @@ -119,8 +86,8 @@ public: void setResource(const QString& p_resource); void setAvailability(Shared::Availability avail); void setPasswordType(Shared::AccountPassword pt); + QString getFullJid() const; void sendMessage(const Shared::Message& data); - void setActive(bool p_active); void requestArchive(const QString& jid, int count, const QString& before); void subscribeToContact(const QString& jid, const QString& reason); void unsubscribeFromContact(const QString& jid, const QString& reason); @@ -130,24 +97,19 @@ public: void removeContactFromGroupRequest(const QString& jid, const QString& groupName); void renameContactRequest(const QString& jid, const QString& newName); void requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data); - void setContactEncryption(const QString& jid, Shared::EncryptionProtocol value); void setRoomJoined(const QString& jid, bool joined); void setRoomAutoJoin(const QString& jid, bool joined); void removeRoomRequest(const QString& jid); void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); - void updateInfo(const Shared::Info& info); + void uploadVCard(const Shared::VCard& card); void resendMessage(const QString& jid, const QString& id); - void replaceMessage(const QString& originalId, const Shared::Message& data); - void invalidatePassword(); - - void discoverInfo(const QString& address, const QString& node); public slots: void connect(); void disconnect(); void reconnect(); - void requestInfo(const QString& jid); + void requestVCard(const QString& jid); signals: void changed(const QMap& data); @@ -171,12 +133,9 @@ signals: void addRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void removeRoomParticipant(const QString& jid, const QString& nickName); - void infoReady(const Shared::Info& info); + void receivedVCard(const QString& jid, const Shared::VCard& card); void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); - void needPassword(); - void infoDiscovered(const QString& address, const QString& node, const std::set& identities, const std::set& features); - void pepSupportChanged(Shared::Support support); private: QString name; @@ -185,26 +144,7 @@ private: QXmppConfiguration config; QXmppPresence presence; Shared::ConnectionState state; - - MessageHandler* mh; - RosterHandler* rh; - VCardHandler* vh; - DiscoveryHandler* dh; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - TrustHandler* th; -#endif -#ifdef WITH_OMEMO - OmemoHandler* oh; - - QXmppOmemoManager* om; -#endif -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - QXmppTrustManager* tm; - QXmppCarbonManagerV2* cm; - QXmppPubSubManager* psm; -#else QXmppCarbonManager* cm; -#endif QXmppMamManager* am; QXmppMucManager* mm; QXmppBookmarkManager* bm; @@ -216,14 +156,16 @@ private: bool reconnectScheduled; QTimer* reconnectTimer; + std::set pendingVCardRequests; + + QString avatarHash; + QString avatarType; + bool ownVCardRequestInProgress; NetworkAccess* network; - DelayManager::Manager* delay; Shared::AccountPassword passwordType; - Error lastError; - Shared::Support pepSupport; - bool active; - bool notReadyPassword; - bool loadingOmemo; + + MessageHandler* mh; + RosterHandler* rh; private slots: void onClientStateChange(QXmppClient::State state); @@ -236,13 +178,22 @@ private slots: void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); void onMamLog(QXmppLogger::MessageType type, const QString &msg); + + void onVCardReceived(const QXmppVCardIq& card); + void onOwnVCardReceived(const QXmppVCardIq& card); + void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); + void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); void onContactHistoryResponse(const std::list& list, bool last); private: void handleDisconnection(); void onReconnectTimer(); - void setPepSupport(Shared::Support support); - void runDiscoveryService(); }; + +void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); +void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); } + + +#endif // CORE_ACCOUNT_H diff --git a/core/adapterfunctions.cpp b/core/adapterFuctions.cpp similarity index 98% rename from core/adapterfunctions.cpp rename to core/adapterFuctions.cpp index eec5a9f..e2559d8 100644 --- a/core/adapterfunctions.cpp +++ b/core/adapterFuctions.cpp @@ -1,5 +1,5 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich * * This program is free software: you can redistribute it and/or modify @@ -15,8 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_ADAPTER_FUNCTIONS_H +#define CORE_ADAPTER_FUNCTIONS_H -#include "adapterfunctions.h" +#include "account.h" void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) { @@ -262,10 +264,12 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { phone.setType(phone.type() | QXmppVCardPhone::Preferred); } } - for (const std::pair& phone : phones) { + for (const std::pair& phone : phones) { phs.push_back(phone.second); } iq.setEmails(emails); iq.setPhones(phs); } + +#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/adapterfunctions.h b/core/adapterfunctions.h deleted file mode 100644 index 287816b..0000000 --- a/core/adapterfunctions.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef CORE_ADAPTER_FUNCTIONS_H -#define CORE_ADAPTER_FUNCTIONS_H - -#include -#include -#include -#include - -namespace Core { - -void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); -void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); - -template -QXmppTask makeReadyTask(T &&value) { - QXmppPromise promise; - promise.finish(std::move(value)); - return promise.task(); -} - -inline QXmppTask makeReadyTask() { - QXmppPromise promise; - promise.finish(); - return promise.task(); -} - -} - - -#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/archive.cpp b/core/archive.cpp new file mode 100644 index 0000000..c67f228 --- /dev/null +++ b/core/archive.cpp @@ -0,0 +1,1006 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "archive.h" +#include +#include +#include +#include +#include +#include + +Core::Archive::Archive(const QString& p_jid, QObject* parent): + QObject(parent), + jid(p_jid), + opened(false), + fromTheBeginning(false), + environment(), + main(), + order(), + stats(), + avatars() +{ +} + +Core::Archive::~Archive() +{ + close(); +} + +void Core::Archive::open(const QString& account) +{ + if (!opened) { + mdb_env_create(&environment); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + account + "/" + jid; + QDir cache(path); + + if (!cache.exists()) { + bool res = cache.mkpath(path); + if (!res) { + throw Directory(path.toStdString()); + } + } + + mdb_env_set_maxdbs(environment, 5); + mdb_env_set_mapsize(environment, +#ifdef Q_OS_WIN + // On Windows, the file is immediately allocated. + // So we have to limit the size. + 80UL * 1024UL * 1024UL +#else + 512UL * 1024UL * 1024UL +#endif + ); + mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + mdb_dbi_open(txn, "main", MDB_CREATE, &main); + mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order); + mdb_dbi_open(txn, "stats", MDB_CREATE, &stats); + mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars); + mdb_dbi_open(txn, "sid", MDB_CREATE, &sid); + mdb_txn_commit(txn); + + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + try { + fromTheBeginning = getStatBoolValue("beginning", txn); + } catch (const NotFound& e) { + fromTheBeginning = false; + } + + std::string sJid = jid.toStdString(); + AvatarInfo info; + bool hasAvatar = readAvatarInfo(info, sJid, txn); + mdb_txn_abort(txn); + + if (hasAvatar) { + QFile ava(path + "/" + sJid.c_str() + "." + info.type); + if (!ava.exists()) { + bool success = dropAvatar(sJid); + if (!success) { + qDebug() << "error opening archive" << jid << "for account" << account + << ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error"; + } + } + } + + opened = true; + } +} + +void Core::Archive::close() +{ + if (opened) { + mdb_dbi_close(environment, sid); + mdb_dbi_close(environment, avatars); + mdb_dbi_close(environment, stats); + mdb_dbi_close(environment, order); + mdb_dbi_close(environment, main); + mdb_env_close(environment); + opened = false; + } +} + +bool Core::Archive::addElement(const Shared::Message& message) +{ + if (!opened) { + throw Closed("addElement", jid.toStdString()); + } + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + message.serialize(ds); + quint64 stamp = message.getTime().toMSecsSinceEpoch(); + const std::string& id = message.getId().toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + int rc; + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + if (rc == 0) { + MDB_val orderKey; + orderKey.mv_size = 8; + orderKey.mv_data = (uint8_t*) &stamp; + + rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0); + if (rc) { + qDebug() << "An element couldn't be inserted into the index" << mdb_strerror(rc); + mdb_txn_abort(txn); + return false; + } else { + if (message.getStanzaId().size() > 0) { + const std::string& szid = message.getStanzaId().toStdString(); + + lmdbKey.mv_size = szid.size(); + lmdbKey.mv_data = (char*)szid.c_str(); + lmdbData.mv_size = id.size(); + lmdbData.mv_data = (uint8_t*)id.data(); + rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + + if (rc) { + qDebug() << "An element stanzaId to id pair couldn't be inserted into the archive" << mdb_strerror(rc); + mdb_txn_abort(txn); + return false; + } else { + rc = mdb_txn_commit(txn); + if (rc) { + qDebug() << "A transaction error: " << mdb_strerror(rc); + return false; + } + return true; + } + + } else { + rc = mdb_txn_commit(txn); + if (rc) { + qDebug() << "A transaction error: " << mdb_strerror(rc); + return false; + } + return true; + } + } + } else { + qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc); + mdb_txn_abort(txn); + return false; + } +} + +void Core::Archive::clear() +{ + if (!opened) { + throw Closed("clear", jid.toStdString()); + } + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + mdb_drop(txn, main, 0); + mdb_drop(txn, order, 0); + mdb_drop(txn, stats, 0); + mdb_drop(txn, avatars, 0); + mdb_drop(txn, sid, 0); + mdb_txn_commit(txn); +} + +Shared::Message Core::Archive::getElement(const QString& id) const +{ + if (!opened) { + throw Closed("getElement", jid.toStdString()); + } + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + try { + Shared::Message msg = getMessage(id.toStdString(), txn); + mdb_txn_abort(txn); + return msg; + } catch (...) { + mdb_txn_abort(txn); + throw; + } +} + +bool Core::Archive::hasElement(const QString& id) const +{ + if (!opened) { + throw Closed("hasElement", jid.toStdString()); + } + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + bool has; + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.toStdString().c_str(); + int rc = mdb_get(txn, main, &lmdbKey, &lmdbData); + has = rc == 0; + mdb_txn_abort(txn); + + return has; +} + +Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) const +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + int rc = mdb_get(txn, main, &lmdbKey, &lmdbData); + + if (rc == 0) { + QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + Shared::Message msg; + msg.deserialize(ds); + + return msg; + } else if (rc == MDB_NOTFOUND) { + throw NotFound(id, jid.toStdString()); + } else { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } +} + +void Core::Archive::changeMessage(const QString& id, const QMap& data) +{ + if (!opened) { + throw Closed("setMessageState", jid.toStdString()); + } + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + + std::string strId(id.toStdString()); + try { + Shared::Message msg = getMessage(strId, txn); + bool hadStanzaId = msg.getStanzaId().size() > 0; + QDateTime oTime = msg.getTime(); + bool idChange = msg.change(data); + QDateTime nTime = msg.getTime(); + bool orderChange = oTime != nTime; + + MDB_val lmdbKey, lmdbData; + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + msg.serialize(ds); + + lmdbKey.mv_size = strId.size(); + lmdbKey.mv_data = (char*)strId.c_str(); + int rc; + if (idChange || orderChange) { + if (idChange) { + rc = mdb_del(txn, main, &lmdbKey, &lmdbData); + } else { + quint64 ostamp = oTime.toMSecsSinceEpoch(); + lmdbData.mv_data = (quint8*)&ostamp; + lmdbData.mv_size = 8; + rc = mdb_del(txn, order, &lmdbData, &lmdbKey); + } + if (rc == 0) { + strId = msg.getId().toStdString(); + lmdbKey.mv_size = strId.size(); + lmdbKey.mv_data = (char*)strId.c_str(); + + quint64 stamp = nTime.toMSecsSinceEpoch(); + lmdbData.mv_data = (quint8*)&stamp; + lmdbData.mv_size = 8; + rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0); + if (rc != 0) { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + } else { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + } + + QString qsid = msg.getStanzaId(); + if (qsid.size() > 0 && (idChange || !hadStanzaId)) { + std::string szid = qsid.toStdString(); + + lmdbData.mv_size = szid.size(); + lmdbData.mv_data = (char*)szid.c_str(); + rc = mdb_put(txn, sid, &lmdbData, &lmdbKey, 0); + + if (rc != 0) { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + }; + + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0); + if (rc == 0) { + rc = mdb_txn_commit(txn); + } else { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + + } catch (...) { + mdb_txn_abort(txn); + throw; + } +} + +Shared::Message Core::Archive::newest() +{ + return edge(true); +} + +QString Core::Archive::newestId() +{ + if (!opened) { + throw Closed("newestId", jid.toStdString()); + } + Shared::Message msg = newest(); + return msg.getId(); +} + +QString Core::Archive::oldestId() +{ + if (!opened) { + throw Closed("oldestId", jid.toStdString()); + } + Shared::Message msg = oldest(); + return msg.getId(); +} + +Shared::Message Core::Archive::oldest() +{ + return edge(false); +} + +Shared::Message Core::Archive::edge(bool end) +{ + QString name; + MDB_cursor_op begin; + MDB_cursor_op iteration; + if (end) { + name = "newest"; + begin = MDB_LAST; + iteration = MDB_PREV; + } else { + name = "oldest"; + begin = MDB_FIRST; + iteration = MDB_NEXT; + } + + + if (!opened) { + throw Closed(name.toStdString(), jid.toStdString()); + } + + MDB_txn *txn; + MDB_cursor* cursor; + MDB_val lmdbKey, lmdbData; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + rc = mdb_cursor_open(txn, order, &cursor); + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, begin); + + Shared::Message msg = getStoredMessage(txn, cursor, iteration, &lmdbKey, &lmdbData, rc); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + if (rc) { + qDebug() << "Error geting" << name << "message" << mdb_strerror(rc); + throw Empty(jid.toStdString()); + } else { + return msg; + } +} + +Shared::Message Core::Archive::getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc) +{ + Shared::Message msg; + std::string sId; + while (true) { + if (rc) { + break; + } + sId = std::string((char*)value->mv_data, value->mv_size); + + try { + msg = getMessage(sId, txn); + if (msg.serverStored()) { + break; + } else { + rc = mdb_cursor_get(cursor, key, value, op); + } + } catch (...) { + break; + } + } + + return msg; +} + +unsigned int Core::Archive::addElements(const std::list& messages) +{ + if (!opened) { + throw Closed("addElements", jid.toStdString()); + } + + int success = 0; + int rc = 0; + MDB_val lmdbKey, lmdbData; + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + std::list::const_iterator itr = messages.begin(); + while (rc == 0 && itr != messages.end()) { + const Shared::Message& message = *itr; + + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + message.serialize(ds); + quint64 stamp = message.getTime().toMSecsSinceEpoch(); + const std::string& id = message.getId().toStdString(); + + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); + + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + if (rc == 0) { + MDB_val orderKey; + orderKey.mv_size = 8; + orderKey.mv_data = (uint8_t*) &stamp; + + rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0); + if (rc) { + qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc); + } else { + if (message.getStanzaId().size() > 0) { + const std::string& szid = message.getStanzaId().toStdString(); + + lmdbKey.mv_size = szid.size(); + lmdbKey.mv_data = (char*)szid.c_str(); + lmdbData.mv_size = id.size(); + lmdbData.mv_data = (uint8_t*)id.data(); + rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + + if (rc) { + qDebug() << "During bulk add an element stanzaId to id pair couldn't be inserted into the archive, continuing without stanzaId" << mdb_strerror(rc); + } + + } + success++; + } + } else { + if (rc == MDB_KEYEXIST) { + rc = 0; + } else { + qDebug() << "An element couldn't been added to the archive, aborting the transaction" << mdb_strerror(rc); + } + } + itr++; + } + + if (rc != 0) { + mdb_txn_abort(txn); + success = 0; + } else { + mdb_txn_commit(txn); + } + + return success; +} + +long unsigned int Core::Archive::size() const +{ + if (!opened) { + throw Closed("size", jid.toStdString()); + } + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_stat stat; + mdb_stat(txn, order, &stat); + size_t amount = stat.ms_entries; + mdb_txn_abort(txn); + return amount; +} + +std::list Core::Archive::getBefore(int count, const QString& id) +{ + if (!opened) { + throw Closed("getBefore", jid.toStdString()); + } + std::list res; + MDB_cursor* cursor; + MDB_txn *txn; + MDB_val lmdbKey, lmdbData; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + rc = mdb_cursor_open(txn, order, &cursor); + if (id == "") { + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST); + if (rc) { + qDebug() << "Error getting before" << mdb_strerror(rc) << ", id:" << id; + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + throw Empty(jid.toStdString()); + } + } else { + std::string stdId(id.toStdString()); + try { + Shared::Message msg = getMessage(stdId, txn); + quint64 stamp = msg.getTime().toMSecsSinceEpoch(); + lmdbKey.mv_data = (quint8*)&stamp; + lmdbKey.mv_size = 8; + + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET); + + if (rc) { + qDebug() << "Error getting before: couldn't set " << mdb_strerror(rc); + throw NotFound(stdId, jid.toStdString()); + } else { + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV); + if (rc) { + qDebug() << "Error getting before, couldn't prev " << mdb_strerror(rc); + throw NotFound(stdId, jid.toStdString()); + } + } + + } catch (...) { + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw; + } + } + + do { + MDB_val dKey, dData; + dKey.mv_size = lmdbData.mv_size; + dKey.mv_data = lmdbData.mv_data; + rc = mdb_get(txn, main, &dKey, &dData); + if (rc) { + qDebug() <<"Get error: " << mdb_strerror(rc); + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw NotFound(sId, jid.toStdString()); + } else { + QByteArray ba((char*)dData.mv_data, dData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + res.emplace_back(); + Shared::Message& msg = res.back(); + msg.deserialize(ds); + } + + --count; + + } while (count > 0 && mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + return res; +} + +bool Core::Archive::isFromTheBeginning() +{ + if (!opened) { + throw Closed("isFromTheBeginning", jid.toStdString()); + } + return fromTheBeginning; +} + +void Core::Archive::setFromTheBeginning(bool is) +{ + if (!opened) { + throw Closed("setFromTheBeginning", jid.toStdString()); + } + if (fromTheBeginning != is) { + fromTheBeginning = is; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + bool success = setStatValue("beginning", is, txn); + if (success) { + mdb_txn_commit(txn); + } else { + mdb_txn_abort(txn); + } + } +} + +QString Core::Archive::idByStanzaId(const QString& stanzaId) const +{ + if (!opened) { + throw Closed("idByStanzaId", jid.toStdString()); + } + QString id; + std::string ssid = stanzaId.toStdString(); + + MDB_txn *txn; + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = ssid.size(); + lmdbKey.mv_data = (char*)ssid.c_str(); + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + int rc = mdb_get(txn, sid, &lmdbKey, &lmdbData); + if (rc == 0) { + id = QString::fromStdString(std::string((char*)lmdbData.mv_data, lmdbData.mv_size)); + } + mdb_txn_abort(txn); + + return id; +} + +QString Core::Archive::stanzaIdById(const QString& id) const +{ + if (!opened) { + throw Closed("stanzaIdById", jid.toStdString()); + } + + try { + Shared::Message msg = getElement(id); + return msg.getStanzaId(); + } catch (const NotFound& e) { + return QString(); + } catch (const Empty& e) { + return QString(); + } catch (...) { + throw; + } +} + +void Core::Archive::printOrder() +{ + qDebug() << "Printing order"; + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_cursor* cursor; + mdb_cursor_open(txn, order, &cursor); + MDB_val lmdbKey, lmdbData; + + mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + + do { + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + qDebug() << QString(sId.c_str()); + } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +} + +void Core::Archive::printKeys() +{ + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_cursor* cursor; + mdb_cursor_open(txn, main, &cursor); + MDB_val lmdbKey, lmdbData; + + mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + + do { + std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size); + qDebug() << QString(sId.c_str()); + } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +} + +bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn) +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + int rc; + rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + throw NotFound(id, jid.toStdString()); + } else if (rc) { + std::string err(mdb_strerror(rc)); + qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str(); + throw Unknown(jid.toStdString(), err); + } else { + uint8_t value = *(uint8_t*)(lmdbData.mv_data); + bool is; + if (value == 144) { + is = false; + } else if (value == 72) { + is = true; + } else { + qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong"; + throw NotFound(id, jid.toStdString()); + } + return is; + } +} + +std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn) +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + int rc; + rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + throw NotFound(id, jid.toStdString()); + } else if (rc) { + std::string err(mdb_strerror(rc)); + qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str(); + throw Unknown(jid.toStdString(), err); + } else { + std::string value((char*)lmdbData.mv_data, lmdbData.mv_size); + return value; + } +} + +bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn) +{ + uint8_t binvalue = 144; + if (value) { + binvalue = 72; + } + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = sizeof binvalue; + lmdbData.mv_data = &binvalue; + int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); + return false; + } + return true; +} + +bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn) +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = value.size(); + lmdbData.mv_data = (char*)value.c_str(); + int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); + return false; + } + return true; +} + +bool Core::Archive::dropAvatar(const std::string& resource) +{ + MDB_txn *txn; + MDB_val lmdbKey; + mdb_txn_begin(environment, NULL, 0, &txn); + lmdbKey.mv_size = resource.size(); + lmdbKey.mv_data = (char*)resource.c_str(); + int rc = mdb_del(txn, avatars, &lmdbKey, NULL); + if (rc != 0) { + mdb_txn_abort(txn); + return false; + } else { + mdb_txn_commit(txn); + return true; + } +} + +bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) +{ + if (!opened) { + throw Closed("setAvatar", jid.toStdString()); + } + + AvatarInfo oldInfo; + bool hasAvatar = readAvatarInfo(oldInfo, resource); + std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString(); + + if (data.size() == 0) { + if (!hasAvatar) { + return false; + } else { + return dropAvatar(res); + } + } else { + const char* cep; + mdb_env_get_path(environment, &cep); + QString currentPath(cep); + bool needToRemoveOld = false; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(data); + QByteArray newHash(hash.result()); + if (hasAvatar) { + if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) { + return false; + } + QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type); + if (oldAvatar.exists()) { + if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) { + needToRemoveOld = true; + } else { + qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); + return false; + } + } + } + QMimeDatabase db; + QMimeType type = db.mimeTypeForData(data); + QString ext = type.preferredSuffix(); + QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(data); + newAvatar.close(); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + + MDB_val lmdbKey, lmdbData; + QByteArray value; + newInfo.type = ext; + newInfo.hash = newHash; + newInfo.autogenerated = generated; + newInfo.serialize(&value); + lmdbKey.mv_size = res.size(); + lmdbKey.mv_data = (char*)res.c_str(); + lmdbData.mv_size = value.size(); + lmdbData.mv_data = value.data(); + int rc = mdb_put(txn, avatars, &lmdbKey, &lmdbData, 0); + + if (rc != 0) { + qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type); + } + mdb_txn_abort(txn); + return false; + } else { + mdb_txn_commit(txn); + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); + oldAvatar.remove(); + } + return true; + } + } else { + qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type); + } + return false; + } + } +} + +bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const +{ + if (!opened) { + throw Closed("readAvatarInfo", jid.toStdString()); + } + std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString(); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + try { + bool success = readAvatarInfo(target, res, txn); + mdb_txn_abort(txn); + return success; + } catch (...) { + mdb_txn_abort(txn); + throw; + } + +} + +bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std::string& res, MDB_txn* txn) const +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = res.size(); + lmdbKey.mv_data = (char*)res.c_str(); + + int rc; + rc = mdb_get(txn, avatars, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + return false; + } else if (rc) { + std::string err(mdb_strerror(rc)); + qDebug() << "error reading avatar info for" << res.c_str() << "resource of" << jid << err.c_str(); + throw Unknown(jid.toStdString(), err); + } else { + target.deserialize((char*)lmdbData.mv_data, lmdbData.mv_size); + return true; + } +} + +void Core::Archive::readAllResourcesAvatars(std::map& data) const +{ + if (!opened) { + throw Closed("readAllResourcesAvatars", jid.toStdString()); + } + + int rc; + MDB_val lmdbKey, lmdbData; + MDB_txn *txn; + MDB_cursor* cursor; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + mdb_cursor_open(txn, avatars, &cursor); + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + if (rc == 0) { //the db might be empty yet + do { + std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size); + QString res(sId.c_str()); + if (res != jid) { + data.emplace(res, AvatarInfo()); + data[res].deserialize((char*)lmdbData.mv_data, lmdbData.mv_size); + } + } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); + } + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +} + +Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const +{ + if (!opened) { + throw Closed("readAvatarInfo", jid.toStdString()); + } + + AvatarInfo info; + bool success = readAvatarInfo(info, resource); + if (success) { + return info; + } else { + throw NoAvatar(jid.toStdString(), resource.toStdString()); + } +} + +Core::Archive::AvatarInfo::AvatarInfo(): +type(), +hash(), +autogenerated(false) {} + +Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated): +type(p_type), +hash(p_hash), +autogenerated(p_autogenerated) {} + +void Core::Archive::AvatarInfo::deserialize(char* pointer, uint32_t size) +{ + QByteArray data = QByteArray::fromRawData(pointer, size); + QDataStream in(&data, QIODevice::ReadOnly); + + in >> type >> hash >> autogenerated; +} + +void Core::Archive::AvatarInfo::serialize(QByteArray* ba) const +{ + QDataStream out(ba, QIODevice::WriteOnly); + + out << type << hash << autogenerated; +} diff --git a/core/archive.h b/core/archive.h new file mode 100644 index 0000000..47c62dc --- /dev/null +++ b/core/archive.h @@ -0,0 +1,197 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CORE_ARCHIVE_H +#define CORE_ARCHIVE_H + +#include +#include +#include +#include + +#include "shared/message.h" +#include "shared/exception.h" +#include +#include + +namespace Core { + +class Archive : public QObject +{ + Q_OBJECT +public: + class AvatarInfo; + + Archive(const QString& jid, QObject* parent = 0); + ~Archive(); + + void open(const QString& account); + void close(); + + bool addElement(const Shared::Message& message); + unsigned int addElements(const std::list& messages); + Shared::Message getElement(const QString& id) const; + bool hasElement(const QString& id) const; + void changeMessage(const QString& id, const QMap& data); + Shared::Message oldest(); + QString oldestId(); + Shared::Message newest(); + QString newestId(); + void clear(); + long unsigned int size() const; + std::list getBefore(int count, const QString& id); + bool isFromTheBeginning(); + void setFromTheBeginning(bool is); + bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = ""); + AvatarInfo getAvatarInfo(const QString& resource = "") const; + bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const; + void readAllResourcesAvatars(std::map& data) const; + QString idByStanzaId(const QString& stanzaId) const; + QString stanzaIdById(const QString& id) const; + +public: + const QString jid; + +public: + class Directory: + public Utils::Exception + { + public: + Directory(const std::string& p_path):Exception(), path(p_path){} + + std::string getMessage() const{return "Can't create directory for database at " + path;} + private: + std::string path; + }; + + class Closed: + public Utils::Exception + { + public: + Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){} + + std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;} + private: + std::string operation; + std::string account; + }; + + class NotFound: + public Utils::Exception + { + public: + NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){} + + std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;} + private: + std::string key; + std::string account; + }; + + class Empty: + public Utils::Exception + { + public: + Empty(const std::string& acc):Exception(), account(acc){} + + std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";} + private: + std::string account; + }; + + class Exist: + public Utils::Exception + { + public: + Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){} + + std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";} + private: + std::string account; + std::string key; + }; + + class NoAvatar: + public Utils::Exception + { + public: + NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){ + if (resource.size() == 0) { + resource = "for himself"; + } + } + + std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;} + private: + std::string element; + std::string resource; + }; + + class Unknown: + public Utils::Exception + { + public: + Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){} + + std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;} + private: + std::string account; + std::string msg; + }; + + + class AvatarInfo { + public: + AvatarInfo(); + AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated); + + void deserialize(char* pointer, uint32_t size); + void serialize(QByteArray* ba) const; + + QString type; + QByteArray hash; + bool autogenerated; + }; + +private: + bool opened; + bool fromTheBeginning; + MDB_env* environment; + MDB_dbi main; //id to message + MDB_dbi order; //time to id + MDB_dbi stats; + MDB_dbi avatars; + MDB_dbi sid; //stanzaId to id + + bool getStatBoolValue(const std::string& id, MDB_txn* txn); + std::string getStatStringValue(const std::string& id, MDB_txn* txn); + + bool setStatValue(const std::string& id, bool value, MDB_txn* txn); + bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn); + bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const; + void printOrder(); + void printKeys(); + bool dropAvatar(const std::string& resource); + Shared::Message getMessage(const std::string& id, MDB_txn* txn) const; + Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc); + Shared::Message edge(bool end); +}; + +} + +#endif // CORE_ARCHIVE_H diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt deleted file mode 100644 index 751a01f..0000000 --- a/core/components/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -set(SOURCE_FILES - networkaccess.cpp - clientcache.cpp - urlstorage.cpp - archive.cpp -) - -set(HEADER_FILES - networkaccess.h - clientcache.h - urlstorage.h - archive.h -) - -target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/components/archive.cpp b/core/components/archive.cpp deleted file mode 100644 index 2f7139a..0000000 --- a/core/components/archive.cpp +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "archive.h" - -#include -#include - -#include - -Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* parent): - QObject(parent), - jid(p_jid), - account(account), - opened(false), - db(account + "/" + jid), - messages(db.addStorage("messages")), - order(db.addStorage("order", true)), - stats(db.addStorage("stats")), - avatars(db.addStorage("avatars")), - stanzaIdToId(db.addStorage("stanzaIdToId")), - cursor(order->createCursor()) -{} - -Core::Archive::~Archive() { - close(); -} - -void Core::Archive::open() { - db.open(); - LMDBAL::WriteTransaction txn = db.beginTransaction(); - - AvatarInfo info; - bool hasAvatar = false; - try { - avatars->getRecord(jid, info, txn); - hasAvatar = true; - } catch (const LMDBAL::NotFound& e) {} - - if (!hasAvatar) - return; - - QFile ava(db.getPath() + "/" + jid + "." + info.type); - if (ava.exists()) - return; - - try { - avatars->removeRecord(jid, txn); - txn.commit(); - } catch (const std::exception& e) { - qDebug() << e.what(); - qDebug() << "error opening archive" << jid << "for account" << account - << ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error"; - } -} - -void Core::Archive::close() { - db.close(); -} - -bool Core::Archive::addElement(const Shared::Message& message) { - QString id = message.getId(); - qDebug() << "Adding message with id " << id; - - try { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - messages->addRecord(id, message, txn); - order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn); - QString stanzaId = message.getStanzaId(); - if (!stanzaId.isEmpty()) - stanzaIdToId->addRecord(stanzaId, id, txn); - - txn.commit(); - return true; - } catch (const std::exception& e) { - qDebug() << "Could not add message with id " + id; - qDebug() << e.what(); - } - - return false; -} - -void Core::Archive::clear() { - db.drop(); -} - -Shared::Message Core::Archive::getElement(const QString& id) const { - return messages->getRecord(id); -} - -bool Core::Archive::hasElement(const QString& id) const { - return messages->checkRecord(id); -} - -void Core::Archive::changeMessage(const QString& id, const QMap& data) { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - Shared::Message msg = messages->getRecord(id, txn); - - bool hadStanzaId = !msg.getStanzaId().isEmpty(); - QDateTime oTime = msg.getTime(); - bool idChange = msg.change(data); - QString newId = msg.getId(); - QDateTime nTime = msg.getTime(); - - bool orderChange = oTime != nTime; - if (idChange || orderChange) { - if (idChange) - messages->removeRecord(id, txn); - - if (orderChange) - order->removeRecord(oTime.toMSecsSinceEpoch(), txn); - - order->forceRecord(nTime.toMSecsSinceEpoch(), newId, txn); - } - - QString sid = msg.getStanzaId(); - if (!sid.isEmpty() && (idChange || !hadStanzaId)) - stanzaIdToId->forceRecord(sid, newId, txn); - - messages->forceRecord(newId, msg, txn); - txn.commit(); -} - -Shared::Message Core::Archive::newest() const { - LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); - - try { - cursor.open(txn); - while (true) { - std::pair pair = cursor.prev(); - Shared::Message msg = messages->getRecord(pair.second, txn); - if (msg.serverStored()) { - cursor.close(); - return msg; - } - } - } catch (...) { - cursor.close(); - throw; - } -} - -QString Core::Archive::newestId() const { - Shared::Message msg = newest(); - return msg.getId(); -} - -QString Core::Archive::oldestId() const { - Shared::Message msg = oldest(); - return msg.getId(); -} - -Shared::Message Core::Archive::oldest() const { - LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); - - try { - cursor.open(txn); - while (true) { - std::pair pair = cursor.next(); - Shared::Message msg = messages->getRecord(pair.second, txn); - if (msg.serverStored()) { - cursor.close(); - return msg; - } - } - } catch (...) { - cursor.close(); - throw; - } -} - -unsigned int Core::Archive::addElements(const std::list& messages) { - unsigned int success = 0; - LMDBAL::WriteTransaction txn = db.beginTransaction(); - for (const Shared::Message& message : messages) { - QString id = message.getId(); - bool added = false; - try { - Core::Archive::messages->addRecord(id, message, txn); - added = true; - } catch (const LMDBAL::Exist& e) {} - - if (!added) - continue; - - order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn); - - QString sid = message.getStanzaId(); - if (!sid.isEmpty()) - stanzaIdToId->addRecord(sid, id, txn); - - ++success; - } - txn.commit(); - return success; -} - -long unsigned int Core::Archive::size() const { - return order->count(); -} - -std::list Core::Archive::getBefore(unsigned int count, const QString& id) { - LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); - std::list res; - try { - cursor.open(txn); - if (!id.isEmpty()) { - Shared::Message reference = messages->getRecord(id, txn); - uint64_t stamp = reference.getTime().toMSecsSinceEpoch(); - cursor.set(stamp); - } - - for (unsigned int i = 0; i < count; ++i) { - std::pair pair; - cursor.prev(pair.first, pair.second); - - res.emplace_back(); - Shared::Message& msg = res.back(); - messages->getRecord(pair.second, msg, txn); - } - cursor.close(); - - return res; - } catch (const LMDBAL::NotFound& e) { - cursor.close(); - if (res.empty()) - throw e; - else - return res; - } catch (...) { - cursor.close(); - throw; - } -} - -bool Core::Archive::isFromTheBeginning() const { - try { - return stats->getRecord("fromTheBeginning").toBool(); - } catch (const LMDBAL::NotFound& e) { - return false; - } -} - -void Core::Archive::setFromTheBeginning(bool is) { - stats->forceRecord("fromTheBeginning", is); -} - -Shared::EncryptionProtocol Core::Archive::encryption() const { - try { - return stats->getRecord("encryption").value(); - } catch (const LMDBAL::NotFound& e) { - return Shared::EncryptionProtocol::none; - } -} - -bool Core::Archive::setEncryption(Shared::EncryptionProtocol is) { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - Shared::EncryptionProtocol current = Shared::EncryptionProtocol::none; - try { - current = stats->getRecord("encryption", txn).value(); - } catch (const LMDBAL::NotFound& e) {} - - if (is != current) { - stats->forceRecord("encryption", static_cast(is), txn); - txn.commit(); - return true; - } - - return false; -} - -QString Core::Archive::idByStanzaId(const QString& stanzaId) const { - return stanzaIdToId->getRecord(stanzaId); -} - -QString Core::Archive::stanzaIdById(const QString& id) const { - try { - Shared::Message msg = getElement(id); - return msg.getStanzaId(); - } catch (const LMDBAL::NotFound& e) { - return QString(); - } -} - -bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - AvatarInfo oldInfo; - bool haveAvatar = false; - QString res = resource.isEmpty() ? jid : resource; - try { - avatars->getRecord(res, oldInfo, txn); - haveAvatar = true; - } catch (const LMDBAL::NotFound& e) {} - - if (data.size() == 0) { - if (!haveAvatar) - return false; - - avatars->removeRecord(res, txn); - txn.commit(); - return true; - } - - QString currentPath = db.getPath(); - bool needToRemoveOld = false; - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(data); - QByteArray newHash(hash.result()); - if (haveAvatar) { - if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) - return false; - - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type); - if (oldAvatar.exists()) { - if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) { - needToRemoveOld = true; - } else { - qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); - return false; - } - } - } - QMimeDatabase mimedb; - QMimeType type = mimedb.mimeTypeForData(data); - QString ext = type.preferredSuffix(); - QFile newAvatar(currentPath + "/" + res + "." + ext); - if (!newAvatar.open(QFile::WriteOnly)) { - qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); - } - return false; - } - - newAvatar.write(data); - newAvatar.close(); - - newInfo.type = ext; - newInfo.hash = newHash; - newInfo.autogenerated = generated; - try { - avatars->forceRecord(res, newInfo, txn); - txn.commit(); - } catch (...) { - qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); - } - return false; - } - - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.remove(); - } - return true; -} - -bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const { - try { - avatars->getRecord(resource.isEmpty() ? jid : resource, target); - return true; - } catch (const LMDBAL::NotFound& e) { - return false; - } -} - -void Core::Archive::readAllResourcesAvatars(std::map& data) const { - avatars->readAll(data); -} - -Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const { - return avatars->getRecord(resource); -} - -Core::Archive::AvatarInfo::AvatarInfo(): - type(), - hash(), - autogenerated(false) -{} - -Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated): - type(p_type), - hash(p_hash), - autogenerated(p_autogenerated) -{} - -QDataStream & operator<<(QDataStream& out, const Core::Archive::AvatarInfo& info) { - out << info.type; - out << info.hash; - out << info.autogenerated; - - return out; -} - -QDataStream & operator>>(QDataStream& in, Core::Archive::AvatarInfo& info) { - in >> info.type; - in >> info.hash; - in >> info.autogenerated; - - return in; -} diff --git a/core/components/archive.h b/core/components/archive.h deleted file mode 100644 index 67208b4..0000000 --- a/core/components/archive.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "shared/enums.h" -#include "shared/message.h" -#include "shared/exception.h" -#include - -#include -#include -#include - -namespace Core { - -class Archive : public QObject { - Q_OBJECT -public: - class AvatarInfo; - - Archive(const QString& account, const QString& jid, QObject* parent = 0); - ~Archive(); - - void open(); - void close(); - - bool addElement(const Shared::Message& message); - unsigned int addElements(const std::list& messages); - Shared::Message getElement(const QString& id) const; - bool hasElement(const QString& id) const; - void changeMessage(const QString& id, const QMap& data); - Shared::Message oldest() const; - QString oldestId() const; - Shared::Message newest() const; - QString newestId() const; - void clear(); - long unsigned int size() const; - std::list getBefore(unsigned int count, const QString& id); - bool isFromTheBeginning() const; - void setFromTheBeginning(bool is); - Shared::EncryptionProtocol encryption() const; - bool setEncryption(Shared::EncryptionProtocol value); //returns true if changed, false otherwise - bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = ""); - AvatarInfo getAvatarInfo(const QString& resource = "") const; - bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const; - void readAllResourcesAvatars(std::map& data) const; - QString idByStanzaId(const QString& stanzaId) const; - QString stanzaIdById(const QString& id) const; - -public: - const QString jid; - const QString account; - -public: - class AvatarInfo { - public: - AvatarInfo(); - AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated); - - QString type; - QByteArray hash; - bool autogenerated; - }; - -private: - bool opened; - LMDBAL::Base db; - LMDBAL::Storage* messages; - LMDBAL::Storage* order; - LMDBAL::Storage* stats; - LMDBAL::Storage* avatars; - LMDBAL::Storage* stanzaIdToId; - mutable LMDBAL::Cursor cursor; -}; - -} - -QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info); -QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info); diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp deleted file mode 100644 index f1eb886..0000000 --- a/core/components/clientcache.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "clientcache.h" - -#include - -Core::ClientCache::ClientCache(): - db("clients"), - cache(db.addCache("info")), - requested(), - specific() -{ - db.open(); -} - -Core::ClientCache::~ClientCache() { - db.close(); -} - -void Core::ClientCache::open() { - db.open();} - -void Core::ClientCache::close() { - db.close();} - -bool Core::ClientCache::checkClient(const Shared::ClientId& p_id) { - QString id = p_id.getId(); - if (requested.count(id) == 0 && !cache->checkRecord(id)) { - requested.emplace(id, p_id); - emit requestClientInfo(id); - return false; - } - - return true; -} - -bool Core::ClientCache::registerClientInfo ( - const QString& sourceFullJid, - const QString& id, - const std::set& identities, - const std::set& features) -{ - std::map::iterator itr = requested.find(id); - if (itr != requested.end()) { - Shared::ClientInfo& info = itr->second; - info.identities = identities; - info.extensions = features; - - bool valid = info.valid(); - if (valid) { - cache->addRecord(id, info); - } else { - info.specificPresence = sourceFullJid; - specific.insert(std::make_pair(sourceFullJid, info)); - } - requested.erase(id); - return valid; - } else { - return false; - } -} diff --git a/core/components/clientcache.h b/core/components/clientcache.h deleted file mode 100644 index c68dbfb..0000000 --- a/core/components/clientcache.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -#include -#include - -#include - -#include -#include -#include - -namespace Core { - -class ClientCache : public QObject { - Q_OBJECT -public: - ClientCache(); - ~ClientCache(); - - void open(); - void close(); - -signals: - void requestClientInfo(const QString& id); - -public slots: - bool checkClient(const Shared::ClientId& id); - bool registerClientInfo( - const QString& sourceFullJid, - const QString& id, - const std::set& identities, - const std::set& features - ); - -private: - LMDBAL::Base db; - LMDBAL::Cache* cache; - std::map requested; - std::map specific; -}; - -} diff --git a/core/components/urlstorage.cpp b/core/components/urlstorage.cpp deleted file mode 100644 index 5e34792..0000000 --- a/core/components/urlstorage.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "urlstorage.h" - -Core::UrlStorage::UrlStorage(const QString& p_name): - base(p_name), - urlToInfo(base.addStorage("urlToInfo")), - pathToUrl(base.addStorage("pathToUrl")) -{} - -Core::UrlStorage::~UrlStorage() { - close(); -} - -void Core::UrlStorage::open() { - base.open(); -} - -void Core::UrlStorage::close() { - base.close(); -} - -void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) { - LMDBAL::WriteTransaction txn = base.beginTransaction(); - writeInfo(key, info, txn, overwrite); - txn.commit(); -} - -void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite) { - if (overwrite) - urlToInfo->forceRecord(key, info, txn); - else - urlToInfo->addRecord(key, info, txn); - - if (info.hasPath()) - pathToUrl->forceRecord(info.getPath(), key, txn); -} - -void Core::UrlStorage::addFile(const QString& url) { - addToInfo(url, "", "", ""); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& path) { - addToInfo(url, "", "", "", path); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) { - addToInfo(url, account, jid, id); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) { - addToInfo(url, account, jid, id, path); -} - -void Core::UrlStorage::addFile(const std::list& msgs, const QString& url, const QString& path) { - UrlInfo info (path, msgs); - writeInfo(url, info, true); -} - -QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id){ - return addToInfo(url, account, jid, id).getPath(); -} - -Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo( - const QString& url, - const QString& account, - const QString& jid, - const QString& id, - const QString& path -) { - UrlInfo info; - LMDBAL::WriteTransaction txn = base.beginTransaction(); - - try { - urlToInfo->getRecord(url, info, txn); - } catch (const LMDBAL::NotFound& e) {} - - bool pathChange = false; - bool listChange = false; - if (path != "-s") { - if (info.getPath() != path) { - info.setPath(path); - pathChange = true; - } - } - - if (account.size() > 0 && jid.size() > 0 && id.size() > 0) - listChange = info.addMessage(account, jid, id); - - if (pathChange || listChange) { - writeInfo(url, info, txn, true); - txn.commit(); - } - - return info; -} - -std::list Core::UrlStorage::setPath(const QString& url, const QString& path) { - std::list list; - LMDBAL::WriteTransaction txn = base.beginTransaction(); - UrlInfo info; - - try { - urlToInfo->getRecord(url, info, txn); - info.getMessages(list); - } catch (const LMDBAL::NotFound& e) {} - - info.setPath(path); - writeInfo(url, info, txn, true); - txn.commit(); - - return list; -} - -std::list Core::UrlStorage::removeFile(const QString& url) { - std::list list; - LMDBAL::WriteTransaction txn = base.beginTransaction(); - UrlInfo info; - - urlToInfo->getRecord(url, info, txn); - urlToInfo->removeRecord(url, txn); - info.getMessages(list); - - if (info.hasPath()) - pathToUrl->removeRecord(info.getPath(), txn); - - txn.commit(); - - return list; -} - -std::list Core::UrlStorage::deletedFile(const QString& path) { - std::list list; - LMDBAL::WriteTransaction txn = base.beginTransaction(); - - QString url = pathToUrl->getRecord(path, txn); - pathToUrl->removeRecord(path, txn); - - UrlInfo info = urlToInfo->getRecord(url, txn); - info.getMessages(list); - info.setPath(QString()); - urlToInfo->changeRecord(url, info, txn); - - txn.commit(); - - return list; -} - -QString Core::UrlStorage::getUrl(const QString& path) { - return pathToUrl->getRecord(path); -} - -std::pair> Core::UrlStorage::getPath(const QString& url) { - UrlInfo info = urlToInfo->getRecord(url); - std::list container; - info.getMessages(container); - return std::make_pair(info.getPath(), container); -} - -Core::UrlStorage::UrlInfo::UrlInfo(): - localPath(), - messages() {} - -Core::UrlStorage::UrlInfo::UrlInfo(const QString& path): - localPath(path), - messages() {} - -Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list& msgs): - localPath(path), - messages(msgs) {} - -Core::UrlStorage::UrlInfo::~UrlInfo() {} - -bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) { - for (const Shared::MessageInfo& info : messages) { - if (info.account == acc && info.jid == jid && info.messageId == id) - return false; - } - messages.emplace_back(acc, jid, id); - return true; -} - -void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const { - data << localPath; - std::list::size_type size = messages.size(); - data << quint32(size); - for (const Shared::MessageInfo& info : messages) { - data << info.account; - data << info.jid; - data << info.messageId; - } -} - -QDataStream & operator << (QDataStream& in, const Core::UrlStorage::UrlInfo& info) { - info.serialize(in); - return in; -} - -QDataStream & operator >> (QDataStream& out, Core::UrlStorage::UrlInfo& info) { - info.deserialize(out); - return out; -} - -void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) { - data >> localPath; - quint32 size; - data >> size; - for (quint32 i = 0; i < size; ++i) { - messages.emplace_back(); - Shared::MessageInfo& info = messages.back(); - data >> info.account; - data >> info.jid; - data >> info.messageId; - } -} - -void Core::UrlStorage::UrlInfo::getMessages(std::list& container) const { - for (const Shared::MessageInfo& info : messages) - container.emplace_back(info); -} - -QString Core::UrlStorage::UrlInfo::getPath() const { - return localPath; -} - -bool Core::UrlStorage::UrlInfo::hasPath() const { - return localPath.size() > 0; -} - -void Core::UrlStorage::UrlInfo::setPath(const QString& path) { - localPath = path; -} diff --git a/core/conference.cpp b/core/conference.cpp index 48331e9..cda19fd 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -42,48 +42,57 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError); room->setNickName(nick); - if (autoJoin) + if (autoJoin) { room->join(); + } archive->readAllResourcesAvatars(exParticipants); } -Core::Conference::~Conference(){ - if (joined) +Core::Conference::~Conference() +{ + if (joined) { room->leave(); - + } room->deleteLater(); } -QString Core::Conference::getNick() const { +QString Core::Conference::getNick() const +{ return nick; } -bool Core::Conference::getAutoJoin() const { +bool Core::Conference::getAutoJoin() +{ return autoJoin; } -bool Core::Conference::getJoined() const { +bool Core::Conference::getJoined() const +{ return joined; } -void Core::Conference::setJoined(bool p_joined) { +void Core::Conference::setJoined(bool p_joined) +{ if (joined != p_joined) { - if (p_joined) + if (p_joined) { room->join(); - else + } else { room->leave(); + } } } -void Core::Conference::setAutoJoin(bool p_autoJoin) { +void Core::Conference::setAutoJoin(bool p_autoJoin) +{ if (autoJoin != p_autoJoin) { autoJoin = p_autoJoin; emit autoJoinChanged(autoJoin); } } -void Core::Conference::setNick(const QString& p_nick) { +void Core::Conference::setNick(const QString& p_nick) +{ if (nick != p_nick) { if (joined) { room->setNickName(p_nick); @@ -94,99 +103,105 @@ void Core::Conference::setNick(const QString& p_nick) { } } -void Core::Conference::onRoomJoined() { +void Core::Conference::onRoomJoined() +{ joined = true; emit joinedChanged(joined); } -void Core::Conference::onRoomLeft() { +void Core::Conference::onRoomLeft() +{ joined = false; emit joinedChanged(joined); } -void Core::Conference::onRoomNameChanged(const QString& p_name) { +void Core::Conference::onRoomNameChanged(const QString& p_name) +{ setName(p_name); } -void Core::Conference::onRoomNickNameChanged(const QString& p_nick) { +void Core::Conference::onRoomNickNameChanged(const QString& p_nick) +{ if (p_nick != nick) { nick = p_nick; emit nickChanged(nick); } } -void Core::Conference::onRoomError(const QXmppStanza::Error& err) { +void Core::Conference::onRoomError(const QXmppStanza::Error& err) +{ qDebug() << "MUC" << jid << "error:" << err.text(); } -void Core::Conference::onRoomParticipantAdded(const QString& p_name) { +void Core::Conference::onRoomParticipantAdded(const QString& p_name) +{ QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); QXmppMucItem mi = pres.mucItem(); - if (resource == jid) + if (resource == jid) { resource = ""; + } std::map::const_iterator itr = exParticipants.find(resource); bool hasAvatar = itr != exParticipants.end(); if (resource.size() > 0) { QDateTime lastInteraction = pres.lastUserInteraction(); - if (!lastInteraction.isValid()) + if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTimeUtc(); + } QMap cData = { {"lastActivity", lastInteraction}, {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, - {"role", mi.role()}, - {"client", QVariant::fromValue( - Shared::ClientId( - pres.capabilityNode(), - pres.capabilityVer().toBase64(), - pres.capabilityHash()) - ) - } + {"role", mi.role()} }; - careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name); + + if (hasAvatar) { + if (itr->second.autogenerated) { + cData.insert("avatarState", static_cast(Shared::Avatar::valid)); + } else { + cData.insert("avatarState", static_cast(Shared::Avatar::autocreated)); + } + cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type); + } else { + cData.insert("avatarState", static_cast(Shared::Avatar::empty)); + cData.insert("avatarPath", ""); + requestVCard(p_name); + } emit addParticipant(resource, cData); - - if (!hasAvatar) // because this way vCard is already requested, no need to handle possible avatar update - return; } - handlePossibleAvatarUpdate(pres, resource, hasAvatar, itr->second); -} - -void Core::Conference::handlePossibleAvatarUpdate ( - const QXmppPresence& pres, - const QString& resource, - bool hasAvatar, - const Archive::AvatarInfo& info -) { + switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here break; - case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any - if (!hasAvatar || !info.autogenerated) + case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any + if (!hasAvatar || !itr->second.autogenerated) { setAutoGeneratedAvatar(resource); - - break; - case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + } + } + break; + case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load if (hasAvatar) { - if (info.autogenerated || info.hash != pres.photoHash()) - emit requestVCard(pres.from()); + if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) { + emit requestVCard(p_name); + } } else { - emit requestVCard(pres.from()); + emit requestVCard(p_name); } break; + } } } -void Core::Conference::onRoomParticipantChanged(const QString& p_name) { +void Core::Conference::onRoomParticipantChanged(const QString& p_name) +{ QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); @@ -194,27 +209,22 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) { handlePresence(pres); if (resource != jid) { QDateTime lastInteraction = pres.lastUserInteraction(); - if (!lastInteraction.isValid()) + if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTimeUtc(); + } emit changeParticipant(resource, { {"lastActivity", lastInteraction}, {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, - {"role", mi.role()}, - {"client", QVariant::fromValue( - Shared::ClientId( - pres.capabilityNode(), - pres.capabilityVer().toBase64(), - pres.capabilityHash()) - ) - } + {"role", mi.role()} }); } } -void Core::Conference::onRoomParticipantRemoved(const QString& p_name) { +void Core::Conference::onRoomParticipantRemoved(const QString& p_name) +{ QStringList comps = p_name.split("/"); QString resource = comps.back(); if (resource == jid) { @@ -224,40 +234,69 @@ void Core::Conference::onRoomParticipantRemoved(const QString& p_name) { } } -QString Core::Conference::getSubject() const { - if (joined) +QString Core::Conference::getSubject() const +{ + if (joined) { return room->subject(); - else + } else { return ""; + } } -void Core::Conference::onRoomSubjectChanged(const QString& p_name) { +void Core::Conference::onRoomSubjectChanged(const QString& p_name) +{ emit subjectChanged(p_name); } -void Core::Conference::handlePresence(const QXmppPresence& pres) { +void Core::Conference::handlePresence(const QXmppPresence& pres) +{ QString id = pres.from(); QStringList comps = id.split("/"); QString jid = comps.front(); QString resource(""); - if (comps.size() > 1) + if (comps.size() > 1) { resource = comps.back(); - - Archive::AvatarInfo info; - bool hasAvatar = readAvatarInfo(info, resource); - handlePossibleAvatarUpdate(pres, resource, hasAvatar, info); + } + + switch (pres.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any + Archive::AvatarInfo info; + bool hasAvatar = readAvatarInfo(info, resource); + if (!hasAvatar || !info.autogenerated) { + setAutoGeneratedAvatar(resource); + } + } + break; + case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load + Archive::AvatarInfo info; + bool hasAvatar = readAvatarInfo(info, resource); + if (hasAvatar) { + if (info.autogenerated || info.hash != pres.photoHash()) { + emit requestVCard(id); + } + } else { + emit requestVCard(id); + } + break; + } + } } -bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { +bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) +{ Archive::AvatarInfo newInfo; bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource); if (result && resource.size() != 0) { std::map::iterator itr = exParticipants.find(resource); - if (itr == exParticipants.end()) + if (itr == exParticipants.end()) { exParticipants.insert(std::make_pair(resource, newInfo)); - else + } else { itr->second = newInfo; - + } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, {"avatarPath", avatarPath(resource) + "." + newInfo.type} @@ -267,15 +306,17 @@ bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { return result; } -bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { +bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) +{ bool result = RosterItem::setAvatar(data, info, resource); if (result && resource.size() != 0) { if (data.size() > 0) { std::map::iterator itr = exParticipants.find(resource); - if (itr == exParticipants.end()) + if (itr == exParticipants.end()) { exParticipants.insert(std::make_pair(resource, info)); - else + } else { itr->second = info; + } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, @@ -283,8 +324,9 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in }); } else { std::map::iterator itr = exParticipants.find(resource); - if (itr != exParticipants.end()) + if (itr != exParticipants.end()) { exParticipants.erase(itr); + } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::empty)}, @@ -297,31 +339,25 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in return result; } -void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { - RosterItem::handleResponseVCard(card, resource, out); - if (resource.size() > 0) +Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource) +{ + Shared::VCard result = RosterItem::handleResponseVCard(card, resource); + + if (resource.size() > 0) { emit changeParticipant(resource, { - {"avatarState", static_cast(out.getAvatarType())}, - {"avatarPath", out.getAvatarPath()} + {"avatarState", static_cast(result.getAvatarType())}, + {"avatarPath", result.getAvatarPath()} }); -} - -QMap Core::Conference::getAllAvatars() const { - QMap result; - for (const std::pair& pair : exParticipants) - result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); - + } + return result; } -QMap Core::Conference::getInfo() const { - QMap data = RosterItem::getInfo(); - - data.insert("autoJoin", getAutoJoin()); - data.insert("joined", getJoined()); - data.insert("nick", getNick()); - data.insert("avatars", getAllAvatars()); - - return data; +QMap Core::Conference::getAllAvatars() const +{ + QMap result; + for (const std::pair& pair : exParticipants) { + result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); + } + return result; } - diff --git a/core/conference.h b/core/conference.h index 501738b..4e0e463 100644 --- a/core/conference.h +++ b/core/conference.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_CONFERENCE_H +#define CORE_CONFERENCE_H #include @@ -25,11 +26,16 @@ #include #include "rosteritem.h" -#include -#include +#include "shared/global.h" -namespace Core { -class Conference : public RosterItem { +namespace Core +{ + +/** + * @todo write docs + */ +class Conference : public RosterItem +{ Q_OBJECT public: Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room); @@ -42,13 +48,12 @@ public: bool getJoined() const; void setJoined(bool p_joined); - bool getAutoJoin() const; + bool getAutoJoin(); void setAutoJoin(bool p_autoJoin); void handlePresence(const QXmppPresence & pres) override; bool setAutoGeneratedAvatar(const QString& resource = "") override; - void handleResponseVCard(const QXmppVCardIq & card, const QString &resource, Shared::VCard& out) override; + Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override; QMap getAllAvatars() const; - QMap getInfo() const override; signals: void nickChanged(const QString& nick); @@ -62,14 +67,6 @@ signals: protected: bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override; -private: - void handlePossibleAvatarUpdate( - const QXmppPresence& pres, - const QString& resource, - bool hasAvatar, - const Archive::AvatarInfo& info - ); - private: QString nick; QXmppMucRoom* room; @@ -92,3 +89,5 @@ private slots: }; } + +#endif // CORE_CONFERENCE_H diff --git a/core/contact.cpp b/core/contact.cpp index aef637b..0471a5c 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -22,49 +22,55 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent): RosterItem(pJid, account, parent), groups(), - subscriptionState(Shared::SubscriptionState::unknown), - pep(Shared::Support::unknown) + subscriptionState(Shared::SubscriptionState::unknown) +{ +} -#ifdef WITH_OMEMO - ,omemoBundles(Shared::Possible::unknown) -#endif -{} +Core::Contact::~Contact() +{ +} -Core::Contact::~Contact() {} - -QSet Core::Contact::getGroups() const { +QSet Core::Contact::getGroups() const +{ return groups; } -unsigned int Core::Contact::groupsCount() const { +unsigned int Core::Contact::groupsCount() const +{ return groups.size(); } -void Core::Contact::setGroups(const QSet& set) { +void Core::Contact::setGroups(const QSet& set) +{ QSet toRemove = groups - set; QSet toAdd = set - groups; groups = set; - for (const QString& group : toRemove) - emit groupRemoved(group); + for (QSet::iterator itr = toRemove.begin(), end = toRemove.end(); itr != end; ++itr) { + emit groupRemoved(*itr); + } - for (const QString& group : toAdd) - emit groupAdded(group); + for (QSet::iterator itr = toAdd.begin(), end = toAdd.end(); itr != end; ++itr) { + emit groupAdded(*itr); + } } -Shared::SubscriptionState Core::Contact::getSubscriptionState() const { +Shared::SubscriptionState Core::Contact::getSubscriptionState() const +{ return subscriptionState; } -void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) { +void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) +{ if (subscriptionState != state) { subscriptionState = state; emit subscriptionStateChanged(subscriptionState); } } -void Core::Contact::handlePresence(const QXmppPresence& pres) { +void Core::Contact::handlePresence(const QXmppPresence& pres) +{ switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; @@ -73,17 +79,18 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) { case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info); - if (!hasAvatar || !info.autogenerated) + if (!hasAvatar || !info.autogenerated) { setAutoGeneratedAvatar(); - + } } break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info); if (hasAvatar) { - if (info.autogenerated || info.hash != pres.photoHash()) + if (info.autogenerated || info.hash != pres.photoHash()) { emit requestVCard(jid); + } } else { emit requestVCard(jid); } @@ -91,23 +98,3 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) { } } } - -void Core::Contact::setPepSupport(Shared::Support support) { - if (pep != support) - pep = support; -} - -Shared::Support Core::Contact::getPepSupport() const { - return pep; -} - -QMap Core::Contact::getInfo() const { - QMap data = RosterItem::getInfo(); - - data.insert("state", QVariant::fromValue(subscriptionState)); - - return data; -} - - - diff --git a/core/contact.h b/core/contact.h index c23d0dc..aa81010 100644 --- a/core/contact.h +++ b/core/contact.h @@ -16,18 +16,17 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_CONTACT_H +#define CORE_CONTACT_H #include #include - #include "rosteritem.h" -#include - namespace Core { -class Contact : public RosterItem { +class Contact : public RosterItem +{ Q_OBJECT public: Contact(const QString& pJid, const QString& account, QObject* parent = 0); @@ -39,11 +38,7 @@ public: void setSubscriptionState(Shared::SubscriptionState state); Shared::SubscriptionState getSubscriptionState() const; - void setPepSupport(Shared::Support support); - Shared::Support getPepSupport() const; - void handlePresence(const QXmppPresence & pres) override; - QMap getInfo() const override; signals: void groupAdded(const QString& name); @@ -53,11 +48,7 @@ signals: private: QSet groups; Shared::SubscriptionState subscriptionState; - Shared::Support pep; - -#ifdef WITH_OMEMO -public: - Shared::Possible omemoBundles; -#endif }; } + +#endif // CORE_CONTACT_H diff --git a/core/delayManager/CMakeLists.txt b/core/delayManager/CMakeLists.txt deleted file mode 100644 index 6d046c5..0000000 --- a/core/delayManager/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -set(SOURCE_FILES - manager.cpp - job.cpp - cardinternal.cpp - infoforuser.cpp - owncardinternal.cpp - owninfoforuser.cpp - contact.cpp - info.cpp -) - -set(HEADER_FILES - manager.h - job.h - cardinternal.h - infoforuser.h - owncardinternal.h - owninfoforuser.h - contact.h - info.h -) - -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) diff --git a/core/delayManager/cardinternal.cpp b/core/delayManager/cardinternal.cpp deleted file mode 100644 index b5be472..0000000 --- a/core/delayManager/cardinternal.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "cardinternal.h" - -Core::DelayManager::CardInternal::CardInternal(Id p_id, const QString& p_jid) : - Job(p_id, Type::cardInternal), - Contact(p_id, p_jid, Type::cardInternal) -{} - - -Core::DelayManager::CardInternal::CardInternal(const CardInternal& other) : - Job(other), - Contact(other) -{} diff --git a/core/delayManager/cardinternal.h b/core/delayManager/cardinternal.h deleted file mode 100644 index 6db7734..0000000 --- a/core/delayManager/cardinternal.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include "contact.h" - -namespace Core { -namespace DelayManager { - -class CardInternal : public Contact { -public: - CardInternal(Id id, const QString& jid); - CardInternal(const CardInternal& other); -}; - -} -} diff --git a/core/delayManager/contact.cpp b/core/delayManager/contact.cpp deleted file mode 100644 index 3af1e3f..0000000 --- a/core/delayManager/contact.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "contact.h" - -Core::DelayManager::Contact::Contact(const Contact& other): - Job(other), - jid(other.jid) {} - - -Core::DelayManager::Contact::Contact(Id p_id, const QString& p_jid, Type p_type): - Job(p_id, p_type), - jid(p_jid) {} diff --git a/core/delayManager/contact.h b/core/delayManager/contact.h deleted file mode 100644 index 3f1ab98..0000000 --- a/core/delayManager/contact.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include "job.h" - -namespace Core { -namespace DelayManager { - -class Contact : public virtual Job { -protected: - Contact(Id id, const QString& jid, Type type); - Contact(const Contact& other); - -public: - const QString jid; -}; - -} -} diff --git a/core/delayManager/info.cpp b/core/delayManager/info.cpp deleted file mode 100644 index 1b8da8d..0000000 --- a/core/delayManager/info.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "info.h" - -Core::DelayManager::Info::Info(Id p_id, Type p_type) : - Job(p_id, p_type), - stage(Stage::waitingForVCard), - info(nullptr) -{} - -Core::DelayManager::Info::Info(const Info& other) : - Job(other), - stage(other.stage), - info(nullptr) -{} - -Core::DelayManager::Info::~Info() { - if (stage == Stage::waitingForBundles) { - delete info; - } -} - -Core::DelayManager::Info::Stage Core::DelayManager::Info::getStage() const { - return stage; -} - -void Core::DelayManager::Info::receivedVCard(const Shared::VCard& card) { - if (stage != Stage::waitingForVCard) - throw 245; - - info = new Shared::VCard(card); - -#ifdef WITH_OMEMO - stage = Stage::waitingForBundles; -#endif -} - -Shared::VCard * Core::DelayManager::Info::claim() { - if (stage != Stage::waitingForBundles) - throw 246; - - Shared::VCard* res = info; - info = nullptr; - return res; -} diff --git a/core/delayManager/info.h b/core/delayManager/info.h deleted file mode 100644 index f03e0c7..0000000 --- a/core/delayManager/info.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "job.h" - -#include -#include - -namespace Core { -namespace DelayManager { - -class Info : public virtual Job { -public: - enum class Stage { - waitingForVCard, - waitingForBundles, - finished - }; - -protected: - Info(Id id, Type type); - Info(const Info& other); - -public: - ~Info(); - - void receivedVCard(const Shared::VCard& card); - Shared::VCard* claim(); - Stage getStage() const; - - -private: - Stage stage; - Shared::VCard* info; -}; - -} -} diff --git a/core/delayManager/infoforuser.cpp b/core/delayManager/infoforuser.cpp deleted file mode 100644 index 7d0aed9..0000000 --- a/core/delayManager/infoforuser.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "infoforuser.h" - -Core::DelayManager::InfoForUser::InfoForUser(Id p_id, const QString& p_jid) : - Job(p_id, Type::infoForUser), - Contact(p_id, p_jid, Type::infoForUser), - Info(p_id, Type::infoForUser) -{} - -Core::DelayManager::InfoForUser::InfoForUser(const InfoForUser& other) : - Job(other), - Contact(other), - Info(other) -{} diff --git a/core/delayManager/infoforuser.h b/core/delayManager/infoforuser.h deleted file mode 100644 index c2f4692..0000000 --- a/core/delayManager/infoforuser.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "contact.h" -#include "info.h" - -namespace Core { -namespace DelayManager { - -class InfoForUser : public Contact, public Info { -public: - InfoForUser(Id id, const QString& jid); - InfoForUser(const InfoForUser& other); -}; - -} -} diff --git a/core/delayManager/job.cpp b/core/delayManager/job.cpp deleted file mode 100644 index 37acc02..0000000 --- a/core/delayManager/job.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "job.h" - -Core::DelayManager::Job::Job(Id p_id, Type p_type) : - id (p_id), - type (p_type) {} - - -Core::DelayManager::Job::Job(const Job& other) : - id(other.id), - type(other.type) {} - -Core::DelayManager::Job::~Job() {} diff --git a/core/delayManager/job.h b/core/delayManager/job.h deleted file mode 100644 index 26156c0..0000000 --- a/core/delayManager/job.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -namespace Core { -namespace DelayManager { - -class Job { -public: - typedef uint16_t Id; - - enum class Type { - cardInternal, - ownCardInternal, - infoForUser, - ownInfoForUser - }; - inline static constexpr const char * const TypeString[] = { - "cardInternal", - "ownCardInternal", - "infoForUser", - "ownInfoForUser" - }; -protected: - Job(Id id, Type type); - Job(const Job& other); - -public: - virtual ~Job(); - - const Id id; - const Type type; -}; - -} -} diff --git a/core/delayManager/manager.cpp b/core/delayManager/manager.cpp deleted file mode 100644 index efafaf5..0000000 --- a/core/delayManager/manager.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "manager.h" - -#include - -#include "cardinternal.h" -#include "infoforuser.h" -#include "owncardinternal.h" -#include "owninfoforuser.h" - -Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* parent) : - QObject(parent), - maxParallelJobs(mpj), - nextJobId(1), - scheduledJobs(), - scheduledJobsById(scheduledJobs.get()), - jobSequence(scheduledJobs.get()), - runningJobs(), - ownVCardJobId(0), - ownInfoJobId(0), - scheduledVCards(), - requestedVCards(), -#ifdef WITH_OMEMO - requestedBundles(), -#endif - ownJid(poj) -{} - -Core::DelayManager::Manager::~Manager() { - for (const std::pair& pair : runningJobs) - delete pair.second; - - for (Job* job : jobSequence) - delete job; -} - -Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() { - Job::Id id = nextJobId++; - if (id == 0) - id = nextJobId++; - - return id; -} - -void Core::DelayManager::Manager::getInfo(const QString& jid) { - if (jid == ownJid) - return getOwnInfo(); - - Job* job = nullptr; -#ifdef WITH_OMEMO - std::map::const_iterator bitr = requestedBundles.find(jid); - if (bitr != requestedBundles.end()) { - std::map::const_iterator itr = runningJobs.find(bitr->second); - if (itr == runningJobs.end()) - throw JobNotFound(bitr->second); - - job = itr->second; - } - else -#endif - job = getVCardJob(jid); - - if (job != nullptr) { - if (job->type == Job::Type::cardInternal) - replaceJob(new InfoForUser(job->id, jid)); - } else - scheduleJob(new InfoForUser(getNextJobId(), jid)); -} - -void Core::DelayManager::Manager::getOwnInfo() { - if (ownInfoJobId == 0) { - if (ownVCardJobId != 0) - replaceJob(new OwnInfoForUser(ownVCardJobId)); - else - scheduleJob(new OwnInfoForUser(getNextJobId())); - } -} - -void Core::DelayManager::Manager::getVCard(const QString& jid) { - Job* job = getVCardJob(jid); - if (job == nullptr) - scheduleJob(new CardInternal(getNextJobId(), jid)); -} - -void Core::DelayManager::Manager::getOwnVCard() { - if (ownVCardJobId == 0) - scheduleJob(new OwnCardInternal(getNextJobId())); -} - -bool Core::DelayManager::Manager::isOwnVCardPending() const { - return ownVCardJobId != 0; -} - -Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { - Job* job = nullptr; - std::map::const_iterator sitr = scheduledVCards.find(jid); - if (sitr == scheduledVCards.end()) { - std::map::const_iterator ritr = requestedVCards.find(jid); - if (ritr != requestedVCards.end()) { - std::map::const_iterator itr = runningJobs.find(ritr->second); - if (itr == runningJobs.end()) - throw JobNotFound(ritr->second, "getVCardJob:1"); - - job = itr->second; - } - } else { - StorageById::const_iterator itr = scheduledJobsById.find(sitr->second); - if (itr == scheduledJobsById.end()) - throw JobNotFound(sitr->second, "getVCardJob:2"); - - job = *itr; - } - - return job; -} -void Core::DelayManager::Manager::preScheduleJob(Job* job) { - switch (job->type) { - case Job::Type::cardInternal: - scheduledVCards.emplace(dynamic_cast(job)->jid, job->id); - break; - case Job::Type::ownCardInternal: - ownVCardJobId = job->id; - break; - case Job::Type::infoForUser: - scheduledVCards.emplace(dynamic_cast(job)->jid, job->id); - break; - case Job::Type::ownInfoForUser: - ownVCardJobId = job->id; - ownInfoJobId = job->id; - break; - } -} - -void Core::DelayManager::Manager::scheduleJob(Job* job) { - preScheduleJob(job); - if (runningJobs.size() < maxParallelJobs) - executeJob(job); - else - scheduledJobs.push_back(job); -} - -void Core::DelayManager::Manager::preExecuteJob(Job* job) { - switch (job->type) { - case Job::Type::cardInternal: - case Job::Type::infoForUser: { - Contact* cij = dynamic_cast(job); - requestedVCards.emplace(cij->jid, job->id); - scheduledVCards.erase(cij->jid); - } - break; - case Job::Type::ownInfoForUser: - case Job::Type::ownCardInternal: - break; - } -} - -void Core::DelayManager::Manager::executeJob(Job* job) { - preExecuteJob(job); - runningJobs.emplace(job->id, job); - switch (job->type) { - case Job::Type::cardInternal: - case Job::Type::infoForUser: - emit requestVCard(dynamic_cast(job)->jid); - break; - case Job::Type::ownInfoForUser: - case Job::Type::ownCardInternal: - emit requestOwnVCard(); - break; - } -} - -void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) { - std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) - throw JobNotFound(jobId, "jobIsDone"); - - Job* job = itr->second; - delete job; - runningJobs.erase(itr); - if (scheduledJobs.size() > 0) { - Job* job = scheduledJobs.front(); - scheduledJobs.pop_front(); - executeJob(job); - } -} - -void Core::DelayManager::Manager::replaceJob(Job* job) { - preScheduleJob(job); - std::map::iterator itr = runningJobs.find(job->id); - if (itr != runningJobs.end()) { - preExecuteJob(job); - delete itr->second; - itr->second = job; - } else { - StorageById::iterator sitr = scheduledJobsById.find(job->id); - if (sitr != scheduledJobsById.end()) { - delete *(sitr); - scheduledJobsById.replace(sitr, job); - } else - throw JobNotFound(job->id, "replaceJob"); - } -} - -void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { - switch (job->type) { - case Job::Type::cardInternal: { - CardInternal* jb = dynamic_cast(job); - if (wasRunning) - requestedVCards.erase(jb->jid); - else - scheduledVCards.erase(jb->jid); - - emit gotVCard(jb->jid, Shared::VCard()); - } - break; - case Job::Type::infoForUser: { - InfoForUser* jb = dynamic_cast(job); - switch (jb->getStage()) { - case InfoForUser::Stage::waitingForVCard: - if (wasRunning) - requestedVCards.erase(jb->jid); - else - scheduledVCards.erase(jb->jid); - - emit gotVCard(jb->jid, Shared::VCard()); - break; - case InfoForUser::Stage::waitingForBundles: -#ifdef WITH_OMEMO - requestedBundles.erase(jb->jid); -#endif - break; - default: - break; - } - emit gotInfo(Shared::Info(jb->jid)); - } - break; - case Job::Type::ownInfoForUser: { - OwnInfoForUser* jb = dynamic_cast(job); - if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) { - ownVCardJobId = 0; - emit gotOwnVCard(Shared::VCard()); - } - ownInfoJobId = 0; - emit gotOwnInfo(Shared::Info (ownJid)); - } - - break; - case Job::Type::ownCardInternal: - ownVCardJobId = 0; - emit gotOwnVCard(Shared::VCard()); - break; - } - - delete job; -} - -void Core::DelayManager::Manager::disconnected() { - for (const std::pair pair : runningJobs) - jobIsCanceled(pair.second, true); - - for (Job* job : scheduledJobs) - jobIsCanceled(job, false); - - runningJobs.clear(); - scheduledJobs.clear(); -} - -void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) { - std::map::const_iterator cardItr = requestedVCards.find(jid); - if (cardItr == requestedVCards.end()) { - qDebug() << "received VCard for" << jid << "but it was never requested through manager, ignoring"; - return; - } - Job::Id jobId = cardItr->second; - requestedVCards.erase(cardItr); - std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) - throw JobNotFound(jobId, "receivedVCard"); - - Job* job = itr->second; - - switch (job->type) { - case Job::Type::cardInternal: - jobIsDone(jobId); - emit gotVCard(jid, card); - break; - case Job::Type::infoForUser: { -#ifdef WITH_OMEMO - requestedBundles.emplace(jid, jobId); - InfoForUser* jb = dynamic_cast(job); - jb->receivedVCard(card); - emit requestBundles(jid); -#else - Shared::Info info(jid); - info.turnIntoContact(card); - emit gotInfo(info); - jobIsDone(jobId); -#endif - emit gotVCard(jid, card); - } - break; - default: - throw UnexpectedJobType(job->type, "receivedVCard"); - } -} - -void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { - if (ownVCardJobId == 0) { - qDebug() << "received own VCard for" << ownJid << "but it was never requested through manager, ignoring"; - return; - } - Job::Id jobId = ownVCardJobId; - ownVCardJobId = 0; - std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) - throw JobNotFound(jobId, "receivedOwnVCard"); - - Job* job = itr->second; - switch (job->type) { - case Job::Type::ownCardInternal: - jobIsDone(jobId); - emit gotOwnVCard(card); - break; - case Job::Type::ownInfoForUser: { -#ifdef WITH_OMEMO - OwnInfoForUser* jb = dynamic_cast(job); - jb->receivedVCard(card); - emit requestOwnBundles(); -#else - Shared::Info info(ownJid); - info.turnIntoOwnAccount(card); - emit gotOwnInfo(info); - jobIsDone(jobId); -#endif - emit gotOwnVCard(card); - } - break; - default: - throw UnexpectedJobType(job->type, "receivedVCard"); - } -} - -#ifdef WITH_OMEMO -void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list& keys) { - std::map::const_iterator itr = requestedBundles.find(jid); - if (itr == requestedBundles.end()) { - qDebug() << "received bundles for" << jid << "but they were never requested through manager, ignoring"; - return; - } - - Job::Id jobId = itr->second; - requestedBundles.erase(itr); - std::map::const_iterator jitr = runningJobs.find(jobId); - if (jitr == runningJobs.end()) - throw JobNotFound(jobId, "receivedBundles"); - - Job* jb = jitr->second; - InfoForUser* job = dynamic_cast(jb); - - Shared::Info info(jid); - info.turnIntoContact(job->claim(), new std::list(keys)); - emit gotInfo(info); - jobIsDone(jobId); -} - -void Core::DelayManager::Manager::receivedOwnBundles(const std::list& keys) { - if (ownInfoJobId == 0) { - qDebug() << "received own bundles for" << ownJid << "but they were never requested through manager, ignoring"; - return; - } - Job::Id jobId = ownInfoJobId; - ownInfoJobId = 0; - std::map::const_iterator jitr = runningJobs.find(jobId); - if (jitr == runningJobs.end()) - throw JobNotFound(jobId, "receivedOwnBundles"); - - Job* jb = jitr->second; - OwnInfoForUser* job = dynamic_cast(jb); - - Shared::Info info(ownJid); - info.turnIntoOwnAccount(job->claim(), new std::list(keys)); - emit gotOwnInfo(info); - jobIsDone(jobId); -} -#endif - -void Core::DelayManager::Manager::setOwnJid(const QString& jid) { - ownJid = jid; -} - - -Core::DelayManager::Manager::UnexpectedJobType::UnexpectedJobType(Job::Type p_type, const std::string& p_method): - Exception(), - type(p_type), - method(p_method) -{} - -std::string Core::DelayManager::Manager::UnexpectedJobType::getMessage() const{ - std::string msg("Unexpected job type: "); - msg += Job::TypeString[static_cast(type)]; - if (method.size() > 0) - msg += " in method " + method; - - return msg; -} - -Core::DelayManager::Manager::JobNotFound::JobNotFound(Job::Id p_id, const std::string& p_method) : - Exception(), - id(p_id), - method(p_method) -{} - -std::string Core::DelayManager::Manager::JobNotFound::getMessage() const { - std::string msg("Job with id "); - msg += std::to_string(id); - msg += " was not found"; - if (method.size() > 0) - msg += " in method " + method; - - return msg; -} diff --git a/core/delayManager/manager.h b/core/delayManager/manager.h deleted file mode 100644 index 8b32139..0000000 --- a/core/delayManager/manager.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include "job.h" - -namespace Core { -namespace DelayManager { - -class Manager : public QObject { - Q_OBJECT -public: - Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr); - ~Manager(); - - void setOwnJid(const QString& jid); - bool isOwnVCardPending() const; - -public slots: - void getOwnVCard(); - void getOwnInfo(); - void getVCard(const QString& jid); - void getInfo(const QString& jid); - -signals: - void requestVCard(const QString& jid); - void requestOwnVCard(); - -#ifdef WITH_OMEMO - void requestBundles(const QString& jid); - void requestOwnBundles(); -#endif - - void gotVCard(const QString& jid, const Shared::VCard& info); - void gotOwnVCard(const Shared::VCard& info); - void gotInfo(const Shared::Info& info); - void gotOwnInfo(const Shared::Info& info); - -public slots: - void disconnected(); - void receivedOwnVCard(const Shared::VCard& card); - void receivedVCard(const QString& jid, const Shared::VCard& card); - -#ifdef WITH_OMEMO - void receivedBundles(const QString& jid, const std::list& keys); - void receivedOwnBundles(const std::list& keys); -#endif - -private: - void preScheduleJob(Job* job); - void scheduleJob(Job* job); - void preExecuteJob(Job* job); - void executeJob(Job* job); - void jobIsCanceled(Job* job, bool wasRunning); - void jobIsDone(Job::Id jobId); - Job::Id getNextJobId(); - void replaceJob(Job* job); - Job* getVCardJob(const QString& jid); - -private: - struct id {}; - struct sequence {}; - - typedef boost::multi_index_container< - Job*, - boost::multi_index::indexed_by< - boost::multi_index::sequenced< - boost::multi_index::tag - >, - boost::multi_index::ordered_unique< - boost::multi_index::tag, - boost::multi_index::member< - Job, - const Job::Id, - &Job::id - > - > - > - > Storage; - - - typedef Storage::index::type StorageById; - typedef Storage::index::type StorageSequence; - Job::Id maxParallelJobs; - Job::Id nextJobId; - - Storage scheduledJobs; - StorageById& scheduledJobsById; - StorageSequence& jobSequence; - std::map runningJobs; - - Job::Id ownVCardJobId; - Job::Id ownInfoJobId; - std::map scheduledVCards; - std::map requestedVCards; -#ifdef WITH_OMEMO - std::map requestedBundles; -#endif - QString ownJid; - -public: - class UnexpectedJobType: public Utils::Exception { - public: - UnexpectedJobType(Job::Type p_type, const std::string& p_method = ""); - std::string getMessage() const override; - private: - Job::Type type; - std::string method; - }; - - class JobNotFound: public Utils::Exception { - public: - JobNotFound(Job::Id p_id, const std::string& p_method = ""); - std::string getMessage() const override; - private: - Job::Id id; - std::string method; - }; -}; - -} -} diff --git a/core/delayManager/owncardinternal.cpp b/core/delayManager/owncardinternal.cpp deleted file mode 100644 index d9729ba..0000000 --- a/core/delayManager/owncardinternal.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "owncardinternal.h" - -Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id) : - Job(p_id, Type::ownCardInternal) -{} - -Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id, Type p_type) : - Job(p_id, p_type) -{} - -Core::DelayManager::OwnCardInternal::OwnCardInternal(const OwnCardInternal& other) : - Job(other) -{} - diff --git a/core/delayManager/owninfoforuser.cpp b/core/delayManager/owninfoforuser.cpp deleted file mode 100644 index 98cbdfa..0000000 --- a/core/delayManager/owninfoforuser.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "owninfoforuser.h" - -Core::DelayManager::OwnInfoForUser::OwnInfoForUser(Id p_id) : - Job(p_id, Type::ownInfoForUser), - Info(p_id, Type::ownInfoForUser) -{} - -Core::DelayManager::OwnInfoForUser::OwnInfoForUser(const OwnInfoForUser& other) : - Job(other), - Info(other) -{} diff --git a/core/delayManager/owninfoforuser.h b/core/delayManager/owninfoforuser.h deleted file mode 100644 index 4348b04..0000000 --- a/core/delayManager/owninfoforuser.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "contact.h" -#include "info.h" - -namespace Core { -namespace DelayManager { - -class OwnInfoForUser : public Info { -public: - OwnInfoForUser(Id id); - OwnInfoForUser(const OwnInfoForUser& other); -}; - -} -} diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index ed359bd..6da2ef3 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -1,22 +1,6 @@ -set(SOURCE_FILES +target_sources(squawk PRIVATE messagehandler.cpp - rosterhandler.cpp - vcardhandler.cpp - discoveryhandler.cpp - trusthandler.cpp -) - -set(HEADER_FILES messagehandler.h + rosterhandler.cpp rosterhandler.h - vcardhandler.h - discoveryhandler.h - trusthandler.h -) - -if(WITH_OMEMO) - list(APPEND SOURCE_FILES omemohandler.cpp) - list(APPEND HEADER_FILES omemohandler.h) -endif() - -target_sources(squawk PRIVATE ${SOURCE_FILES}) + ) diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp deleted file mode 100644 index 6ed06c6..0000000 --- a/core/handlers/discoveryhandler.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "discoveryhandler.h" -#include "core/account.h" - -#include - -Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): - QObject(), - acc(account), - omemoToCarbonsConnected (false) {} - -Core::DiscoveryHandler::~DiscoveryHandler() {} - -void Core::DiscoveryHandler::initialize() -{ - QObject::connect(acc->dm, &QXmppDiscoveryManager::itemsReceived, this, &DiscoveryHandler::onItemsReceived); - QObject::connect(acc->dm, &QXmppDiscoveryManager::infoReceived, this, &DiscoveryHandler::onInfoReceived); - - acc->dm->setClientType("pc"); - acc->dm->setClientCategory("client"); - acc->dm->setClientName(qApp->applicationDisplayName() + " " + qApp->applicationVersion()); - acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk"); -} - -void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items) -{ - QString server = acc->getServer(); - if (items.from() == server) { - std::set needToRequest; - qDebug() << "Server items list received for account " << acc->getName() << ":"; - for (QXmppDiscoveryIq::Item item : items.items()) { - QString jid = item.jid(); - if (jid != server) { - qDebug() << " Node" << jid; - needToRequest.insert(jid); - } else { - qDebug() << " " << item.node().toStdString().c_str(); - } - } - - for (const QString& jid : needToRequest) { - acc->dm->requestInfo(jid); - } - } -} - -void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) -{ - QString from = info.from(); - QString server = acc->getServer(); - QString accName = acc->getName(); - QString bareJid = acc->getBareJid(); - if (from == server) { - bool enableCC = false; - qDebug() << "Server info received for account" << accName; - QStringList features = info.features(); - qDebug() << "List of supported features of the server " << server << ":"; - for (const QString& feature : features) { - qDebug() << " " << feature.toStdString().c_str(); - if (feature == "urn:xmpp:carbons:2") { - enableCC = true; - } - } - - if (enableCC) { - qDebug() << "Enabling carbon copies for account" << accName; -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) - acc->cm->setCarbonsEnabled(true); -#endif -#ifdef WITH_OMEMO - if (!omemoToCarbonsConnected && acc->oh->hasOwnDevice()) { - // connect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage); - // connect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage); - omemoToCarbonsConnected = true; - } - } else { - if (omemoToCarbonsConnected) { - // disconnect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage); - // disconnect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage); - omemoToCarbonsConnected = false; - } -#endif - } - - qDebug() << "Requesting account" << accName << "capabilities"; - acc->dm->requestInfo(bareJid); - } else if (from == bareJid) { - qDebug() << "Received capabilities for account" << accName << ":"; - QList identities = info.identities(); - bool pepSupported = false; - for (const QXmppDiscoveryIq::Identity& identity : identities) { - QString type = identity.type(); - QString category = identity.category(); - qDebug() << " " << category << type; - if (type == "pep" && category == "pubsub") { - pepSupported = true; - } - } - acc->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); - } else { - QString node = info.queryNode(); - if (!node.isEmpty()) { - qDebug() << "Received features and identities for account" << accName << "about" << from; - QStringList feats = info.features(); - std::set identities; - std::set features(feats.begin(), feats.end()); - QList idents = info.identities(); - for (const QXmppDiscoveryIq::Identity& ident : idents) { - Shared::Identity identity; - identity.category = ident.category(); - identity.language = ident.language(); - identity.name = ident.name(); - identity.type = ident.type(); - identities.insert(identity); - - qDebug() << " " << identity.name << identity.category << identity.type; - } - for (const QString& feat : features) { - qDebug() << " " << feat; - } - emit acc->infoDiscovered(from, node, identities, features); - } else { - Contact* cont = acc->rh->getContact(from); - if (cont != nullptr) { - qDebug() << "Received info for account" << accName << "about contact" << from; - QList identities = info.identities(); - bool pepSupported = false; - for (const QXmppDiscoveryIq::Identity& identity : identities) { - QString type = identity.type(); - QString category = identity.category(); - qDebug() << " " << category << type; - if (type == "pep" && category == "pubsub") { - pepSupported = true; - } - } - cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); - } - } - } -} diff --git a/core/handlers/discoveryhandler.h b/core/handlers/discoveryhandler.h deleted file mode 100644 index 4bf5baf..0000000 --- a/core/handlers/discoveryhandler.h +++ /dev/null @@ -1,48 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef CORE_DISCOVERYHANDLER_H -#define CORE_DISCOVERYHANDLER_H - -#include - -#include -#include - -namespace Core { -class Account; - -class DiscoveryHandler : public QObject -{ - Q_OBJECT -public: - DiscoveryHandler(Account* account); - ~DiscoveryHandler(); - - void initialize(); - -private slots: - void onItemsReceived (const QXmppDiscoveryIq& items); - void onInfoReceived (const QXmppDiscoveryIq& info); - -private: - Account* acc; - bool omemoToCarbonsConnected; -}; - -} - -#endif // CORE_DISCOVERYHANDLER_H diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 34f7fd1..33b3458 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -19,218 +19,186 @@ #include "messagehandler.h" #include "core/account.h" -static const QMap statePending({{"state", static_cast(Shared::Message::State::pending)}}); -static const QMap stateDelivered({{"state", static_cast(Shared::Message::State::delivered)}}); -static const QMap stateSent({{"state", static_cast(Shared::Message::State::sent)}}); - Core::MessageHandler::MessageHandler(Core::Account* account): QObject(), acc(account), pendingStateMessages(), uploadingSlotsQueue() -{} +{ +} -void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) -#ifdef WITH_OMEMO - switch (msg.encryptionMethod()) { - case QXmpp::NoEncryption: - break; //just do nothing - case QXmpp::UnknownEncryption: - qDebug() << "Account" << acc->getName() << "received a message with unknown encryption type"; - break; //let it go the way it is, there is nothing I can do here - case QXmpp::Otr: - qDebug() << "Account" << acc->getName() << "received an OTR encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet - case QXmpp::LegacyOpenPgp: - qDebug() << "Account" << acc->getName() << "received an LegacyOpenPgp encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet - case QXmpp::Ox: - qDebug() << "Account" << acc->getName() << "received an Ox encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet - case QXmpp::Omemo0: - qDebug() << "Account" << acc->getName() << "received an Omemo0 encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet - case QXmpp::Omemo1: - qDebug() << "Account" << acc->getName() << "received an Omemo1 encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet - case QXmpp::Omemo2: - break; - } -#endif -#endif +void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) +{ bool handled = false; switch (msg.type()) { case QXmppMessage::Normal: qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; break; case QXmppMessage::Chat: -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - handled = handleChatMessage(msg, false, msg.isCarbonForwarded(), true); -#else handled = handleChatMessage(msg); -#endif break; case QXmppMessage::GroupChat: handled = handleGroupMessage(msg); break; - case QXmppMessage::Error: - handled = handlePendingMessageError(msg.id(), msg.error().text()); - if (!handled) + case QXmppMessage::Error: { + QString id = msg.id(); + std::map::const_iterator itr = pendingStateMessages.find(id); + if (itr != pendingStateMessages.end()) { + QString jid = itr->second; + RosterItem* cnt = acc->rh->getRosterItem(jid); + QMap cData = { + {"state", static_cast(Shared::Message::State::error)}, + {"errorText", msg.error().text()} + }; + if (cnt != 0) { + cnt->changeMessage(id, cData); + } + ; + emit acc->changeMessage(jid, id, cData); + pendingStateMessages.erase(itr); + handled = true; + } else { qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; - - break; + } + } + break; case QXmppMessage::Headline: qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; break; } - if (!handled) + if (!handled) { logMessage(msg); + } } -bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) { - return adjustPendingMessage(id, { - {"state", static_cast(Shared::Message::State::error)}, - {"errorText", errorText} - }, true); -} - -bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { - if (msg.body().isEmpty() && msg.outOfBandUrl().isEmpty()) - return false; - - Shared::Message sMsg(Shared::Message::chat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - Contact* cnt = acc->rh->getContact(jid); - if (cnt == 0) { - cnt = acc->rh->addOutOfRosterContact(jid); - qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; - } - if (sMsg.getOutgoing()) { - if (sMsg.getForwarded()) - sMsg.setState(Shared::Message::State::sent); - } else { - sMsg.setState(Shared::Message::State::delivered); - } - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit acc->changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - emit acc->message(sMsg); - } - - return true; -} - -bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { - const QString& body(msg.body()); - if (body.isEmpty()) - return false; - - Shared::Message sMsg(Shared::Message::groupChat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - Conference* cnt = acc->rh->getConference(jid); - if (cnt == 0) - return false; - - bool result = adjustPendingMessage(msg.id(), stateDelivered, true); - if (result) //then it was an echo of my own sent message, nothing else needs to be done - return result; - - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit acc->changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); - if (sMsg.getTime() > minAgo) //otherwise it's considered a delayed delivery, most probably MUC history initial fetch +bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) +{ + if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { + Shared::Message sMsg(Shared::Message::chat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + Contact* cnt = acc->rh->getContact(jid); + if (cnt == 0) { + cnt = acc->rh->addOutOfRosterContact(jid); + } + if (outgoing) { + if (forwarded) { + sMsg.setState(Shared::Message::State::sent); + } + } else { + sMsg.setState(Shared::Message::State::delivered); + } + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); emit acc->message(sMsg); + } + + return true; } - - return true; + return false; } -void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { - initializeIDs(target, source); +bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) +{ + const QString& body(msg.body()); + if (body.size() != 0) { + QString id = msg.id(); + + Shared::Message sMsg(Shared::Message::groupChat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + Conference* cnt = acc->rh->getConference(jid); + if (cnt == 0) { + return false; + } + + std::map::const_iterator pItr = pendingStateMessages.find(id); + if (pItr != pendingStateMessages.end()) { + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + cnt->changeMessage(id, cData); + pendingStateMessages.erase(pItr); + emit acc->changeMessage(jid, id, cData); + } else { + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); + QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); + if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt + emit acc->message(sMsg); + } else { + //qDebug() << "Delayed delivery: "; + } + } + } + + return true; + } + return false; +} + + +void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const +{ + const QDateTime& time(source.stamp()); + QString id; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + id = source.originId(); + if (id.size() == 0) { + id = source.id(); + } + target.setStanzaId(source.stanzaId()); +#else + id = source.id(); +#endif + target.setId(id); QString messageId = target.getId(); if (messageId.size() == 0) { target.generateRandomId(); //TODO out of desperation, I need at least a random ID messageId = target.getId(); - qDebug() << "Had do initialize a message with no id, assigning autogenerated" << messageId; } target.setFrom(source.from()); target.setTo(source.to()); target.setBody(source.body()); target.setForwarded(forwarded); -#ifdef WITH_OMEMO - #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - if (source.encryptionMethod() == QXmpp::EncryptionMethod::Omemo2) - target.setEncryption(Shared::EncryptionProtocol::omemo2); - #endif -#endif - if (guessing) - outgoing = target.getFromJid() == acc->getBareJid(); - - const QDateTime& time(source.stamp()); + if (guessing) { + if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) { + outgoing = true; + } else { + outgoing = false; + } + } target.setOutgoing(outgoing); - if (time.isValid()) + if (time.isValid()) { target.setTime(time); - else + } else { target.setCurrentTime(); - + } + QString oob = source.outOfBandUrl(); - if (oob.size() > 0) + if (oob.size() > 0) { target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId)); - + } target.setOutOfBandUrl(oob); } -void Core::MessageHandler::initializeIDs(Shared::Message& target, const QXmppMessage& source) const { - QString id; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - id = source.originId(); - if (id.size() == 0) - id = source.id(); - - QString stanzaID; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - // here I'm looking preferably for id generated by myself, but if there isn't - any is better than nothing - QVector sIDs = source.stanzaIds(); - for (const QXmppStanzaId& sID : sIDs) { - bool match = sID.by == acc->getBareJid(); - if (stanzaID.isEmpty() || match) - stanzaID = sID.id; - - if (match) - break; - } -#else - stanzaID = source.stanzaId(); -#endif - target.setStanzaId(stanzaID); - qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stanzaId:" << stanzaID; -#else - id = source.id(); -#endif - target.setId(id); -} - - -void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) { +void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) +{ qDebug() << reason; qDebug() << "- from: " << msg.from(); qDebug() << "- to: " << msg.to(); @@ -239,395 +207,279 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re qDebug() << "- state: " << msg.state(); qDebug() << "- stamp: " << msg.stamp(); qDebug() << "- id: " << msg.id(); +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + qDebug() << "- stanzaId: " << msg.stanzaId(); +#endif qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); qDebug() << "=============================="; } -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) -void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) { +void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) +{ handleChatMessage(msg, false, true); } -void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { +void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) +{ handleChatMessage(msg, true, true); } -#endif -std::optional Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { +void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) +{ std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { - Shared::MessageInfo info(acc->name, itr->second, itr->first); - std::map::const_iterator itrC = pendingCorrectionMessages.find(id); - if (itrC != pendingCorrectionMessages.end()) { - if (itrC->second.size() > 0) - info.jid = itrC->second; - - if (clear) - pendingCorrectionMessages.erase(itrC); + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + RosterItem* ri = acc->rh->getRosterItem(itr->second); + if (ri != 0) { + ri->changeMessage(id, cData); } - - if (clear) - pendingStateMessages.erase(itr); - - return info; + emit acc->changeMessage(itr->second, id, cData); + pendingStateMessages.erase(itr); } - - return std::nullopt; } -void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) { - SHARED_UNUSED(jid); - adjustPendingMessage(id, {{"state", static_cast(Shared::Message::State::delivered)}}, true); -} - -void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) { +void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage) +{ if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) { - pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId)); prepareUpload(data, newMessage); } else { - performSending(data, originalId, newMessage); + performSending(data, newMessage); } } -void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) { +void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) +{ QString jid = data.getPenPalJid(); QString id = data.getId(); - qDebug() << "Sending message with id:" << id; - if (originalId.size() > 0) - qDebug() << "To replace the one with id:" << originalId; - - RosterItem* ri = acc->rh->getRosterItem(jid); - if (newMessage && originalId.size() > 0) - newMessage = false; - - QDateTime sendTime = QDateTime::currentDateTimeUtc(); - std::pair result = scheduleSending(data, sendTime, originalId); - data.setState(result.first); - data.setErrorText(result.second); - - QMap changes(getChanges(data, sendTime, newMessage, originalId)); - if (ri != nullptr) { - if (newMessage) - ri->appendMessageToArchive(data); - else - ri->changeMessage(originalId.isEmpty() ? id : originalId, changes); - - if (data.getState() != Shared::Message::State::error) { - pendingStateMessages.insert(std::make_pair(id, jid)); - if (originalId.size() > 0) - pendingCorrectionMessages.insert(std::make_pair(id, originalId)); - } else { - pendingStateMessages.erase(id); - pendingCorrectionMessages.erase(id); - } - } - - emit acc->changeMessage(jid, originalId.isEmpty() ? id : originalId, changes); -} - -std::pair Core::MessageHandler::scheduleSending( - const Shared::Message& message, - const QDateTime& sendTime, - const QString& originalId -) { - if (acc->state != Shared::ConnectionState::connected) - return {Shared::Message::State::error, "You are is offline or reconnecting"}; - - QXmppMessage msg = createPacket(message, sendTime, originalId); - QString id = msg.id(); -#ifdef WITH_OMEMO - if (message.getEncryption() == Shared::EncryptionProtocol::omemo2) { - QXmppTask task = acc->om->encryptMessage(std::move(msg), std::nullopt); - if (task.isFinished()) { - const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); - if (std::holds_alternative>(res)) { - qDebug() << "Successfully encrypted a message"; - const std::unique_ptr& encrypted = std::get>(res); - encrypted->setBody(QString()); - encrypted->setOutOfBandUrl(QString()); - bool success = acc->client.sendPacket(*encrypted.get()); - if (success) { - qDebug() << "Successfully sent an encrypted message"; - return {Shared::Message::State::sent, ""}; - } else { - qDebug() << "Couldn't sent an encrypted message"; - return {Shared::Message::State::error, "Error sending successfully encrypted message"}; - } - } else if (std::holds_alternative(res)) { - qDebug() << "Couldn't encrypt a message"; - const QXmppError& err = std::get(res); - return {Shared::Message::State::error, err.description}; - } else { - qDebug() << "Couldn't encrypt a message"; - return {Shared::Message::State::error, "Unexpected error ecryptng the message"}; - } - } else { - task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) { - if (std::holds_alternative>(result)) { - qDebug() << "Successfully encrypted a message"; - const std::unique_ptr& encrypted = std::get>(result); - encrypted->setBody(QString()); - encrypted->setOutOfBandUrl(QString()); - bool success = acc->client.sendPacket(*encrypted.get()); - if (success) { - qDebug() << "Successfully sent an encrypted message"; - if (!adjustPendingMessage(id, stateSent, false)) - qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate"; - } else { - qDebug() << "Couldn't sent an encrypted message"; - handlePendingMessageError(id, "Error sending successfully encrypted message"); - } - } else if (std::holds_alternative(result)) { - qDebug() << "Couldn't encrypt a message"; - const QXmppError& err = std::get(result); - handlePendingMessageError(id, err.description); - } else { - qDebug() << "Couldn't encrypt a message"; - handlePendingMessageError(id, "Unexpected error ecryptng the message"); - } - }); - return {Shared::Message::State::pending, ""}; - } - } else -#endif - { - bool success = acc->client.sendPacket(msg); - if (success) - return {Shared::Message::State::sent, ""}; - else - return {Shared::Message::State::error, "Error sending message, internal QXMPP error"}; - } -} - -bool Core::MessageHandler::adjustPendingMessage(const QString& messageId, const QMap& data, bool final) { - std::optional info = getOriginalPendingMessageId(messageId, final); - if (info) { - RosterItem* ri = acc->rh->getRosterItem(info->jid); - if (ri != nullptr) - ri->changeMessage(info->messageId, data); - - emit acc->changeMessage(info->jid, info->messageId, data); - return true; - } - - return false; -} - -QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { - QMap changes; - QString oob = data.getOutOfBandUrl(); + RosterItem* ri = acc->rh->getRosterItem(jid); + bool sent = false; + QMap changes; + QDateTime sendTime = QDateTime::currentDateTimeUtc(); + if (acc->state == Shared::ConnectionState::connected) { + QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); + +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + msg.setOriginId(id); +#endif + msg.setId(id); + msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible + msg.setOutOfBandUrl(oob); + msg.setReceiptRequested(true); + msg.setStamp(sendTime); + + sent = acc->client.sendPacket(msg); + //sent = false; + + if (sent) { + data.setState(Shared::Message::State::sent); + } else { + data.setState(Shared::Message::State::error); + data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs"); + } + + } else { + data.setState(Shared::Message::State::error); + data.setErrorText("You are is offline or reconnecting"); + } + Shared::Message::State mstate = data.getState(); changes.insert("state", static_cast(mstate)); - if (mstate == Shared::Message::State::error) + if (mstate == Shared::Message::State::error) { changes.insert("errorText", data.getErrorText()); - - if (oob.size() > 0) - changes.insert("outOfBandUrl", oob); - - if (newMessage) - data.setTime(time); - - if (originalId.size() > 0) - changes.insert("body", data.getBody()); - - changes.insert("stamp", time); - - //sometimes (when the image is pasted with ctrl+v) - //I start sending message with one path, then copy it to downloads directory - //so, the final path changes. Let's assume it changes always since it costs me close to nothing - QString attachPath = data.getAttachPath(); - if (attachPath.size() > 0) { - QString squawkified = Shared::squawkifyPath(attachPath); - changes.insert("attachPath", squawkified); - if (attachPath != squawkified) - data.setAttachPath(squawkified); } - - return changes; + if (oob.size() > 0) { + changes.insert("outOfBandUrl", oob); + } + if (newMessage) { + data.setTime(sendTime); + } + changes.insert("stamp", sendTime); + + if (ri != 0) { + if (newMessage) { + ri->appendMessageToArchive(data); + } else { + ri->changeMessage(id, changes); + } + if (sent) { + pendingStateMessages.insert(std::make_pair(id, jid)); + } else { + pendingStateMessages.erase(id); + } + } + + emit acc->changeMessage(jid, id, changes); } -QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const { - QXmppMessage msg(QString(), data.getTo(), data.getBody(), data.getThread()); - QString id(data.getId()); - - if (originalId.size() > 0) - msg.setReplaceId(originalId); - - -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - msg.setOriginId(id); -#endif - msg.setId(id); - msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible - msg.setOutOfBandUrl(data.getOutOfBandUrl()); - msg.setReceiptRequested(true); - msg.setStamp(time); - - return msg; -} - -void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) { - if (acc->state != Shared::ConnectionState::connected) { +void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) +{ + if (acc->state == Shared::ConnectionState::connected) { + QString jid = data.getPenPalJid(); + QString id = data.getId(); + RosterItem* ri = acc->rh->getRosterItem(jid); + if (!ri) { + qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; + return; + } + QString path = data.getAttachPath(); + QString url = acc->network->getFileRemoteUrl(path); + if (url.size() != 0) { + sendMessageWithLocalUploadedFile(data, url, newMessage); + } else { + pendingStateMessages.insert(std::make_pair(id, jid)); + if (newMessage) { + ri->appendMessageToArchive(data); + } else { + QMap changes({ + {"state", (uint)Shared::Message::State::pending} + }); + ri->changeMessage(id, changes); + emit acc->changeMessage(jid, id, changes); + } + //this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file + if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) { + if (acc->um->serviceFound()) { + QFileInfo file(path); + if (file.exists() && file.isReadable()) { + pendingStateMessages.insert(std::make_pair(id, jid)); + uploadingSlotsQueue.emplace_back(path, id); + if (uploadingSlotsQueue.size() == 1) { + acc->um->requestUploadSlot(file); + } + } else { + handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; + } + } else { + handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; + } + } + } + } else { handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting"); qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping"; - return; - } - - QString jid = data.getPenPalJid(); - QString id = data.getId(); - RosterItem* ri = acc->rh->getRosterItem(jid); - if (ri == nullptr) { - qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; - return; - } - - QString path = data.getAttachPath(); - QString url = acc->network->getFileRemoteUrl(path); - if (url.size() != 0) - return sendMessageWithLocalUploadedFile(data, url, newMessage); - - pendingStateMessages.insert(std::make_pair(id, jid)); - if (newMessage) { - ri->appendMessageToArchive(data); - } else { - ri->changeMessage(id, statePending); - emit acc->changeMessage(jid, id, statePending); - } - - //this checks if the file is already uploading, and if so it subscribes to it's success, - //So, I need to do stuff only if the network knows nothing of this file - if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) - return; - - if (!acc->um->serviceFound()) { - handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account"); - qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; - return; - } - - QFileInfo file(path); - if (file.exists() && file.isReadable()) { - pendingStateMessages.insert(std::make_pair(id, jid)); - uploadingSlotsQueue.emplace_back(file, id); - if (uploadingSlotsQueue.size() == 1) - acc->um->requestUploadSlot(file); - } else { - handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it"); - qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; } } -void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) { +void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) +{ if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); const QString& mId = pair.second; QString palJid = pendingStateMessages.at(mId); - acc->network->uploadFile({acc->name, palJid, mId}, pair.first.path(), slot.putUrl(), slot.getUrl(), slot.putHeaders()); + acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); uploadingSlotsQueue.pop_front(); - if (uploadingSlotsQueue.size() > 0) + if (uploadingSlotsQueue.size() > 0) { acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); + } } } -void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) { +void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) +{ QString err(request.error().text()); if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; qDebug() << err; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err; handleUploadError(pendingStateMessages.at(pair.second), pair.second, err); uploadingSlotsQueue.pop_front(); - if (uploadingSlotsQueue.size() > 0) + if (uploadingSlotsQueue.size() > 0) { acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); - } -} - -void Core::MessageHandler::onDownloadFileComplete(const std::list& msgs, const QString& path) { - QMap cData = { - {"attachPath", path} - }; - for (const Shared::MessageInfo& info : msgs) { - if (info.account != acc->getName()) - continue; - - RosterItem* cnt = acc->rh->getRosterItem(info.jid); - if (cnt != nullptr) { - bool changed = cnt->changeMessage(info.messageId, cData); - if (changed) - emit acc->changeMessage(info.jid, info.messageId, cData); } } } -void Core::MessageHandler::onLoadFileError(const std::list& msgs, const QString& text, bool up) { - if (!up) - return; - - for (const Shared::MessageInfo& info : msgs) - if (info.account == acc->getName()) - handleUploadError(info.jid, info.messageId, text); +void Core::MessageHandler::onDownloadFileComplete(const std::list& msgs, const QString& path) +{ + QMap cData = { + {"attachPath", path} + }; + for (const Shared::MessageInfo& info : msgs) { + if (info.account == acc->getName()) { + RosterItem* cnt = acc->rh->getRosterItem(info.jid); + if (cnt != 0) { + if (cnt->changeMessage(info.messageId, cData)) { + emit acc->changeMessage(info.jid, info.messageId, cData); + } + } + } + } } -void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) { +void Core::MessageHandler::onLoadFileError(const std::list& msgs, const QString& text, bool up) +{ + if (up) { + for (const Shared::MessageInfo& info : msgs) { + if (info.account == acc->getName()) { + handleUploadError(info.jid, info.messageId, text); + } + } + } +} + +void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) +{ emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText); - pendingStateMessages.erase(messageId); - pendingCorrectionMessages.erase(messageId); + pendingStateMessages.erase(jid); requestChangeMessage(jid, messageId, { {"state", static_cast(Shared::Message::State::error)}, {"errorText", errorText} }); } -void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path) { +void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& path) +{ for (const Shared::MessageInfo& info : msgs) { - if (info.account != acc->getName()) - continue; - - RosterItem* ri = acc->rh->getRosterItem(info.jid); - if (ri != nullptr) { - Shared::Message msg = ri->getMessage(info.messageId); - msg.setAttachPath(path); - sendMessageWithLocalUploadedFile(msg, url, false); - } else { - qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; + if (info.account == acc->getName()) { + RosterItem* ri = acc->rh->getRosterItem(info.jid); + if (ri != 0) { + Shared::Message msg = ri->getMessage(info.messageId); + sendMessageWithLocalUploadedFile(msg, path, false); + } else { + qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; + } } } } -void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage) { +void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage) +{ msg.setOutOfBandUrl(url); - if (msg.getBody().size() == 0) //not sure why, but most messengers do that + if (msg.getBody().size() == 0) { //not sure why, but most messages do that msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that - - performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage); + } + performSending(msg, newMessage); //TODO removal/progress update } -static const std::set allowedToChangeKeys({ +static const std::set allowerToChangeKeys({ "attachPath", "outOfBandUrl", "state", "errorText" }); -void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data) { +void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data) +{ RosterItem* cnt = acc->rh->getRosterItem(jid); - if (cnt != nullptr) { + if (cnt != 0) { bool allSupported = true; QString unsupportedString; - for (QMap::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness - if (allowedToChangeKeys.count(itr.key()) != 1) { //to not allow this method - allSupported = false; //to make a message to look like if it was edited - unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method - break; //because the underlying tech assumes that - } //the change is initiated by user, not by system + for (QMap::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness + if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method + allSupported = false; //to make a message to look like if it was edited + unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method + break; //because the underlying tech assumes that the change is initiated by user + } //not by system } if (allSupported) { cnt->changeMessage(messageId, data); @@ -639,23 +491,18 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin } } -void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) { +void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) +{ RosterItem* cnt = acc->rh->getRosterItem(jid); - if (cnt != nullptr) { + if (cnt != 0) { try { Shared::Message msg = cnt->getMessage(id); if (msg.getState() == Shared::Message::State::error) { - if (msg.getEdited()) { - QString originalId = msg.getId(); - msg.generateRandomId(); - sendMessage(msg, false, originalId); - } else { - sendMessage(msg, false); - } + sendMessage(msg, false); } else { qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping"; } - } catch (const LMDBAL::NotFound& err) { + } catch (const Archive::NotFound& err) { qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping"; } } else { diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 3555548..4eb9265 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -16,50 +16,48 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_MESSAGEHANDLER_H +#define CORE_MESSAGEHANDLER_H #include -#include #include #include -#include -#include #include #include -#ifdef WITH_OMEMO - #include -#endif #include #include -#include namespace Core { + +/** + * @todo write docs + */ + class Account; -class MessageHandler : public QObject { +class MessageHandler : public QObject +{ Q_OBJECT public: MessageHandler(Account* account); public: - void sendMessage(const Shared::Message& data, bool newMessage = true, QString originalId = ""); + void sendMessage(const Shared::Message& data, bool newMessage = true); void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; void resendMessage(const QString& jid, const QString& id); public slots: void onMessageReceived(const QXmppMessage& message); -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) void onCarbonMessageReceived(const QXmppMessage& message); void onCarbonMessageSent(const QXmppMessage& message); -#endif void onReceiptReceived(const QString& jid, const QString& id); void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); void onDownloadFileComplete(const std::list& msgs, const QString& path); - void onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path); + void onUploadFileComplete(const std::list& msgs, const QString& path); void onLoadFileError(const std::list& msgs, const QString& path, bool up); void requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data); @@ -68,22 +66,16 @@ private: bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true); - void performSending(Shared::Message data, const QString& originalId, bool newMessage = true); + void performSending(Shared::Message data, bool newMessage = true); void prepareUpload(const Shared::Message& data, bool newMessage = true); void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); - QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; - QMap getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; - std::optional getOriginalPendingMessageId(const QString& id, bool clear = true); - bool handlePendingMessageError(const QString& id, const QString& errorText); - std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); - bool adjustPendingMessage(const QString& messageId, const QMap& data, bool final); - void initializeIDs(Shared::Message& target, const QXmppMessage& source) const; private: Account* acc; std::map pendingStateMessages; //key is message id, value is JID - std::map pendingCorrectionMessages; //key is new mesage, value is originalOne - std::deque> uploadingSlotsQueue; + std::deque> uploadingSlotsQueue; }; } + +#endif // CORE_MESSAGEHANDLER_H diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp deleted file mode 100644 index b50bed8..0000000 --- a/core/handlers/omemohandler.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include "omemohandler.h" -#include "core/account.h" -#include "core/adapterfunctions.h" - -Core::OmemoHandler::OmemoHandler(Account* account) : - QObject(), - QXmppOmemoStorage(), - acc(account), - ownDevice(std::nullopt), - db(acc->getName() + "/omemo"), - meta(db.addCache("meta")), - devices(db.addCache>("devices")), - preKeyPairs(db.addCache("preKeyPairs")), - signedPreKeyPairs(db.addCache("signedPreKeyPairs")) -{ - db.open(); - try { - QVariant own = meta->getRecord("ownDevice"); - ownDevice = own.value(); - qDebug() << "Successfully found own device omemo data for account" << acc->getName(); - } catch (const LMDBAL::NotFound& e) { - qDebug() << "No device omemo data was found for account" << acc->getName(); - } -} - -Core::OmemoHandler::~OmemoHandler() { - db.close(); -} - -bool Core::OmemoHandler::hasOwnDevice() { - return ownDevice.has_value(); -} - -QXmppTask Core::OmemoHandler::allData() { - OmemoData data; - data.ownDevice = ownDevice; - - LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); - std::map pkeys = preKeyPairs->readAll(txn); - for (const std::pair& pair : pkeys) - data.preKeyPairs.insert(pair.first, pair.second); - - std::map spre = signedPreKeyPairs->readAll(txn); - for (const std::pair& pair : spre) { - QXmppOmemoStorage::SignedPreKeyPair qxpair = {pair.second.first, pair.second.second}; - data.signedPreKeyPairs.insert(pair.first, qxpair); - } - - std::map> devs = devices->readAll(txn); - for (const std::pair>& pair : devs) - data.devices.insert(pair.first, pair.second); - - return Core::makeReadyTask(std::move(data)); -} - -QXmppTask Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) { - QHash devs; - LMDBAL::WriteTransaction txn = db.beginTransaction(); - bool had = true; - try { - devices->getRecord(jid, devs, txn); - } catch (const LMDBAL::NotFound& error) { - had = false; - } - - devs.insert(deviceId, device); //overwrites - if (had) - devices->changeRecord(jid, devs, txn); - else - devices->addRecord(jid, devs, txn); - - txn.commit(); - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::addPreKeyPairs(const QHash& keyPairs) { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - for (QHash::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr) - preKeyPairs->forceRecord(itr.key(), itr.value(), txn); - - txn.commit(); - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { - signedPreKeyPairs->forceRecord(keyId, std::make_pair(keyPair.creationDate, keyPair.data)); - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) { - LMDBAL::WriteTransaction txn = db.beginTransaction(); - QHash devs = devices->getRecord(jid, txn); - devs.remove(deviceId); - if (devs.isEmpty()) - devices->removeRecord(jid, txn); - else - devices->changeRecord(jid, devs, txn); - - txn.commit(); - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::removeDevices(const QString& jid) { - devices->removeRecord(jid); - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { - try { - preKeyPairs->removeRecord(keyId); - } catch (const LMDBAL::NotFound& e) { - qDebug() << "Couldn't remove preKeyPair " << e.what(); - } - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) { - try { - signedPreKeyPairs->removeRecord(keyId); - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::setOwnDevice(const std::optional& device) { - bool had = ownDevice.has_value(); - ownDevice = device; - if (ownDevice.has_value()) { - if (had) - meta->changeRecord("ownDevice", QVariant::fromValue(ownDevice.value())); - else - meta->addRecord("ownDevice", QVariant::fromValue(ownDevice.value())); - } else if (had) { - meta->removeRecord("ownDevice"); - } - return Core::makeReadyTask(); -} - -QXmppTask Core::OmemoHandler::resetAll() { - ownDevice = std::nullopt; - db.drop(); - - return Core::makeReadyTask(); -} - -void Core::OmemoHandler::getDevices(const QString& jid, std::list& out) const { - QHash devs; - try { - devices->getRecord(jid, devs); - } catch (const LMDBAL::NotFound& error) {} - - for (QHash::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) { - const Device& dev = itr.value(); - out.emplace_back( - itr.key(), - dev.keyId, - dev.label, - dev.removalFromDeviceListDate, - Shared::TrustLevel::undecided, - Shared::EncryptionProtocol::omemo2, - false - ); - } -} - -void Core::OmemoHandler::requestBundles(const QString& jid) { - QXmppTask task = acc->om->buildMissingSessions({jid}); - Contact* cnt = acc->rh->getContact(jid); - if (cnt) - cnt->omemoBundles = Shared::Possible::discovering; - - task.then(this, std::bind(&OmemoHandler::onBundlesReceived, this, jid)); -} - -void Core::OmemoHandler::requestOwnBundles() { - QXmppTask task = acc->om->buildMissingSessions({acc->getBareJid()}); - task.then(this, std::bind(&OmemoHandler::onOwnBundlesReceived, this)); -} - -void Core::OmemoHandler::onBundlesReceived(const QString& jid) { - std::list keys = readKeys(jid); - - Contact* cnt = acc->rh->getContact(jid); - if (cnt) - cnt->omemoBundles = Shared::Possible::present; - - acc->delay->receivedBundles(jid, keys); -} - -void Core::OmemoHandler::onOwnBundlesReceived() { - std::list keys = readKeys(acc->getBareJid()); - if (ownDevice) - keys.emplace_front( - ownDevice->id, - ownDevice->publicIdentityKey, - ownDevice->label, - QDateTime::currentDateTime(), - Shared::TrustLevel::authenticated, - Shared::EncryptionProtocol::omemo2, - true - ); - - acc->delay->receivedOwnBundles(keys); -} - -std::list Core::OmemoHandler::readKeys(const QString& jid) { - std::list keys; - getDevices(jid, keys); - std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); - - for (Shared::KeyInfo& key : keys) { - std::map::const_iterator itr = trustLevels.find(key.fingerPrint); - if (itr != trustLevels.end()) - key.trustLevel = itr->second; - } - - return keys; -} - -void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) { - SHARED_UNUSED(id); - qDebug() << "OMEMO device added for" << jid; -} - -QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { - in >> device.label; - in >> device.keyId; - in >> device.session; - in >> device.unrespondedSentStanzasCount; - in >> device.unrespondedReceivedStanzasCount; - in >> device.removalFromDeviceListDate; - - return in; -} - -QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::Device& device) { - out << device.label; - out << device.keyId; - out << device.session; - out << device.unrespondedSentStanzasCount; - out << device.unrespondedReceivedStanzasCount; - out << device.removalFromDeviceListDate; - - return out; -} - -QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::OwnDevice& device) { - in >> device.id; - in >> device.label; - in >> device.privateIdentityKey; - in >> device.publicIdentityKey; - in >> device.latestSignedPreKeyId; - in >> device.latestPreKeyId; - - return in; -} - -QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::OwnDevice& device) { - out << device.id; - out << device.label; - out << device.privateIdentityKey; - out << device.publicIdentityKey; - out << device.latestSignedPreKeyId; - out << device.latestPreKeyId; - - return out; -} diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h deleted file mode 100644 index 99bfe38..0000000 --- a/core/handlers/omemohandler.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice); -Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); - -namespace Core { -class Account; - -class OmemoHandler : public QObject, public QXmppOmemoStorage { - Q_OBJECT -public: - typedef std::pair SignedPreKeyPair; - - OmemoHandler(Account* account); - ~OmemoHandler() override; - - virtual QXmppTask allData() override; - - virtual QXmppTask setOwnDevice(const std::optional &device) override; - - virtual QXmppTask addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; - virtual QXmppTask removeSignedPreKeyPair(uint32_t keyId) override; - - virtual QXmppTask addPreKeyPairs(const QHash &keyPairs) override; - virtual QXmppTask removePreKeyPair(uint32_t keyId) override; - - virtual QXmppTask addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; - virtual QXmppTask removeDevice(const QString &jid, uint32_t deviceId) override; - virtual QXmppTask removeDevices(const QString &jid) override; - - virtual QXmppTask resetAll() override; - - bool hasOwnDevice(); - - void requestBundles(const QString& jid); - void requestOwnBundles(); - void getDevices(const QString& jid, std::list& out) const; - -public slots: - void onOmemoDeviceAdded(const QString& jid, uint32_t id); - -private slots: - void onBundlesReceived(const QString& jid); - void onOwnBundlesReceived(); - std::list readKeys(const QString& jid); - -private: - Account* acc; - std::optional ownDevice; - LMDBAL::Base db; - LMDBAL::Cache* meta; - LMDBAL::Cache>* devices; - LMDBAL::Cache* preKeyPairs; - LMDBAL::Cache* signedPreKeyPairs; -}; - -} - -QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::Device& device); -QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device); - -QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device); -QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device); diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index e3020c8..ce5f1b7 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -26,41 +26,34 @@ Core::RosterHandler::RosterHandler(Core::Account* account): conferences(), groups(), queuedContacts(), - outOfRosterContacts() {} - -void Core::RosterHandler::initialize() { + outOfRosterContacts() +{ connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved); connect(acc->rm, &QXmppRosterManager::itemChanged, this, &RosterHandler::onRosterItemChanged); - - + + connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded); connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived); - - connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged); - -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - connect(acc->th, &TrustHandler::trustLevelsChanged, this, &RosterHandler::onTrustChanged); -#endif } -Core::RosterHandler::~RosterHandler() { - clear(); +Core::RosterHandler::~RosterHandler() +{ + for (std::map::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { + delete itr->second; + } + + for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { + delete itr->second; + } } -void Core::RosterHandler::clear() { - for (const std::pair& pair : contacts) - delete pair.second; - - for (const std::pair& pair : conferences) - delete pair.second; - - contacts.clear(); - conferences.clear(); -} - -void Core::RosterHandler::onRosterReceived() { +void Core::RosterHandler::onRosterReceived() +{ + acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards + acc->ownVCardRequestInProgress = true; + QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { const QString& jid = bj[i]; @@ -68,7 +61,8 @@ void Core::RosterHandler::onRosterReceived() { } } -void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) { +void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) +{ QString lcJid = bareJid.toLower(); addedAccount(lcJid); std::map::const_iterator itr = queuedContacts.find(lcJid); @@ -78,7 +72,8 @@ void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) { } } -void Core::RosterHandler::addedAccount(const QString& jid) { +void Core::RosterHandler::addedAccount(const QString& jid) +{ std::map::const_iterator itr = contacts.find(jid); QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid); Contact* contact; @@ -87,6 +82,7 @@ void Core::RosterHandler::addedAccount(const QString& jid) { newContact = true; contact = new Contact(jid, acc->name); contacts.insert(std::make_pair(jid, contact)); + } else { contact = itr->second; } @@ -98,44 +94,70 @@ void Core::RosterHandler::addedAccount(const QString& jid) { contact->setName(re.name()); if (newContact) { - handleNewContact(contact); - QMap cData = contact->getInfo(); -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - cData.insert("trust", QVariant::fromValue(acc->th->getSummary(jid))); -#endif + QMap cData({ + {"name", re.name()}, + {"state", QVariant::fromValue(state)} + }); + + careAboutAvatar(contact, cData); int grCount = 0; - for (const QString& groupName : gr) { + for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { + const QString& groupName = *itr; addToGroup(jid, groupName); emit acc->addContact(jid, groupName, cData); grCount++; } - if (grCount == 0) + if (grCount == 0) { emit acc->addContact(jid, "", cData); - - if (acc->pepSupport == Shared::Support::supported) { - acc->dm->requestInfo(jid); - //acc->dm->requestItems(jid); } + handleNewContact(contact); } } -void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) { +void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) +{ QXmppMucRoom* room = acc->mm->addRoom(jid); QString lNick = nick; - if (lNick.size() == 0) + if (lNick.size() == 0) { lNick = acc->getName(); - + } Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room); conferences.insert(std::make_pair(jid, conf)); handleNewConference(conf); - QMap cData = conf->getInfo(); + QMap cData = { + {"autoJoin", conf->getAutoJoin()}, + {"joined", conf->getJoined()}, + {"nick", conf->getNick()}, + {"name", conf->getName()}, + {"avatars", conf->getAllAvatars()} + }; + careAboutAvatar(conf, cData); emit acc->addRoom(jid, cData); } -void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet& groups) { +void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMap& data) +{ + Archive::AvatarInfo info; + bool hasAvatar = item->readAvatarInfo(info); + if (hasAvatar) { + if (info.autogenerated) { + data.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated)); + } else { + data.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid)); + } + data.insert("avatarPath", item->avatarPath() + "." + info.type); + } else { + data.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty)); + data.insert("avatarPath", ""); + acc->requestVCard(item->jid); + } +} + +void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet& groups) +{ if (acc->state == Shared::ConnectionState::connected) { std::map::const_iterator itr = queuedContacts.find(jid); if (itr != queuedContacts.end()) { @@ -149,7 +171,8 @@ void Core::RosterHandler::addContactRequest(const QString& jid, const QString& n } } -void Core::RosterHandler::removeContactRequest(const QString& jid) { +void Core::RosterHandler::removeContactRequest(const QString& jid) +{ QString lcJid = jid.toLower(); if (acc->state == Shared::ConnectionState::connected) { std::set::const_iterator itr = outOfRosterContacts.find(lcJid); @@ -164,23 +187,25 @@ void Core::RosterHandler::removeContactRequest(const QString& jid) { } } -void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) { +void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) +{ connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory); connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse); connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged); connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged); - connect(contact, &RosterItem::encryptionChanged, this, &RosterHandler::onContactEncryptionChanged); - connect(contact, &RosterItem::requestVCard, acc->delay, &DelayManager::Manager::getVCard); + connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard); } -void Core::RosterHandler::handleNewContact(Core::Contact* contact) { +void Core::RosterHandler::handleNewContact(Core::Contact* contact) +{ handleNewRosterItem(contact); connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded); connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved); connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged); } -void Core::RosterHandler::handleNewConference(Core::Conference* contact) { +void Core::RosterHandler::handleNewConference(Core::Conference* contact) +{ handleNewRosterItem(contact); connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged); connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged); @@ -191,74 +216,78 @@ void Core::RosterHandler::handleNewConference(Core::Conference* contact) { connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant); } -void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap& data) { +void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap& data) +{ Conference* room = static_cast(sender()); emit acc->addRoomParticipant(room->jid, nickName, data); } -void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap& data) { +void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap& data) +{ Conference* room = static_cast(sender()); emit acc->changeRoomParticipant(room->jid, nickName, data); } -void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) { +void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) +{ Conference* room = static_cast(sender()); emit acc->removeRoomParticipant(room->jid, nickName); } -void Core::RosterHandler::onMucSubjectChanged(const QString& subject) { +void Core::RosterHandler::onMucSubjectChanged(const QString& subject) +{ Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, {{"subject", subject}}); + emit acc->changeRoom(room->jid, { + {"subject", subject} + }); } -void Core::RosterHandler::onContactGroupAdded(const QString& group) { +void Core::RosterHandler::onContactGroupAdded(const QString& group) +{ Contact* contact = static_cast(sender()); if (contact->groupsCount() == 1) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway } - + QMap cData({ {"name", contact->getName()}, - {"state", QVariant::fromValue(contact->getSubscriptionState())}, -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) - {"trust", QVariant::fromValue(acc->th->getSummary(contact->jid))}, -#endif - {"encryption", QVariant::fromValue(contact->encryption())} + {"state", QVariant::fromValue(contact->getSubscriptionState())} }); addToGroup(contact->jid, group); emit acc->addContact(contact->jid, group, cData); } -void Core::RosterHandler::onContactGroupRemoved(const QString& group) { +void Core::RosterHandler::onContactGroupRemoved(const QString& group) +{ Contact* contact = static_cast(sender()); if (contact->groupsCount() == 0) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway } - + emit acc->removeContact(contact->jid, group); removeFromGroup(contact->jid, group); } -void Core::RosterHandler::onContactNameChanged(const QString& cname) { - RosterItem* contact = static_cast(sender()); - emit acc->changeContact(contact->jid, {{"name", cname}}); -} - -void Core::RosterHandler::onContactEncryptionChanged(Shared::EncryptionProtocol value) { - RosterItem* contact = static_cast(sender()); - emit acc->changeContact(contact->jid, {{"encryption", QVariant::fromValue(value)}}); -} - -void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) { +void Core::RosterHandler::onContactNameChanged(const QString& cname) +{ Contact* contact = static_cast(sender()); - emit acc->changeContact(contact->jid, {{"state", QVariant::fromValue(cstate)}}); + QMap cData({ + {"name", cname}, + }); + emit acc->changeContact(contact->jid, cData); } -void Core::RosterHandler::onTrustChanged(const QString& jid, const Shared::TrustSummary& trust) { - emit acc->changeContact(jid, {{"trust", QVariant::fromValue(trust)}}); +void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) +{ + Contact* contact = static_cast(sender()); + QMap cData({ + {"state", QVariant::fromValue(cstate)}, + }); + emit acc->changeContact(contact->jid, cData); } -void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { +void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) +{ std::map>::iterator gItr = groups.find(group); if (gItr == groups.end()) { gItr = groups.insert(std::make_pair(group, std::set())).first; @@ -267,7 +296,8 @@ void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { gItr->second.insert(jid.toLower()); } -void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) { +void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) +{ QSet toRemove; std::map>::iterator itr = groups.find(group); if (itr == groups.end()) { @@ -285,53 +315,58 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro } } -Core::RosterItem* Core::RosterHandler::getRosterItem(const QString& jid) { - RosterItem* item = nullptr; +Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) +{ + RosterItem* item = 0; QString lcJid = jid.toLower(); std::map::const_iterator citr = contacts.find(lcJid); if (citr != contacts.end()) { item = citr->second; } else { std::map::const_iterator coitr = conferences.find(lcJid); - if (coitr != conferences.end()) + if (coitr != conferences.end()) { item = coitr->second; + } } return item; } -Core::Conference* Core::RosterHandler::getConference(const QString& jid) { +Core::Conference * Core::RosterHandler::getConference(const QString& jid) +{ Conference* item = 0; std::map::const_iterator coitr = conferences.find(jid.toLower()); - if (coitr != conferences.end()) + if (coitr != conferences.end()) { item = coitr->second; - + } return item; } -Core::Contact* Core::RosterHandler::getContact(const QString& jid) { +Core::Contact * Core::RosterHandler::getContact(const QString& jid) +{ Contact* item = 0; std::map::const_iterator citr = contacts.find(jid.toLower()); - if (citr != contacts.end()) + if (citr != contacts.end()) { item = citr->second; - + } return item; } -Core::Contact* Core::RosterHandler::addOutOfRosterContact(const QString& jid) { +Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid) +{ QString lcJid = jid.toLower(); Contact* cnt = new Contact(lcJid, acc->name); contacts.insert(std::make_pair(lcJid, cnt)); outOfRosterContacts.insert(lcJid); cnt->setSubscriptionState(Shared::SubscriptionState::unknown); emit acc->addContact(lcJid, "", QMap({ - {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}, - {"encryption", QVariant::fromValue(Shared::EncryptionProtocol::none)} + {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)} })); handleNewContact(cnt); return cnt; } -void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) { +void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) +{ QString lcJid = bareJid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -348,7 +383,8 @@ void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) { contact->setName(re.name()); } -void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) { +void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) +{ QString lcJid = bareJid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -358,20 +394,22 @@ void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) { Contact* contact = itr->second; contacts.erase(itr); QSet cGroups = contact->getGroups(); - for (const QString& group : cGroups) - removeFromGroup(lcJid, group); - + for (QSet::const_iterator itr = cGroups.begin(), end = cGroups.end(); itr != end; ++itr) { + removeFromGroup(lcJid, *itr); + } emit acc->removeContact(lcJid); contact->deleteLater(); } -void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) { +void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) +{ qDebug() << "room" << room->jid() << "added with name" << room->name() << ", account" << acc->getName() << "joined:" << room->isJoined(); } -void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) { +void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) +{ QList confs = bookmarks.conferences(); for (QList::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) { const QXmppBookmarkConference& c = *itr; @@ -381,42 +419,54 @@ void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) { if (cItr == conferences.end()) { addNewRoom(jid, c.nickName(), c.name(), c.autoJoin()); } else { - if (c.autoJoin()) + if (c.autoJoin()) { cItr->second->setJoined(true); - else + } else { cItr->second->setAutoJoin(false); + } } } } -void Core::RosterHandler::onMucJoinedChanged(bool joined){ +void Core::RosterHandler::onMucJoinedChanged(bool joined) +{ Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, {{"joined", joined}}); + emit acc->changeRoom(room->jid, { + {"joined", joined} + }); } -void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) { +void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) +{ storeConferences(); Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, {{"autoJoin", autoJoin}}); + emit acc->changeRoom(room->jid, { + {"autoJoin", autoJoin} + }); } -void Core::RosterHandler::onMucNickNameChanged(const QString& nickName){ +void Core::RosterHandler::onMucNickNameChanged(const QString& nickName) +{ storeConferences(); Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, {{"nick", nickName}}); + emit acc->changeRoom(room->jid, { + {"nick", nickName} + }); } -Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs){ +Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) +{ Shared::SubscriptionState state; - if (qs == QXmppRosterIq::Item::NotSet) + if (qs == QXmppRosterIq::Item::NotSet) { state = Shared::SubscriptionState::unknown; - else + } else { state = static_cast(qs); - + } return state; } -void Core::RosterHandler::storeConferences() { +void Core::RosterHandler::storeConferences() +{ QXmppBookmarkSet bms = acc->bm->bookmarks(); QList confs; for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { @@ -432,7 +482,8 @@ void Core::RosterHandler::storeConferences() { acc->bm->setBookmarks(bms); } -void Core::RosterHandler::clearConferences() { +void Core::RosterHandler::clearConferences() +{ for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) { itr->second->deleteLater(); emit acc->removeRoom(itr->first); @@ -440,20 +491,21 @@ void Core::RosterHandler::clearConferences() { conferences.clear(); } -void Core::RosterHandler::removeRoomRequest(const QString& jid) { +void Core::RosterHandler::removeRoomRequest(const QString& jid) +{ QString lcJid = jid.toLower(); std::map::const_iterator itr = conferences.find(lcJid); - if (itr == conferences.end()) + if (itr == conferences.end()) { qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping"; - + } itr->second->deleteLater(); conferences.erase(itr); emit acc->removeRoom(lcJid); storeConferences(); } -void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) { - SHARED_UNUSED(password); +void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) +{ QString lcJid = jid.toLower(); std::map::const_iterator cItr = conferences.find(lcJid); if (cItr == conferences.end()) { @@ -464,7 +516,8 @@ void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick } } -void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) { +void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) +{ QString lcJid = jid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -488,43 +541,45 @@ void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QSt } } -void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { +void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) +{ QString lcJid = jid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { qDebug() << "An attempt to remove non existing contact" << lcJid << "of account" << acc->name << "from the group" << groupName << ", skipping"; - return; - } - - QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid); - QSet groups = item.groups(); - QSet::const_iterator gItr = groups.find(groupName); - if (gItr != groups.end()) { - groups.erase(gItr); - item.setGroups(groups); - - QXmppRosterIq iq; - iq.setType(QXmppIq::Set); - iq.addItem(item); - acc->client.sendPacket(iq); } else { - qDebug() << "An attempt to remove contact" << lcJid << "of account" - << acc->name << "from the group" << groupName << "but it's not in that group, skipping"; + QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid); + QSet groups = item.groups(); + QSet::const_iterator gItr = groups.find(groupName); + if (gItr != groups.end()) { + groups.erase(gItr); + item.setGroups(groups); + + QXmppRosterIq iq; + iq.setType(QXmppIq::Set); + iq.addItem(item); + acc->client.sendPacket(iq); + } else { + qDebug() << "An attempt to remove contact" << lcJid << "of account" + << acc->name << "from the group" << groupName << "but it's not in that group, skipping"; + } } } -void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) { +void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) +{ RosterItem* item = static_cast(sender()); QMap cData({ - {"avatarState", QVariant::fromValue(type)}, + {"avatarState", static_cast(type)}, {"avatarPath", path} }); emit acc->changeContact(item->jid, cData); } -void Core::RosterHandler::handleOffline() { +void Core::RosterHandler::handleOffline() +{ for (const std::pair& pair : conferences) { pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); @@ -534,12 +589,3 @@ void Core::RosterHandler::handleOffline() { pair.second->downgradeDatabaseState(); } } - -void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) { - if (support == Shared::Support::supported) { - for (const std::pair& pair : contacts) { - if (pair.second->getPepSupport() == Shared::Support::unknown) - acc->dm->requestInfo(pair.first); - } - } -} diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index afbd372..b1dfc45 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_ROSTERHANDLER_H +#define CORE_ROSTERHANDLER_H #include #include @@ -26,24 +27,22 @@ #include #include #include -#include #include #include #include -#include #include -#include - #include #include -#include namespace Core { + + class Account; -class RosterHandler : public QObject { +class RosterHandler : public QObject +{ Q_OBJECT public: RosterHandler(Account* account); @@ -65,16 +64,12 @@ public: void storeConferences(); void clearConferences(); - - void initialize(); - void clear(); private slots: void onRosterReceived(); void onRosterItemAdded(const QString& bareJid); void onRosterItemChanged(const QString& bareJid); void onRosterItemRemoved(const QString& bareJid); - void onTrustChanged(const QString& jid, const Shared::TrustSummary& trust); void onMucRoomAdded(QXmppMucRoom* room); void onMucJoinedChanged(bool joined); @@ -92,8 +87,6 @@ private slots: void onContactNameChanged(const QString& name); void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactAvatarChanged(Shared::Avatar, const QString& path); - void onContactEncryptionChanged(Shared::EncryptionProtocol value); - void onPepSupportedChanged(Shared::Support support); private: void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin); @@ -103,6 +96,7 @@ private: void handleNewRosterItem(Core::RosterItem* contact); void handleNewContact(Core::Contact* contact); void handleNewConference(Core::Conference* contact); + void careAboutAvatar(Core::RosterItem* item, QMap& data); static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs); @@ -116,3 +110,5 @@ private: }; } + +#endif // CORE_ROSTERHANDLER_H diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp deleted file mode 100644 index 566265f..0000000 --- a/core/handlers/trusthandler.cpp +++ /dev/null @@ -1,463 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "trusthandler.h" -#include "core/account.h" -#include "core/adapterfunctions.h" - -Core::TrustHandler::TrustHandler(Account* account): - QObject(), - QXmppTrustStorage(), - acc(account), - db(acc->getName() + "/trust"), - protocols(db.createDirectory() + "/protocols"), - securityPolicies(db.addCache("securityPolicies")), - ownKeys(db.addCache("ownKeys")), - keysByProtocol() -{ - if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) //never supposed to happen since I have just created a directory; - throw LMDBAL::Directory(protocols.fileName().toStdString()); - - QTextStream in(&protocols); - while(!in.atEnd()) { - QString protocol = in.readLine(); - - if (protocol.size() > 1) { //I'm afraid of reading some nonsence like separately standing \n or EF or BOM, so... let it be at least 2 chars long - KeyCache* cache = db.addCache(protocol.toStdString()); - keysByProtocol.insert(std::make_pair(protocol, cache)); - } - } - - protocols.close(); - db.open(); -} - -Core::TrustHandler::~TrustHandler() { - protocols.close(); - db.close(); -} - -Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) { - std::map::iterator itr = keysByProtocol.find(encryption); - if (itr == keysByProtocol.end()) - return createNewCache(encryption); - else - return itr->second; -} - -Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) { - db.close(); - KeyCache* cache = db.addCache(encryption.toStdString()); - keysByProtocol.insert(std::make_pair(encryption, cache)); - - if (!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) - throw LMDBAL::Directory(protocols.fileName().toStdString()); - - QTextStream out(&protocols); - out << encryption + "\n"; - protocols.close(); - - db.open(); - return cache; -} - -QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { - securityPolicies->removeRecord(encryption); - ownKeys->removeRecord(encryption); - std::map::const_iterator itr = keysByProtocol.find(encryption); - if (itr == keysByProtocol.end()) - return Core::makeReadyTask(); - - LMDBAL::WriteTransaction txn = db.beginTransaction(); - KeyCache* cache = itr->second; - std::map keys = cache->readAll(txn); - cache->drop(txn); - - txn.commit(); - for (const std::pair& pair : keys) { - bool empty = true; - for (const std::pair& trust : pair.second) { - if (trust.second != Shared::TrustLevel::undecided) { - empty = false; - break; - } - } - if (!empty) - emit trustLevelsChanged(pair.first, getSummary(pair.first)); - } - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::trustLevel( - const QString& encryption, - const QString& keyOwnerJid, - const QByteArray& keyId -) { - KeyCache* cache = getCache(encryption); - Shared::TrustLevel level = Shared::TrustLevel::undecided; - try { - Keys map = cache->getRecord(keyOwnerJid); - Keys::const_iterator itr = map.find(keyId); - if (itr != map.end()) - level = itr->second; - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(std::move(convert(level))); -} - -QXmppTask>> Core::TrustHandler::setTrustLevel( - const QString& encryption, - const QList& keyOwnerJids, - QXmpp::TrustLevel oldTrustLevel, - QXmpp::TrustLevel newTrustLevel -) { - QHash> modifiedKeys; - Shared::TrustLevel oldLevel = convert(oldTrustLevel); - Shared::TrustLevel newLevel = convert(newTrustLevel); - std::set modifiedJids; - KeyCache* cache = getCache(encryption); - - LMDBAL::WriteTransaction txn = db.beginTransaction(); - for (const QString& keyOwnerJid : keyOwnerJids) { - Keys map = cache->getRecord(keyOwnerJid, txn); - uint count = 0; - for (std::pair& pair : map) { - Shared::TrustLevel& current = pair.second; - if (current == oldLevel) { - current = newLevel; - modifiedKeys[encryption].insert(keyOwnerJid, pair.first); - modifiedJids.insert(keyOwnerJid); - ++count; - } - } - if (count > 0) - cache->changeRecord(keyOwnerJid, map, txn); - } - txn.commit(); - for (const QString& jid : modifiedJids) - emit trustLevelsChanged(jid, getSummary(jid)); - - return Core::makeReadyTask(std::move(modifiedKeys)); -} - -QXmppTask>> Core::TrustHandler::setTrustLevel( - const QString& encryption, - const QMultiHash& keyIds, - QXmpp::TrustLevel trustLevel -) { - QHash> modifiedKeys; - Shared::TrustLevel level = convert(trustLevel); - std::set modifiedJids; - KeyCache* cache = getCache(encryption); - - LMDBAL::WriteTransaction txn = db.beginTransaction(); - for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { - const QString& keyOwnerJid = itr.key(); - const QByteArray& keyId = itr.value(); - try { - Keys map = cache->getRecord(keyOwnerJid, txn); - std::pair result = map.insert(std::make_pair(keyId, level)); - bool changed = result.second; - if (!changed && result.first->second != level) { - result.first->second = level; - changed = true; - } - if (changed) { - modifiedKeys[encryption].insert(keyOwnerJid, keyId); - modifiedJids.insert(keyOwnerJid); - cache->changeRecord(keyOwnerJid, map, txn); - } - } catch (const LMDBAL::NotFound& e) { - Keys map({{keyId, level}}); - modifiedKeys[encryption].insert(keyOwnerJid, keyId); - modifiedJids.insert(keyOwnerJid); - cache->addRecord(keyOwnerJid, map, txn); - } - } - txn.commit(); - - for (const QString& jid : modifiedJids) - emit trustLevelsChanged(jid, getSummary(jid)); - - return Core::makeReadyTask(std::move(modifiedKeys)); -} - -QXmppTask Core::TrustHandler::hasKey( - const QString& encryption, - const QString& keyOwnerJid, - QXmpp::TrustLevels trustLevels -) { - KeyCache* cache = getCache(encryption); - bool found = false; - try { - Keys map = cache->getRecord(keyOwnerJid); - for (const std::pair& pair : map) { - if (trustLevels.testFlag(convert(pair.second))) { - found = true; - break; - } - } - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(std::move(found)); -} - -QXmppTask>> Core::TrustHandler::keys( - const QString& encryption, - const QList& keyOwnerJids, - QXmpp::TrustLevels trustLevels -) { - HSHBTL res; - - KeyCache* cache = getCache(encryption); - for (const QString& keyOwnerJid : keyOwnerJids) { - try { - Keys map = cache->getRecord(keyOwnerJid); - QHash& pRes = res[keyOwnerJid]; - for (const std::pair& pair : map) { - QXmpp::TrustLevel level = convert(pair.second); - if (!trustLevels || trustLevels.testFlag(level)) { - pRes.insert(pair.first, level); - } - } - } catch (const LMDBAL::NotFound& e) {} - } - return Core::makeReadyTask(std::move(res)); -} - -QXmppTask>> Core::TrustHandler::keys( - const QString& encryption, - QXmpp::TrustLevels trustLevels -) { - QHash res; - KeyCache* cache = getCache(encryption); - std::map storage = cache->readAll(); - for (const std::pair& value : storage) { - for (const std::pair& pair : value.second) { - QXmpp::TrustLevel level = convert(pair.second); - if (!trustLevels || trustLevels.testFlag(level)) - res[level].insert(value.first, pair.first); - } - } - return Core::makeReadyTask(std::move(res)); -} - -QXmppTask Core::TrustHandler::removeKeys(const QString& encryption) { - std::map::const_iterator itr = keysByProtocol.find(encryption); - if (itr == keysByProtocol.end()) - return Core::makeReadyTask(); - - LMDBAL::WriteTransaction txn = db.beginTransaction(); - KeyCache* cache = itr->second; - std::map keys = cache->readAll(txn); - cache->drop(txn); - txn.commit(); - - for (const std::pair& pair : keys) { - bool empty = true; - for (const std::pair& trust : pair.second) { - if (trust.second != Shared::TrustLevel::undecided) { - empty = false; - break; - } - } - if (!empty) - emit trustLevelsChanged(pair.first, getSummary(pair.first)); - } - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { - std::map::const_iterator itr = keysByProtocol.find(encryption); - if (itr == keysByProtocol.end()) - return Core::makeReadyTask(); - - KeyCache* cache = itr->second; - try { - cache->removeRecord(keyOwnerJid); - emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change - } catch (const LMDBAL::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys - - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { - std::set set; - for (const QByteArray& keyId : keyIds) - set.insert(keyId); - - LMDBAL::WriteTransaction txn = db.beginTransaction(); - KeyCache* cache = getCache(encryption); - std::map data = cache->readAll(txn); - std::set modifiedJids; - - for (std::map::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) { - Keys& byOwner = cItr->second; - for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) { - const QByteArray& keyId = itr->first; - if (set.erase(keyId)) { - byOwner.erase(itr++); - modifiedJids.insert(cItr->first); - } else - ++itr; - } - if (byOwner.size() > 0) - data.erase(cItr++); - else - ++cItr; - } - if (modifiedJids.size() > 0) - cache->replaceAll(data, txn); - - txn.commit(); - for (const QString& jid : modifiedJids) - emit trustLevelsChanged(jid, getSummary(jid)); - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::addKeys( - const QString& encryption, - const QString& keyOwnerJid, - const QList& keyIds, - QXmpp::TrustLevel trustLevel -) { - KeyCache* cache = getCache(encryption); - Shared::TrustLevel level = convert(trustLevel); - Keys data; - bool had = false; - LMDBAL::WriteTransaction txn = db.beginTransaction(); - try { - data = cache->getRecord(keyOwnerJid, txn); - had = true; - } catch (const LMDBAL::NotFound& e) {} - for (const QByteArray& keyId : keyIds) { - std::pair result = data.insert(std::make_pair(keyId, level)); - if (!result.second) - result.first->second = level; - } - - if (had) - cache->changeRecord(keyOwnerJid, data, txn); - else - cache->addRecord(keyOwnerJid, data, txn); - - txn.commit(); - emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::ownKey(const QString& encryption) { - QByteArray res; - try { - res = ownKeys->getRecord(encryption); - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(std::move(res)); -} - -QXmppTask Core::TrustHandler::resetOwnKey(const QString& encryption) { - try { - ownKeys->removeRecord(encryption); - } catch (const LMDBAL::NotFound& e) {} - - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { - ownKeys->forceRecord(encryption, keyId); - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::securityPolicy(const QString& encryption) { - QXmpp::TrustSecurityPolicy res; - try { - res = static_cast(securityPolicies->getRecord(encryption)); - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(std::move(res)); -} - -QXmppTask Core::TrustHandler::resetSecurityPolicy(const QString& encryption) { - try { - securityPolicies->removeRecord(encryption); - } catch (const LMDBAL::NotFound& e) {} - return Core::makeReadyTask(); -} - -QXmppTask Core::TrustHandler::setSecurityPolicy( - const QString& encryption, - QXmpp::TrustSecurityPolicy securityPolicy) -{ - uint8_t pol = securityPolicy; - securityPolicies->forceRecord(encryption, pol); - - return Core::makeReadyTask(); -} - -Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const { - const QString& prt = Shared::TrustSummary::protocolKeys.at(protocol); - std::map::const_iterator itr = keysByProtocol.find(prt); - if (itr != keysByProtocol.end()) { - try { - Keys map = itr->second->getRecord(jid); - return map; - } catch (const LMDBAL::NotFound& e) { - return Keys(); - } - } else { - return Keys(); - } -} - -Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const { - Shared::TrustSummary result; - for (const std::pair& pair : keysByProtocol) { - try { - Keys keys = pair.second->getRecord(jid); - Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first); - for (const std::pair& trust : keys) - result.increment(protocol, trust.second); - - } catch (const LMDBAL::NotFound& e) {} - } - - return result; -} - -Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { - switch (level) { - case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided; - case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted; - case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::manuallyDistrusted; - case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted; - case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted; - case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated; - default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning - } -} - -Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) { - switch (level) { - case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided; - case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; - case Shared::TrustLevel::manuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted; - case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; - case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; - case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated; - default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning - } -} diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h deleted file mode 100644 index 68a4aa1..0000000 --- a/core/handlers/trusthandler.h +++ /dev/null @@ -1,89 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef CORE_TRUSTHANDLER_H -#define CORE_TRUSTHANDLER_H - -#include -#include - -#include -#include - -namespace Core { -class Account; - -class TrustHandler : public QObject, public QXmppTrustStorage { - Q_OBJECT -public: - TrustHandler(Account* account); - ~TrustHandler(); - -signals: - void trustLevelsChanged(const QString& jid, const Shared::TrustSummary& summary) const; - -public: - typedef QMultiHash MultySB; - typedef QHash HashSM; - typedef const QList& CLSR; - typedef const QList& CLBAR; - typedef const QString& CSR; - typedef QXmpp::TrustLevel TL; - typedef QHash> HSHBTL; - - typedef std::map Keys; - typedef LMDBAL::Cache KeyCache; - - virtual QXmppTask resetAll(CSR encryption) override; - virtual QXmppTask trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId) override; - virtual QXmppTask setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel) override; - virtual QXmppTask setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel) override; - virtual QXmppTask hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels) override; - virtual QXmppTask keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels) override; - virtual QXmppTask> keys(CSR encryption, QXmpp::TrustLevels trustLevels) override; - virtual QXmppTask removeKeys(CSR encryption) override; - virtual QXmppTask removeKeys(CSR encryption, CSR keyOwnerJid) override; - virtual QXmppTask removeKeys(CSR encryption, CLBAR keyIds) override; - virtual QXmppTask addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel) override; - virtual QXmppTask ownKey(CSR encryption) override; - virtual QXmppTask resetOwnKey(CSR encryption) override; - virtual QXmppTask setOwnKey(CSR encryption, const QByteArray& keyId) override; - virtual QXmppTask securityPolicy(CSR encryption) override; - virtual QXmppTask resetSecurityPolicy(CSR encryption) override; - virtual QXmppTask setSecurityPolicy(const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) override; - - static TL convert(Shared::TrustLevel level); - static Shared::TrustLevel convert(TL level); - - Keys getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const; - Shared::TrustSummary getSummary(const QString& jid) const; - -private: - KeyCache* createNewCache(const QString& encryption); - KeyCache* getCache(const QString& encryption); - -private: - Account* acc; - LMDBAL::Base db; - QFile protocols; - LMDBAL::Cache* securityPolicies; - LMDBAL::Cache* ownKeys; - std::map keysByProtocol; -}; - -} - -#endif // CORE_TRUSTHANDLER_H diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp deleted file mode 100644 index 33b9c31..0000000 --- a/core/handlers/vcardhandler.cpp +++ /dev/null @@ -1,270 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "vcardhandler.h" -#include "core/account.h" - -Core::VCardHandler::VCardHandler(Account* account): - QObject(), - acc(account), - avatarHash(), - avatarType() -{ - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + acc->name; - QDir dir(path); - - if (!dir.exists()) { - bool res = dir.mkpath(path); - if (!res) { - qDebug() << "Couldn't create a cache directory for account" << acc->name; - throw 22; - } - } - - QFile* avatar = new QFile(path + "/avatar.png"); - QString type = "png"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpg"); - type = "jpg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpeg"); - type = "jpeg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.gif"); - type = "gif"; - } - } - } - - if (avatar->exists()) { - if (avatar->open(QFile::ReadOnly)) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(avatar); - avatarHash = sha1.result(); - avatarType = type; - } - } -} - -Core::VCardHandler::~VCardHandler() {} - -void Core::VCardHandler::initialize() { - connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); - //for some reason it doesn't work, launching from common handler - //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); - - if (avatarType.size() != 0) { - acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - acc->presence.setPhotoHash(avatarHash.toUtf8()); - } else { - acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); - } -} - -void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { - QString id = card.from(); - QStringList comps = id.split("/"); - QString jid = comps.front().toLower(); - QString resource(""); - if (comps.size() > 1) { - resource = comps.back(); - } - RosterItem* item = acc->rh->getRosterItem(jid); - - if (item == nullptr) { - if (jid == acc->getBareJid()) - onOwnVCardReceived(card); - else - qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; - - return; - } - - Shared::VCard vCard; - item->handleResponseVCard(card, resource, vCard); - acc->delay->receivedVCard(id, vCard); -} - -void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { - QByteArray ava = card.photo(); - bool avaChanged = false; - QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; - if (ava.size() > 0) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(ava); - QString newHash(sha1.result()); - QMimeDatabase db; - QMimeType newType = db.mimeTypeForData(ava); - if (avatarType.size() > 0) { - if (avatarHash != newHash) { - QString oldPath = path + "avatar." + avatarType; - QFile oldAvatar(oldPath); - bool oldToRemove = false; - if (oldAvatar.exists()) { - if (oldAvatar.rename(oldPath + ".bak")) { - oldToRemove = true; - } else { - qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing"; - } - } - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; - if (oldToRemove) { - qDebug() << "rolling back to the old avatar"; - if (!oldAvatar.rename(oldPath)) { - qDebug() << "Couldn't roll back to the old avatar in account" << acc->name; - } - } - } - } - } else { - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; - } - } - } else { - if (avatarType.size() > 0) { - QFile oldAvatar(path + "avatar." + avatarType); - if (!oldAvatar.remove()) { - qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing"; - } else { - avatarType = ""; - avatarHash = ""; - avaChanged = true; - } - } - } - - if (avaChanged) { - QMap change; - if (avatarType.size() > 0) { - acc->presence.setPhotoHash(avatarHash.toUtf8()); - acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - change.insert("avatarPath", path + "avatar." + avatarType); - } else { - acc->presence.setPhotoHash(""); - acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); - change.insert("avatarPath", ""); - } - acc->client.setClientPresence(acc->presence); - emit acc->changed(change); - } - - Shared::VCard vCard; - initializeVCard(vCard, card); - - if (avatarType.size() > 0) { - vCard.setAvatarType(Shared::Avatar::valid); - vCard.setAvatarPath(path + "avatar." + avatarType); - } else { - vCard.setAvatarType(Shared::Avatar::empty); - } - - if (acc->delay->isOwnVCardPending()) - acc->delay->receivedOwnVCard(vCard); -} - -void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { - if (!acc->delay->isOwnVCardPending()) { - switch (p_presence.vCardUpdateType()) { - case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo - break; - case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here - break; - case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any - if (avatarType.size() > 0) - acc->delay->getOwnVCard(); - break; - case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load - if (avatarHash != p_presence.photoHash()) - acc->delay->getOwnVCard(); - break; - } - } -} - -void Core::VCardHandler::uploadVCard(const Shared::VCard& card) { - QXmppVCardIq iq; - initializeQXmppVCard(iq, card); - - if (card.getAvatarType() != Shared::Avatar::empty) { - QString newPath = card.getAvatarPath(); - QString oldPath = getAvatarPath(); - QByteArray data; - QString type; - if (newPath != oldPath) { - QFile avatar(newPath); - if (!avatar.open(QFile::ReadOnly)) { - qDebug() << "An attempt to upload new vCard to account" << acc->name - << "but it wasn't possible to read file" << newPath - << "which was supposed to be new avatar, uploading old avatar"; - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { - qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; - } else { - data = oA.readAll(); - } - } - } else { - data = avatar.readAll(); - } - } else { - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) - qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; - else - data = oA.readAll(); - } - } - - if (data.size() > 0) { - QMimeDatabase db; - type = db.mimeTypeForData(data).name(); - iq.setPhoto(data); - iq.setPhotoType(type); - } - } - - acc->vm->setClientVCard(iq); - onOwnVCardReceived(iq); -} - -QString Core::VCardHandler::getAvatarPath() const { - if (avatarType.size() == 0) - return ""; - else - return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; -} diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h deleted file mode 100644 index 469398b..0000000 --- a/core/handlers/vcardhandler.h +++ /dev/null @@ -1,63 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef CORE_VCARDHANDLER_H -#define CORE_VCARDHANDLER_H - -#include - -#include -#include - -#include - -#include -#include - -/** - * @todo write docs - */ - -namespace Core { - -class Account; - -class VCardHandler : public QObject -{ - Q_OBJECT -public: - VCardHandler(Account* account); - ~VCardHandler(); - - void handlePresenceOfMyAccountChange(const QXmppPresence& p_presence); - void uploadVCard(const Shared::VCard& card); - QString getAvatarPath() const; - - void initialize(); - -private slots: - void onVCardReceived(const QXmppVCardIq& card); - void onOwnVCardReceived(const QXmppVCardIq& card); - -private: - Account* acc; - - QString avatarHash; - QString avatarType; -}; -} - -#endif // CORE_VCARDHANDLER_H diff --git a/core/main.cpp b/core/main.cpp new file mode 100644 index 0000000..f63d4f8 --- /dev/null +++ b/core/main.cpp @@ -0,0 +1,172 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "../shared/global.h" +#include "../shared/messageinfo.h" +#include "../ui/squawk.h" +#include "signalcatcher.h" +#include "squawk.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + qRegisterMetaType("Shared::Message"); + qRegisterMetaType("Shared::MessageInfo"); + qRegisterMetaType("Shared::VCard"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("QSet"); + qRegisterMetaType("Shared::ConnectionState"); + qRegisterMetaType("Shared::Availability"); + + QApplication app(argc, argv); + SignalCatcher sc(&app); +#ifdef Q_OS_WIN + // Windows need an organization name for QSettings to work + // https://doc.qt.io/qt-5/qsettings.html#basic-usage + { + const QString& orgName = QApplication::organizationName(); + if (orgName.isNull() || orgName.isEmpty()) { + QApplication::setOrganizationName("squawk"); + } + } +#endif + QApplication::setApplicationName("squawk"); + QApplication::setApplicationDisplayName("Squawk"); + QApplication::setApplicationVersion("0.1.5"); + + QTranslator qtTranslator; + qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + app.installTranslator(&qtTranslator); + + QTranslator myappTranslator; + QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + bool found = false; + for (QString share : shares) { + found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); + if (found) { + break; + } + } + if (!found) { + myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); + } + + app.installTranslator(&myappTranslator); + + QIcon icon; + icon.addFile(":images/logo.svg", QSize(16, 16)); + icon.addFile(":images/logo.svg", QSize(24, 24)); + icon.addFile(":images/logo.svg", QSize(32, 32)); + icon.addFile(":images/logo.svg", QSize(48, 48)); + icon.addFile(":images/logo.svg", QSize(64, 64)); + icon.addFile(":images/logo.svg", QSize(96, 96)); + icon.addFile(":images/logo.svg", QSize(128, 128)); + icon.addFile(":images/logo.svg", QSize(256, 256)); + icon.addFile(":images/logo.svg", QSize(512, 512)); + QApplication::setWindowIcon(icon); + + new Shared::Global(); //translates enums + + Squawk w; + w.show(); + + Core::Squawk* squawk = new Core::Squawk(); + QThread* coreThread = new QThread(); + squawk->moveToThread(coreThread); + + QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); + QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop); + QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); + QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit); + QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater); + + QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest); + QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest); + QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest); + QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount); + QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount); + QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState); + QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage); + QObject::connect(&w, &Squawk::resendMessage, squawk,&Core::Squawk::resendMessage); + QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive); + QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact); + QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact); + QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest); + QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest); + QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined); + QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin); + QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest); + QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest); + QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest); + QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest); + QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest); + QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest); + QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard); + QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard); + QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword); + QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid); + + QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); + QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); + QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount); + QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount); + QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup); + QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup); + QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), + &w, qOverload(&Squawk::removeContact)); + QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), + &w, qOverload(&Squawk::removeContact)); + QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact); + QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence); + QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence); + QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged); + QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage); + QObject::connect(squawk, &Core::Squawk::changeMessage, &w, &Squawk::changeMessage); + QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive); + QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom); + QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom); + QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom); + QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant); + QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant); + QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant); + QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete); + QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete); + QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress); + QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError); + QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); + QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword); + QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings); + + coreThread->start(); + + int result = app.exec(); + + w.writeSettings(); + coreThread->wait(500); //TODO hate doing that but settings for some reason don't get saved to the disk + + + return result; +} + diff --git a/core/components/networkaccess.cpp b/core/networkaccess.cpp similarity index 83% rename from core/components/networkaccess.cpp rename to core/networkaccess.cpp index 59e2448..69fe812 100644 --- a/core/components/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ + #include #include @@ -27,18 +28,17 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): manager(0), storage("fileURLStorage"), downloads(), - uploads(), - currentPath() + uploads() { - QSettings settings; - currentPath = settings.value("downloadsPath").toString(); } -Core::NetworkAccess::~NetworkAccess() { +Core::NetworkAccess::~NetworkAccess() +{ stop(); } -void Core::NetworkAccess::downladFile(const QString& url) { +void Core::NetworkAccess::downladFile(const QString& url) +{ std::map::iterator itr = downloads.find(url); if (itr != downloads.end()) { qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping"; @@ -47,25 +47,27 @@ void Core::NetworkAccess::downladFile(const QString& url) { std::pair> p = storage.getPath(url); if (p.first.size() > 0) { QFileInfo info(p.first); - if (info.exists() && info.isFile()) + if (info.exists() && info.isFile()) { emit downloadFileComplete(p.second, p.first); - else + } else { startDownload(p.second, url); + } } else { startDownload(p.second, url); } - } catch (const LMDBAL::NotFound& e) { + } catch (const Archive::NotFound& e) { qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway"; storage.addFile(url); startDownload(std::list(), url); - } catch (const LMDBAL::Unknown& e) { + } catch (const Archive::Unknown& e) { qDebug() << "Error requesting file path:" << e.what(); emit loadFileError(std::list(), QString("Database error: ") + e.what(), false); } } } -void Core::NetworkAccess::start() { +void Core::NetworkAccess::start() +{ if (!running) { manager = new QNetworkAccessManager(); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) @@ -76,7 +78,8 @@ void Core::NetworkAccess::start() { } } -void Core::NetworkAccess::stop() { +void Core::NetworkAccess::stop() +{ if (running) { storage.close(); manager->deleteLater(); @@ -90,7 +93,8 @@ void Core::NetworkAccess::stop() { } } -void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { +void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); @@ -108,7 +112,8 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT } } -void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { +void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) +{ qDebug() << "DEBUG: DOWNLOAD ERROR"; QNetworkReply* rpl = static_cast(sender()); qDebug() << rpl->errorString(); @@ -126,11 +131,12 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { } } -void Core::NetworkAccess::onDownloadSSLError(const QList& errors) { +void Core::NetworkAccess::onDownloadSSLError(const QList& errors) +{ qDebug() << "DEBUG: DOWNLOAD SSL ERRORS"; - for (const QSslError& err : errors) + for (const QSslError& err : errors) { qDebug() << err.errorString(); - + } QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); @@ -145,7 +151,9 @@ void Core::NetworkAccess::onDownloadSSLError(const QList& errors) { } } -QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) { + +QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) +{ QString errorText(""); switch (code) { case QNetworkReply::NoError: @@ -269,7 +277,8 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) { } -void Core::NetworkAccess::onDownloadFinished() { +void Core::NetworkAccess::onDownloadFinished() +{ qDebug() << "DEBUG: DOWNLOAD FINISHED"; QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); @@ -284,16 +293,16 @@ void Core::NetworkAccess::onDownloadFinished() { QStringList hops = url.split("/"); QString fileName = hops.back(); QString jid; - if (dwn->messages.size() > 0) + if (dwn->messages.size() > 0) { jid = dwn->messages.front().jid; - else + } else { qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken"; - + } QString path = prepareDirectory(jid); if (path.size() > 0) { path = checkFileName(fileName, path); - QFile file(Shared::resolvePath(path)); + QFile file(path); if (file.open(QIODevice::WriteOnly)) { file.write(dwn->reply->readAll()); file.close(); @@ -307,10 +316,11 @@ void Core::NetworkAccess::onDownloadFinished() { err = "Couldn't prepare a directory for file"; } - if (path.size() > 0) + if (path.size() > 0) { emit downloadFileComplete(dwn->messages, path); - else + } else { emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false); + } } dwn->reply->deleteLater(); @@ -319,7 +329,8 @@ void Core::NetworkAccess::onDownloadFinished() { } } -void Core::NetworkAccess::startDownload(const std::list& msgs, const QString& url) { +void Core::NetworkAccess::startDownload(const std::list& msgs, const QString& url) +{ Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0}); QNetworkRequest req(url); dwn->reply = manager->get(req); @@ -335,7 +346,8 @@ void Core::NetworkAccess::startDownload(const std::list& ms emit loadFileProgress(dwn->messages, 0, false); } -void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) { +void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) +{ QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -353,7 +365,8 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) { } } -void Core::NetworkAccess::onUploadFinished() { +void Core::NetworkAccess::onUploadFinished() +{ QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -363,30 +376,9 @@ void Core::NetworkAccess::onUploadFinished() { Transfer* upl = itr->second; if (upl->success) { qDebug() << "upload success for" << url; - - // Copy file to Download folder if it is a temp file. See Conversation::onImagePasted. - if (upl->path.startsWith(QDir::tempPath() + QDir::separator() + QStringLiteral("squawk_img_attach_")) && upl->path.endsWith(".png")) { - QString err = ""; - QString downloadDirPath = prepareDirectory(upl->messages.front().jid); - if (downloadDirPath.size() > 0) { - QString newPath = downloadDirPath + QDir::separator() + upl->path.mid(QDir::tempPath().length() + 1); - - // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder - bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath)); - if (copyResult) - upl->path = newPath; // Change storage - else - err = "copying to " + newPath + " failed"; - } else { - err = "Couldn't prepare a directory for file"; - } - - if (err.size() != 0) - qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; - } - + storage.addFile(upl->messages, upl->url, upl->path); - emit uploadFileComplete(upl->messages, upl->url, upl->path); + emit uploadFileComplete(upl->messages, upl->url); } upl->reply->deleteLater(); @@ -397,7 +389,8 @@ void Core::NetworkAccess::onUploadFinished() { } } -void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) { +void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -415,13 +408,14 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot } } -QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) { - QString p = Shared::squawkifyPath(path); +QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) +{ + QString p; try { - p = storage.getUrl(p); - } catch (const LMDBAL::NotFound& err) { - p = ""; + p = storage.getUrl(path); + } catch (const Archive::NotFound& err) { + } catch (...) { throw; } @@ -429,13 +423,8 @@ QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) { return p; } -void Core::NetworkAccess::uploadFile( - const Shared::MessageInfo& info, - const QString& path, - const QUrl& put, - const QUrl& get, - const QMap headers -) { +void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap headers) +{ QFile* file = new QFile(path); Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file}); QNetworkRequest req(put); @@ -462,18 +451,22 @@ void Core::NetworkAccess::uploadFile( } } -void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) { +void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) +{ storage.addFile(url, account, jid, id); std::map::iterator itr = downloads.find(url); - if (itr != downloads.end()) + if (itr != downloads.end()) { itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay? + } } -void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) { +void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) +{ storage.addFile(url, path, account, jid, id); } -bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) { +bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) +{ for (const std::pair& pair : uploads) { Transfer* info = pair.second; if (pair.second->path == path) { @@ -495,66 +488,53 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri return false; } -QString Core::NetworkAccess::prepareDirectory(const QString& jid) { - QString path = currentPath; - QString addition; +QString Core::NetworkAccess::prepareDirectory(const QString& jid) +{ + QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + path += "/" + QApplication::applicationName(); if (jid.size() > 0) { - addition = jid; - path += QDir::separator() + jid; + path += "/" + jid; } - QDir location(path); if (!location.exists()) { bool res = location.mkpath(path); - if (!res) + if (!res) { return ""; - else - return "squawk://" + addition; + } else { + return path; + } } - return "squawk://" + addition; + return path; } -QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) { +QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) +{ QStringList parts = name.split("."); QString suffix(""); QStringList::const_iterator sItr = parts.begin(); QString realName = *sItr; ++sItr; - for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) + for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) { suffix += "." + (*sItr); - + } QString postfix(""); - QString resolvedPath = Shared::resolvePath(path); - QString count(""); - QFileInfo proposedName(resolvedPath + QDir::separator() + realName + count + suffix); - + QFileInfo proposedName(path + "/" + realName + suffix); int counter = 0; while (proposedName.exists()) { - count = QString("(") + std::to_string(++counter).c_str() + ")"; - proposedName = QFileInfo(resolvedPath + QDir::separator() + realName + count + suffix); + QString count = QString("(") + std::to_string(++counter).c_str() + ")"; + proposedName = QFileInfo(path + "/" + realName + count + suffix); } - - return path + QDir::separator() + realName + count + suffix; + + return proposedName.absoluteFilePath(); } -QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) { +QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) +{ return storage.addMessageAndCheckForPath(url, account, jid, id); } -std::list Core::NetworkAccess::reportPathInvalid(const QString& path) { +std::list Core::NetworkAccess::reportPathInvalid(const QString& path) +{ return storage.deletedFile(path); } - -void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { - QDir dir(currentPath); - bool success = true; - qDebug() << "moving" << currentPath << "to" << newPath; - for (QString fileName : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) - success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; - - if (!success) - qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; - - currentPath = newPath; -} diff --git a/core/components/networkaccess.h b/core/networkaccess.h similarity index 93% rename from core/components/networkaccess.h rename to core/networkaccess.h index ddb5ba8..75c189c 100644 --- a/core/components/networkaccess.h +++ b/core/networkaccess.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_NETWORKACCESS_H +#define CORE_NETWORKACCESS_H #include #include @@ -25,17 +26,20 @@ #include #include #include -#include #include -#include #include "urlstorage.h" namespace Core { +/** + * @todo write docs + */ + //TODO Need to describe how to get rid of records when file is no longer reachable; -class NetworkAccess : public QObject { +class NetworkAccess : public QObject +{ Q_OBJECT struct Transfer; public: @@ -54,14 +58,13 @@ public: signals: void loadFileProgress(const std::list& msgs, qreal value, bool up); void loadFileError(const std::list& msgs, const QString& text, bool up); - void uploadFileComplete(const std::list& msgs, const QString& url, const QString& path); + void uploadFileComplete(const std::list& msgs, const QString& url); void downloadFileComplete(const std::list& msgs, const QString& path); public slots: void downladFile(const QString& url); void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id); void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id); - void moveFilesDirectory(const QString& newPath); private: void startDownload(const std::list& msgs, const QString& url); @@ -84,7 +87,6 @@ private: UrlStorage storage; std::map downloads; std::map uploads; - QString currentPath; struct Transfer { std::list messages; @@ -98,3 +100,5 @@ private: }; } + +#endif // CORE_NETWORKACCESS_H diff --git a/core/passwordStorageEngines/CMakeLists.txt b/core/passwordStorageEngines/CMakeLists.txt index 2a38931..4da3873 100644 --- a/core/passwordStorageEngines/CMakeLists.txt +++ b/core/passwordStorageEngines/CMakeLists.txt @@ -1,6 +1,9 @@ if (WITH_KWALLET) - target_sources(squawk PRIVATE kwallet.cpp) + target_sources(squawk PRIVATE + kwallet.cpp + kwallet.h + ) add_subdirectory(wrappers) - target_include_directories(squawk PRIVATE $) + target_include_directories(squawk PRIVATE $) endif () diff --git a/core/passwordStorageEngines/kwallet.cpp b/core/passwordStorageEngines/kwallet.cpp index c92085b..0dfe071 100644 --- a/core/passwordStorageEngines/kwallet.cpp +++ b/core/passwordStorageEngines/kwallet.cpp @@ -28,8 +28,7 @@ Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0; Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0; Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial; - -QLibrary Core::PSE::KWallet::lib(QString("%1/kwalletWrapper").arg(PLUGIN_PATH)); +QLibrary Core::PSE::KWallet::lib("kwalletWrapper"); Core::PSE::KWallet::KWallet(): QObject(), diff --git a/core/passwordStorageEngines/kwallet.h b/core/passwordStorageEngines/kwallet.h index 1f047e6..28475d2 100644 --- a/core/passwordStorageEngines/kwallet.h +++ b/core/passwordStorageEngines/kwallet.h @@ -27,7 +27,7 @@ #include #include -#include +#include namespace Core { namespace PSE { diff --git a/core/passwordStorageEngines/wrappers/CMakeLists.txt b/core/passwordStorageEngines/wrappers/CMakeLists.txt index 6280cc0..6d486c0 100644 --- a/core/passwordStorageEngines/wrappers/CMakeLists.txt +++ b/core/passwordStorageEngines/wrappers/CMakeLists.txt @@ -1,5 +1,2 @@ add_library(kwalletWrapper SHARED kwallet.cpp) - -target_link_libraries(kwalletWrapper PRIVATE KF${QT_VERSION_MAJOR}::Wallet) - -install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) +target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet) diff --git a/core/passwordStorageEngines/wrappers/kwallet.cpp b/core/passwordStorageEngines/wrappers/kwallet.cpp index 5c7bc99..f5e7cb5 100644 --- a/core/passwordStorageEngines/wrappers/kwallet.cpp +++ b/core/passwordStorageEngines/wrappers/kwallet.cpp @@ -1,22 +1,4 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include +#include extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) { return KWallet::Wallet::openWallet(name, w, ot); diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index a5763e2..b1951d6 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -27,7 +27,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje account(pAccount), name(), archiveState(empty), - archive(new Archive(account, jid)), + archive(new Archive(jid)), syncronizing(false), requestedCount(0), requestedBefore(), @@ -38,36 +38,42 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje toCorrect(), muc(false) { - archive->open(); + archive->open(account); if (archive->size() != 0) { - if (archive->isFromTheBeginning()) + if (archive->isFromTheBeginning()) { archiveState = beginning; - else + } else { archiveState = chunk; + } } } -Core::RosterItem::~RosterItem() { +Core::RosterItem::~RosterItem() +{ delete archive; } -Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const { +Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const +{ return archiveState; } -QString Core::RosterItem::getName() const { +QString Core::RosterItem::getName() const +{ return name; } -void Core::RosterItem::setName(const QString& n) { +void Core::RosterItem::setName(const QString& n) +{ if (name != n) { name = n; emit nameChanged(name); } } -void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) { +void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) +{ if (msg.storable()) { hisoryCache.push_back(msg); std::map::iterator itr = toCorrect.find(msg.getId()); @@ -81,7 +87,8 @@ void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) { } } -void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) { +void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) +{ if (msg.storable()) { QDateTime thisTime = msg.getTime(); std::map::iterator itr = toCorrect.find(originalId); @@ -102,7 +109,8 @@ void Core::RosterItem::correctMessageInArchive(const QString& originalId, const } } -void Core::RosterItem::requestHistory(int count, const QString& before) { +void Core::RosterItem::requestHistory(int count, const QString& before) +{ if (syncronizing) { requestCache.emplace_back(count, before); } else { @@ -110,25 +118,21 @@ void Core::RosterItem::requestHistory(int count, const QString& before) { } } -void Core::RosterItem::nextRequest() { +void Core::RosterItem::nextRequest() +{ if (syncronizing) { if (requestedCount != -1) { bool last = false; if (archiveState == beginning || archiveState == complete) { - try { - QString firstId = archive->oldestId(); - if (responseCache.size() == 0) { - if (requestedBefore == firstId) { - last = true; - } - } else { - if (responseCache.front().getId() == firstId) { - last = true; - } + QString firstId = archive->oldestId(); + if (responseCache.size() == 0) { + if (requestedBefore == firstId) { + last = true; + } + } else { + if (responseCache.front().getId() == firstId) { + last = true; } - //} catch (const Archive::Empty& e) { - } catch (const LMDBAL::NotFound& e) { - last = true; } } else if (archiveState == empty && responseCache.size() == 0) { last = true; @@ -149,7 +153,8 @@ void Core::RosterItem::nextRequest() { } } -void Core::RosterItem::performRequest(int count, const QString& before) { +void Core::RosterItem::performRequest(int count, const QString& before) +{ syncronizing = true; requestedCount = count; requestedBefore = before; @@ -166,13 +171,8 @@ void Core::RosterItem::performRequest(int count, const QString& before) { requestCache.emplace_back(requestedCount, before); requestedCount = -1; } - try { - Shared::Message msg = archive->newest(); - emit needHistory("", getId(msg), msg.getTime()); - //} catch (const Archive::Empty& e) { - } catch (const LMDBAL::NotFound& e) { //this can happen when the only message in archive is not server stored (error, for example) - emit needHistory(before, ""); - } + Shared::Message msg = archive->newest(); + emit needHistory("", getId(msg), msg.getTime()); } break; case end: @@ -188,14 +188,14 @@ void Core::RosterItem::performRequest(int count, const QString& before) { std::list arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); found = true; - } catch (const LMDBAL::NotFound& e) { + } catch (const Archive::NotFound& e) { + requestCache.emplace_back(requestedCount, before); + requestedCount = -1; + emit needHistory(getId(archive->oldest()), ""); + } catch (const Archive::Empty& e) { requestCache.emplace_back(requestedCount, before); requestedCount = -1; emit needHistory(getId(archive->oldest()), ""); - // } catch (const Archive::Empty& e) { - // requestCache.emplace_back(requestedCount, before); - // requestedCount = -1; - // emit needHistory(getId(archive->oldest()), ""); } if (found) { @@ -228,17 +228,18 @@ void Core::RosterItem::performRequest(int count, const QString& before) { try { std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - } catch (const LMDBAL::NotFound& e) { + } catch (const Archive::NotFound& e) { + qDebug("requesting id hasn't been found in archive, skipping"); + } catch (const Archive::Empty& e) { qDebug("requesting id hasn't been found in archive, skipping"); - // } catch (const Archive::Empty& e) { - // qDebug("requesting id hasn't been found in archive, skipping"); } nextRequest(); break; } } -QString Core::RosterItem::getId(const Shared::Message& msg) { +QString Core::RosterItem::getId(const Shared::Message& msg) +{ QString id; if (muc) { id = msg.getStanzaId(); @@ -248,7 +249,8 @@ QString Core::RosterItem::getId(const Shared::Message& msg) { return id; } -void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) { +void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) +{ if (msg.getId().size() > 0) { if (msg.storable()) { switch (archiveState) { @@ -289,7 +291,8 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) { } } -bool Core::RosterItem::changeMessage(const QString& id, const QMap& data) { +bool Core::RosterItem::changeMessage(const QString& id, const QMap& data) +{ bool found = false; for (Shared::Message& msg : appendCache) { if (msg.getId() == id) { @@ -313,7 +316,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMapchangeMessage(id, data); found = true; - } catch (const LMDBAL::NotFound& e) { + } catch (const Archive::NotFound& e) { qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found"; } } @@ -330,7 +333,8 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap 0) { added = archive->addElements(hisoryCache); @@ -389,8 +393,10 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); found = true; - } catch (const LMDBAL::NotFound& e) { - // } catch (const Archive::Empty& e) { + } catch (const Archive::NotFound& e) { + + } catch (const Archive::Empty& e) { + } if (!found || requestedCount > int(responseCache.size())) { if (archiveState == complete) { @@ -415,43 +421,51 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs } } -QString Core::RosterItem::getServer() const { +QString Core::RosterItem::getServer() const +{ QStringList lst = jid.split("@"); return lst.back(); } -bool Core::RosterItem::isMuc() const { +bool Core::RosterItem::isMuc() const +{ return muc; } -QString Core::RosterItem::avatarPath(const QString& resource) const { +QString Core::RosterItem::avatarPath(const QString& resource) const +{ QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource); return path; } -QString Core::RosterItem::folderPath() const { +QString Core::RosterItem::folderPath() const +{ QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + account + "/" + jid; return path; } -bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { +bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) +{ bool result = archive->setAvatar(data, info, false, resource); if (resource.size() == 0 && result) { - if (data.size() == 0) + if (data.size() == 0) { emit avatarChanged(Shared::Avatar::empty, ""); - else + } else { emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type); + } } return result; } -bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) { +bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) +{ Archive::AvatarInfo info; return setAutoGeneratedAvatar(info, resource); } -bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) { +bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) +{ QImage image(96, 96, QImage::Format_ARGB32_Premultiplied); QPainter painter(&image); quint8 colorIndex = rand() % Shared::colorPalette.size(); @@ -461,11 +475,11 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q f.setBold(true); f.setPixelSize(72); painter.setFont(f); - if (bg.lightnessF() > 0.5) + if (bg.lightnessF() > 0.5) { painter.setPen(Qt::black); - else + } else { painter.setPen(Qt::white); - + } painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper()); QByteArray arr; QBuffer stream(&arr); @@ -473,22 +487,25 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q image.save(&stream, "PNG"); stream.close(); bool result = archive->setAvatar(arr, info, true, resource); - if (resource.size() == 0 && result) + if (resource.size() == 0 && result) { emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png"); - + } return result; } -bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const { +bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const +{ return archive->readAvatarInfo(target, resource); } -void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) { +Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource) +{ Archive::AvatarInfo info; Archive::AvatarInfo newInfo; bool hasAvatar = readAvatarInfo(info, resource); QByteArray ava = card.photo(); + Shared::VCard vCard; initializeVCard(vCard, card); Shared::Avatar type = Shared::Avatar::empty; QString path = ""; @@ -508,9 +525,9 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri } } } else { - if (!hasAvatar || !info.autogenerated) + if (!hasAvatar || !info.autogenerated) { setAutoGeneratedAvatar(resource); - + } type = Shared::Avatar::autocreated; path = avatarPath(resource) + ".png"; } @@ -518,11 +535,15 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri vCard.setAvatarType(type); vCard.setAvatarPath(path); - if (resource.size() == 0) + if (resource.size() == 0) { emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath()); + } + + return vCard; } -void Core::RosterItem::clearArchiveRequests() { +void Core::RosterItem::clearArchiveRequests() +{ syncronizing = false; requestedCount = 0; requestedBefore = ""; @@ -538,72 +559,30 @@ void Core::RosterItem::clearArchiveRequests() { requestCache.clear(); } -void Core::RosterItem::downgradeDatabaseState() { - if (archiveState == ArchiveState::complete) +void Core::RosterItem::downgradeDatabaseState() +{ + if (archiveState == ArchiveState::complete) { archiveState = ArchiveState::beginning; - + } - if (archiveState == ArchiveState::end) + if (archiveState == ArchiveState::end) { archiveState = ArchiveState::chunk; + } } -Shared::Message Core::RosterItem::getMessage(const QString& id) { +Shared::Message Core::RosterItem::getMessage(const QString& id) +{ for (const Shared::Message& msg : appendCache) { - if (msg.getId() == id) + if (msg.getId() == id) { return msg; + } } for (Shared::Message& msg : hisoryCache) { - if (msg.getId() == id) + if (msg.getId() == id) { return msg; + } } return archive->getElement(id); } - -Shared::EncryptionProtocol Core::RosterItem::encryption() const { - return archive->encryption(); -} - -void Core::RosterItem::setEncryption(Shared::EncryptionProtocol value) { - bool changed = archive->setEncryption(value); - if (changed) - emit encryptionChanged(value); -} - -QMap Core::RosterItem::getInfo() const { - QMap result({ - {"name", name}, - {"encryption", QVariant::fromValue(encryption())}, - }); - Archive::AvatarInfo info; - bool hasAvatar = readAvatarInfo(info); - careAboutAvatar(hasAvatar, info, result); - - return result; -} - - -void Core::RosterItem::careAboutAvatar ( - bool hasAvatar, - const Archive::AvatarInfo& info, - QMap& output, - const QString& resource, - const QString& subject -) const { - if (hasAvatar) { - if (info.autogenerated) - output.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated)); - else - output.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid)); - - output.insert("avatarPath", avatarPath(resource) + "." + info.type); - } else { - output.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty)); - output.insert("avatarPath", ""); - if (subject.size() == 0) - emit requestVCard(jid); - else - emit requestVCard(subject); - } -} diff --git a/core/rosteritem.h b/core/rosteritem.h index 33a08f0..237a46a 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_ROSTERITEM_H +#define CORE_ROSTERITEM_H #include #include @@ -33,8 +34,7 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/vcard.h" -#include "components/archive.h" -#include "adapterfunctions.h" +#include "archive.h" namespace Core { @@ -61,8 +61,6 @@ public: void setName(const QString& n); QString getServer() const; bool isMuc() const; - Shared::EncryptionProtocol encryption() const; - void setEncryption(Shared::EncryptionProtocol value); void addMessageToArchive(const Shared::Message& msg); void correctMessageInArchive(const QString& originalId, const Shared::Message& msg); @@ -73,24 +71,22 @@ public: QString folderPath() const; bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const; virtual bool setAutoGeneratedAvatar(const QString& resource = ""); - virtual void handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& out); + virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource); virtual void handlePresence(const QXmppPresence& pres) = 0; bool changeMessage(const QString& id, const QMap& data); void clearArchiveRequests(); void downgradeDatabaseState(); - virtual QMap getInfo() const; Shared::Message getMessage(const QString& id); signals: - void nameChanged(const QString& name) const; - void subscriptionStateChanged(Shared::SubscriptionState state) const; - void historyResponse(const std::list& messages, bool last) const; - void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()) const; - void avatarChanged(Shared::Avatar, const QString& path) const; - void requestVCard(const QString& jid) const; - void encryptionChanged(Shared::EncryptionProtocol value) const; + void nameChanged(const QString& name); + void subscriptionStateChanged(Shared::SubscriptionState state); + void historyResponse(const std::list& messages, bool last); + void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()); + void avatarChanged(Shared::Avatar, const QString& path); + void requestVCard(const QString& jid); public: const QString jid; @@ -99,13 +95,6 @@ public: protected: virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = ""); virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = ""); - void careAboutAvatar( - bool hasAvatar, - const Archive::AvatarInfo& info, - QMap& output, - const QString& resource = "", - const QString& subject = "" - ) const; protected: QString name; @@ -129,3 +118,5 @@ private: }; } + +#endif // CORE_ROSTERITEM_H diff --git a/core/signalcatcher.cpp b/core/signalcatcher.cpp index ef3e14c..dcdcb88 100644 --- a/core/signalcatcher.cpp +++ b/core/signalcatcher.cpp @@ -21,8 +21,6 @@ #include #include -#include "shared/defines.h" - int SignalCatcher::sigintFd[2] = {0,0}; SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): @@ -30,10 +28,14 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): app(p_app) { if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd)) + { qFatal("Couldn't create INT socketpair"); + } if (setup_unix_signal_handlers() != 0) + { qFatal("Couldn't install unix handlers"); + } snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt); @@ -42,25 +44,25 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): SignalCatcher::~SignalCatcher() {} -void SignalCatcher::handleSigInt() { +void SignalCatcher::handleSigInt() +{ snInt->setEnabled(false); char tmp; ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp)); - SHARED_UNUSED(s); - emit interrupt(); + app->quit(); snInt->setEnabled(true); } -void SignalCatcher::intSignalHandler(int unused) { +void SignalCatcher::intSignalHandler(int unused) +{ char a = 1; ssize_t s = ::write(sigintFd[0], &a, sizeof(a)); - SHARED_UNUSED(s); - SHARED_UNUSED(unused); } -int SignalCatcher::setup_unix_signal_handlers() { +int SignalCatcher::setup_unix_signal_handlers() +{ struct sigaction s_int; s_int.sa_handler = SignalCatcher::intSignalHandler; diff --git a/core/signalcatcher.h b/core/signalcatcher.h index 0c8e757..7d75260 100644 --- a/core/signalcatcher.h +++ b/core/signalcatcher.h @@ -33,9 +33,6 @@ public: static void intSignalHandler(int unused); -signals: - void interrupt(); - public slots: void handleSigInt(); diff --git a/core/squawk.cpp b/core/squawk.cpp index 7f04d9a..6b8af49 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -22,19 +22,15 @@ #include #include -#include "utils/jammer.h" - Core::Squawk::Squawk(QObject* parent): QObject(parent), accounts(), amap(), - state(Shared::Availability::offline), network(), - isInitialized(false), + waitingForAccounts(0) #ifdef WITH_KWALLET - kwallet(), + ,kwallet() #endif - clientCache() { connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError); @@ -45,14 +41,15 @@ Core::Squawk::Squawk(QObject* parent): if (kwallet.supportState() == PSE::KWallet::success) { connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened); connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword); - connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword); + connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword); Shared::Global::setSupported("KWallet", true); } #endif } -Core::Squawk::~Squawk() { +Core::Squawk::~Squawk() +{ Accounts::const_iterator itr = accounts.begin(); Accounts::const_iterator end = accounts.end(); for (; itr != end; ++itr) { @@ -60,73 +57,69 @@ Core::Squawk::~Squawk() { } } -void Core::Squawk::onWalletOpened(bool success) { +void Core::Squawk::onWalletOpened(bool success) +{ qDebug() << "KWallet opened: " << success; } -void Core::Squawk::stop() { +void Core::Squawk::stop() +{ qDebug("Stopping squawk core.."); network.stop(); - clientCache.close(); - - if (isInitialized) { - QSettings settings; - settings.beginGroup("core"); - settings.beginWriteArray("accounts"); - for (std::deque::size_type i = 0; i < accounts.size(); ++i) { - settings.setArrayIndex(i); - Account* acc = accounts[i]; - - Shared::AccountPassword ap = acc->getPasswordType(); - QString password; - - switch (ap) { - case Shared::AccountPassword::plain: - password = acc->getPassword(); - break; - case Shared::AccountPassword::jammed: - password = Jammer::encrypt(acc->getPassword(), passwordHash); - break; - default: - break; - } - - settings.setValue("name", acc->getName()); - settings.setValue("server", acc->getServer()); - settings.setValue("login", acc->getLogin()); - settings.setValue("password", password); - settings.setValue("resource", acc->getResource()); - settings.setValue("passwordType", static_cast(ap)); - settings.setValue("active", acc->getActive()); + QSettings settings; + settings.beginGroup("core"); + settings.beginWriteArray("accounts"); + SimpleCrypt crypto(passwordHash); + for (std::deque::size_type i = 0; i < accounts.size(); ++i) { + settings.setArrayIndex(i); + Account* acc = accounts[i]; + + Shared::AccountPassword ap = acc->getPasswordType(); + QString password; + + switch (ap) { + case Shared::AccountPassword::plain: + password = acc->getPassword(); + break; + case Shared::AccountPassword::jammed: + password = crypto.encryptToString(acc->getPassword()); + break; + default: + break; } - settings.endArray(); - settings.endGroup(); - - settings.sync(); + + settings.setValue("name", acc->getName()); + settings.setValue("server", acc->getServer()); + settings.setValue("login", acc->getLogin()); + settings.setValue("password", password); + settings.setValue("resource", acc->getResource()); + settings.setValue("passwordType", static_cast(ap)); } + settings.endArray(); + settings.endGroup(); + + settings.sync(); emit quit(); } -void Core::Squawk::start() { +void Core::Squawk::start() +{ qDebug("Starting squawk core.."); readSettings(); - isInitialized = true; network.start(); - clientCache.open(); } -void Core::Squawk::newAccountRequest(const QMap& map) { +void Core::Squawk::newAccountRequest(const QMap& map) +{ QString name = map.value("name").toString(); QString login = map.value("login").toString(); QString server = map.value("server").toString(); QString password = map.value("password").toString(); QString resource = map.value("resource").toString(); - int passwordType = map.value("passwordType").toInt(); - bool active = map.value("active").toBool(); - addAccount(login, server, password, name, resource, active, Shared::Global::fromInt(passwordType)); + addAccount(login, server, password, name, resource, Shared::AccountPassword::plain); } void Core::Squawk::addAccount( @@ -134,15 +127,13 @@ void Core::Squawk::addAccount( const QString& server, const QString& password, const QString& name, - const QString& resource, - bool active, - Shared::AccountPassword passwordType) + const QString& resource, + Shared::AccountPassword passwordType +) { - if (amap.count(name) > 0) { - qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring"; - return; - } - Account* acc = new Account(login, server, password, name, active, &network); + QSettings settings; + + Account* acc = new Account(login, server, password, name, &network); acc->setResource(resource); acc->setPasswordType(passwordType); accounts.push_back(acc); @@ -151,8 +142,6 @@ void Core::Squawk::addAccount( connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged); connect(acc, &Account::changed, this, &Squawk::onAccountChanged); connect(acc, &Account::error, this, &Squawk::onAccountError); - connect(acc, &Account::needPassword, this, &Squawk::onAccountNeedPassword); - connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged); connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact); connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup); @@ -176,11 +165,9 @@ void Core::Squawk::addAccount( connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence); connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence); - connect(acc, &Account::infoReady, this, &Squawk::responseInfo); + connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError); - - connect(acc, &Account::infoDiscovered, this, &Squawk::onAccountInfoDiscovered); QMap map = { {"login", login}, @@ -192,76 +179,49 @@ void Core::Squawk::addAccount( {"offline", QVariant::fromValue(Shared::Availability::offline)}, {"error", ""}, {"avatarPath", acc->getAvatarPath()}, - {"passwordType", QVariant::fromValue(passwordType)}, - {"active", active} + {"passwordType", QVariant::fromValue(passwordType)} }; emit newAccount(map); - - switch (passwordType) { - case Shared::AccountPassword::alwaysAsk: - case Shared::AccountPassword::kwallet: - if (password == "") - acc->invalidatePassword(); - - break; - default: - break; - } - - if (state != Shared::Availability::offline) { - acc->setAvailability(state); - if (acc->getActive()) - acc->connect(); - } } -void Core::Squawk::changeState(Shared::Availability p_state) { +void Core::Squawk::changeState(Shared::Availability p_state) +{ if (state != p_state) { - for (std::deque::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { - Account* acc = *itr; - acc->setAvailability(p_state); - if (state == Shared::Availability::offline && acc->getActive()) - acc->connect(); - } state = p_state; - - emit stateChanged(p_state); + } + + for (std::deque::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { + (*itr)->setAvailability(state); } } -void Core::Squawk::connectAccount(const QString& account) { +void Core::Squawk::connectAccount(const QString& account) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to connect non existing account, skipping"); return; } - itr->second->setActive(true); - if (state != Shared::Availability::offline) - itr->second->connect(); + itr->second->connect(); } -void Core::Squawk::disconnectAccount(const QString& account) { +void Core::Squawk::disconnectAccount(const QString& account) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to connect non existing account, skipping"); return; } - itr->second->setActive(false); itr->second->disconnect(); } -void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) { +void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) +{ Account* acc = static_cast(sender()); - QMap changes = { - {"state", QVariant::fromValue(p_state)} - }; - if (acc->getLastError() == Account::Error::none) - changes.insert("error", ""); - - emit changeAccount(acc->getName(), changes); - + emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}}); + #ifdef WITH_KWALLET if (p_state == Shared::ConnectionState::connected) { if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) { @@ -269,87 +229,103 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta } } #endif + + Accounts::const_iterator itr = accounts.begin(); + bool es = true; + bool ea = true; + Shared::ConnectionState cs = (*itr)->getState(); + Shared::Availability av = (*itr)->getAvailability(); + itr++; + for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) { + Account* item = *itr; + if (item->getState() != cs) { + es = false; + } + if (item->getAvailability() != av) { + ea = false; + } + } + + if (es) { + if (cs == Shared::ConnectionState::disconnected) { + state = Shared::Availability::offline; + emit stateChanged(state); + } else if (ea) { + state = av; + emit stateChanged(state); + } + } + } -void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) { +void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) +{ Account* acc = static_cast(sender()); emit addContact(acc->getName(), jid, group, data); } -void Core::Squawk::onAccountAddGroup(const QString& name) { +void Core::Squawk::onAccountAddGroup(const QString& name) +{ Account* acc = static_cast(sender()); emit addGroup(acc->getName(), name); } -void Core::Squawk::onAccountRemoveGroup(const QString& name) { +void Core::Squawk::onAccountRemoveGroup(const QString& name) +{ Account* acc = static_cast(sender()); emit removeGroup(acc->getName(), name); } -void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap& data) { +void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap& data) +{ Account* acc = static_cast(sender()); emit changeContact(acc->getName(), jid, data); } -void Core::Squawk::onAccountRemoveContact(const QString& jid) { +void Core::Squawk::onAccountRemoveContact(const QString& jid) +{ Account* acc = static_cast(sender()); emit removeContact(acc->getName(), jid); } -void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) { +void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) +{ Account* acc = static_cast(sender()); emit removeContact(acc->getName(), jid, group); } -void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap& data) { - Account* acc = static_cast(sender()); - emit addPresence(acc->getName(), jid, name, data); - - //it's equal if a MUC sends its status with presence of the same jid (ex: muc@srv.im/muc@srv.im), it's not a client, so, no need to request - if (jid != name) { - const Shared::ClientId& id = data["client"].value(); - if (!id.valid()) - return; - - if (!clientCache.checkClient(id)) - acc->discoverInfo(jid + "/" + name, id.getId()); - } -} - -void Core::Squawk::onAccountInfoDiscovered( - const QString& address, - const QString& node, - const std::set& identities, - const std::set& features) +void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap& data) { Account* acc = static_cast(sender()); - - if (!clientCache.registerClientInfo(address, node, identities, features)) { - qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node; - } + emit addPresence(acc->getName(), jid, name, data); } -void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) { +void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) +{ Account* acc = static_cast(sender()); emit removePresence(acc->getName(), jid, name); } -void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) { +void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) +{ Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}}); } -void Core::Squawk::onAccountChanged(const QMap& data) { +void Core::Squawk::onAccountChanged(const QMap& data) +{ Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), data); } -void Core::Squawk::onAccountMessage(const Shared::Message& data) { +void Core::Squawk::onAccountMessage(const Shared::Message& data) +{ Account* acc = static_cast(sender()); emit accountMessage(acc->getName(), data); } -void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) { +void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to send a message with non existing account" << account << ", skipping"; @@ -359,17 +335,8 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } -void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) { - AccountsMap::const_iterator itr = amap.find(account); - if (itr == amap.end()) { - qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping"; - return; - } - - itr->second->replaceMessage(originalId, data); -} - -void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) { +void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping"; @@ -379,7 +346,8 @@ void Core::Squawk::resendMessage(const QString& account, const QString& jid, con itr->second->resendMessage(jid, id); } -void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) { +void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to request an archive of non existing account, skipping"); @@ -388,12 +356,14 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in itr->second->requestArchive(jid, count, before); } -void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list& list, bool last) { +void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list& list, bool last) +{ Account* acc = static_cast(sender()); emit responseArchive(acc->getName(), jid, list, last); } -void Core::Squawk::modifyAccountRequest(const QString& name, const QMap& map) { +void Core::Squawk::modifyAccountRequest(const QString& name, const QMap& map) +{ AccountsMap::const_iterator itr = amap.find(name); if (itr == amap.end()) { qDebug("An attempt to modify non existing account, skipping"); @@ -404,7 +374,6 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetState(); QMap::const_iterator mItr; bool needToReconnect = false; - bool wentReconnecting = false; mItr = map.find("login"); if (mItr != map.end()) { @@ -430,37 +399,34 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMaptoBool() == acc->getActive()) { - if (needToReconnect && st != Shared::ConnectionState::disconnected) { - acc->reconnect(); - wentReconnecting = true; - } - } else { - acc->setActive(mItr->toBool()); - activeChanged = true; + if (needToReconnect && st != Shared::ConnectionState::disconnected) { + acc->reconnect(); } mItr = map.find("login"); - if (mItr != map.end()) + if (mItr != map.end()) { acc->setLogin(mItr->toString()); + } mItr = map.find("password"); - if (mItr != map.end()) + if (mItr != map.end()) { acc->setPassword(mItr->toString()); + } mItr = map.find("resource"); - if (mItr != map.end()) + if (mItr != map.end()) { acc->setResource(mItr->toString()); + } mItr = map.find("server"); - if (mItr != map.end()) + if (mItr != map.end()) { acc->setServer(mItr->toString()); + } mItr = map.find("passwordType"); - if (mItr != map.end()) + if (mItr != map.end()) { acc->setPasswordType(Shared::Global::fromInt(mItr->toInt())); + } #ifdef WITH_KWALLET if (acc->getPasswordType() == Shared::AccountPassword::kwallet @@ -471,25 +437,17 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetActive()) - acc->connect(); - else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) - acc->connect(); - } - emit changeAccount(name, map); } -void Core::Squawk::onAccountError(const QString& text) { +void Core::Squawk::onAccountError(const QString& text) +{ Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"error", text}}); - - if (acc->getLastError() == Account::Error::authentication) - emit requestPassword(acc->getName(), true); } -void Core::Squawk::removeAccountRequest(const QString& name) { +void Core::Squawk::removeAccountRequest(const QString& name) +{ AccountsMap::const_iterator itr = amap.find(name); if (itr == amap.end()) { qDebug() << "An attempt to remove non existing account " << name << " from core, skipping"; @@ -497,8 +455,9 @@ void Core::Squawk::removeAccountRequest(const QString& name) { } Account* acc = itr->second; - if (acc->getState() != Shared::ConnectionState::disconnected) + if (acc->getState() != Shared::ConnectionState::disconnected) { acc->disconnect(); + } for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) { if (*aItr == acc) { @@ -518,7 +477,8 @@ void Core::Squawk::removeAccountRequest(const QString& name) { acc->deleteLater(); } -void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) { +void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to subscribe to the contact with non existing account, skipping"); @@ -528,7 +488,8 @@ void Core::Squawk::subscribeContact(const QString& account, const QString& jid, itr->second->subscribeToContact(jid, reason); } -void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) { +void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to subscribe to the contact with non existing account, skipping"); @@ -538,7 +499,8 @@ void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid itr->second->unsubscribeFromContact(jid, reason); } -void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) { +void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to remove contact from non existing account, skipping"); @@ -548,7 +510,8 @@ void Core::Squawk::removeContactRequest(const QString& account, const QString& j itr->second->removeContactRequest(jid); } -void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups) { +void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to add contact to a non existing account, skipping"); @@ -558,22 +521,26 @@ void Core::Squawk::addContactRequest(const QString& account, const QString& jid, itr->second->addContactRequest(jid, name, groups); } -void Core::Squawk::onAccountAddRoom(const QString jid, const QMap& data) { +void Core::Squawk::onAccountAddRoom(const QString jid, const QMap& data) +{ Account* acc = static_cast(sender()); emit addRoom(acc->getName(), jid, data); } -void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap& data) { +void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap& data) +{ Account* acc = static_cast(sender()); emit changeRoom(acc->getName(), jid, data); } -void Core::Squawk::onAccountRemoveRoom(const QString jid) { +void Core::Squawk::onAccountRemoveRoom(const QString jid) +{ Account* acc = static_cast(sender()); emit removeRoom(acc->getName(), jid); } -void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) { +void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping"; @@ -582,7 +549,8 @@ void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, boo itr->second->setRoomJoined(jid, joined); } -void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) { +void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping"; @@ -591,36 +559,32 @@ void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, b itr->second->setRoomAutoJoin(jid, joined); } -void Core::Squawk::setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value) { - AccountsMap::const_iterator itr = amap.find(account); - if (itr == amap.end()) { - qDebug() << "An attempt to set encryption to the contact" << jid << "of non existing account" << account << ", skipping"; - return; - } - itr->second->setContactEncryption(jid, value); -} - -void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap& data) { +void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap& data) +{ Account* acc = static_cast(sender()); emit addRoomParticipant(acc->getName(), jid, nick, data); } -void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data) { +void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data) +{ Account* acc = static_cast(sender()); emit changeRoomParticipant(acc->getName(), jid, nick, data); } -void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) { +void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) +{ Account* acc = static_cast(sender()); emit removeRoomParticipant(acc->getName(), jid, nick); } -void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data) { +void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data) +{ Account* acc = static_cast(sender()); emit changeMessage(acc->getName(), jid, id, data); } -void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) { +void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping"; @@ -629,7 +593,8 @@ void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) itr->second->removeRoomRequest(jid); } -void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) { +void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping"; @@ -638,11 +603,13 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co itr->second->addRoomRequest(jid, nick, password, autoJoin); } -void Core::Squawk::fileDownloadRequest(const QString& url) { +void Core::Squawk::fileDownloadRequest(const QString& url) +{ network.downladFile(url); } -void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) { +void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; @@ -651,7 +618,8 @@ void Core::Squawk::addContactToGroupRequest(const QString& account, const QStrin itr->second->addContactToGroupRequest(jid, groupName); } -void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) { +void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; @@ -660,7 +628,8 @@ void Core::Squawk::removeContactFromGroupRequest(const QString& account, const Q itr->second->removeContactFromGroupRequest(jid, groupName); } -void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) { +void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping"; @@ -669,106 +638,138 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j itr->second->renameContactRequest(jid, newName); } -void Core::Squawk::requestInfo(const QString& account, const QString& jid) { +void Core::Squawk::requestVCard(const QString& account, const QString& jid) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to request info about" << jid << "of non existing account" << account << ", skipping"; + qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping"; return; } - itr->second->requestInfo(jid); + itr->second->requestVCard(jid); } -void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) { +void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to update info to non existing account" << account << ", skipping"; + qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping"; return; } - itr->second->updateInfo(info); + itr->second->uploadVCard(card); } -void Core::Squawk::readSettings() { - QSettings settings; - settings.beginGroup("core"); - int size = settings.beginReadArray("accounts"); - for (int i = 0; i < size; ++i) { - settings.setArrayIndex(i); - Shared::AccountPassword passwordType = - Shared::Global::fromInt( - settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt() - ); - - QString name = settings.value("name").toString(); - QString password = settings.value("password", "").toString(); - if (passwordType == Shared::AccountPassword::jammed) - password = Jammer::decrypt(password, passwordHash); - - addAccount( - settings.value("login").toString(), - settings.value("server").toString(), - password, - name, - settings.value("resource").toString(), - settings.value("active").toBool(), - passwordType - ); - } - settings.endArray(); - settings.endGroup(); - - qDebug() << "Squawk core is ready"; - emit ready(); -} - -void Core::Squawk::onAccountNeedPassword() { - Account* acc = static_cast(sender()); - switch (acc->getPasswordType()) { - case Shared::AccountPassword::alwaysAsk: - emit requestPassword(acc->getName(), false); - break; - case Shared::AccountPassword::kwallet: { -#ifdef WITH_KWALLET - if (kwallet.supportState() == PSE::KWallet::success) { - kwallet.requestReadPassword(acc->getName()); - } else { -#endif - emit requestPassword(acc->getName(), false); -#ifdef WITH_KWALLET - } -#endif - break; - } - default: - break; //should never happen; - } -} - -void Core::Squawk::onWalletRejectPassword(const QString& login) { - emit requestPassword(login, false); -} - -void Core::Squawk::responsePassword(const QString& account, const QString& password) { +void Core::Squawk::responsePassword(const QString& account, const QString& password) +{ AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set password to non existing account" << account << ", skipping"; return; } - Account* acc = itr->second; - acc->setPassword(password); + itr->second->setPassword(password); emit changeAccount(account, {{"password", password}}); - if (state != Shared::Availability::offline && acc->getActive()) - acc->connect(); + accountReady(); } -void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) { +void Core::Squawk::readSettings() +{ + QSettings settings; + settings.beginGroup("core"); + int size = settings.beginReadArray("accounts"); + waitingForAccounts = size; + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + parseAccount( + settings.value("login").toString(), + settings.value("server").toString(), + settings.value("password", "").toString(), + settings.value("name").toString(), + settings.value("resource").toString(), + Shared::Global::fromInt(settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt()) + ); + } + settings.endArray(); + settings.endGroup(); +} + +void Core::Squawk::accountReady() +{ + --waitingForAccounts; + + if (waitingForAccounts == 0) { + emit ready(); + } +} + +void Core::Squawk::parseAccount( + const QString& login, + const QString& server, + const QString& password, + const QString& name, + const QString& resource, + Shared::AccountPassword passwordType +) +{ + switch (passwordType) { + case Shared::AccountPassword::plain: + addAccount(login, server, password, name, resource, passwordType); + accountReady(); + break; + case Shared::AccountPassword::jammed: { + SimpleCrypt crypto(passwordHash); + QString decrypted = crypto.decryptToString(password); + addAccount(login, server, decrypted, name, resource, passwordType); + accountReady(); + } + break; + case Shared::AccountPassword::alwaysAsk: + addAccount(login, server, QString(), name, resource, passwordType); + emit requestPassword(name); + break; + case Shared::AccountPassword::kwallet: { + addAccount(login, server, QString(), name, resource, passwordType); +#ifdef WITH_KWALLET + if (kwallet.supportState() == PSE::KWallet::success) { + kwallet.requestReadPassword(name); + } else { +#endif + emit requestPassword(name); +#ifdef WITH_KWALLET + } +#endif + } + } +} + +void Core::Squawk::onWalletRejectPassword(const QString& login) +{ + emit requestPassword(login); +} + +void Core::Squawk::onWalletResponsePassword(const QString& login, const QString& password) +{ + AccountsMap::const_iterator itr = amap.find(login); + if (itr == amap.end()) { + qDebug() << "An attempt to set password to non existing account" << login << ", skipping"; + return; + } + itr->second->setPassword(password); + emit changeAccount(login, {{"password", password}}); + accountReady(); +} + +void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) +{ Account* acc = static_cast(sender()); emit fileError({{acc->getName(), jid, id}}, errorText, true); } -void Core::Squawk::onLocalPathInvalid(const QString& path) { +void Core::Squawk::onLocalPathInvalid(const QString& path) +{ std::list list = network.reportPathInvalid(path); - QMap data({{"attachPath", ""}}); + QMap data({ + {"attachPath", ""} + }); for (const Shared::MessageInfo& info : list) { AccountsMap::const_iterator itr = amap.find(info.account); if (itr != amap.end()) { @@ -778,8 +779,3 @@ void Core::Squawk::onLocalPathInvalid(const QString& path) { } } } - -void Core::Squawk::changeDownloadsPath(const QString& path) { - network.moveFilesDirectory(path); -} - diff --git a/core/squawk.h b/core/squawk.h index 983f5ab..338eb40 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -31,18 +31,17 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/global.h" -#include "shared/info.h" -#include "shared/clientinfo.h" - -#include -#include +#include "networkaccess.h" +#include "external/simpleCrypt/simplecrypt.h" #ifdef WITH_KWALLET #include "passwordStorageEngines/kwallet.h" #endif -namespace Core { -class Squawk : public QObject { +namespace Core +{ +class Squawk : public QObject +{ Q_OBJECT public: @@ -83,11 +82,11 @@ signals: void fileError(const std::list msgs, const QString& error, bool up); void fileProgress(const std::list msgs, qreal value, bool up); void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& path); - void responseInfo(const Shared::Info& info); + void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); - void requestPassword(const QString& account, bool authernticationError); + void requestPassword(const QString& account); public slots: void start(); @@ -102,7 +101,6 @@ public slots: void changeState(Shared::Availability state); void sendMessage(const QString& account, const Shared::Message& data); - void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); void resendMessage(const QString& account, const QString& jid, const QString& id); void requestArchive(const QString& account, const QString& jid, int count, const QString& before); @@ -113,7 +111,6 @@ public slots: void removeContactRequest(const QString& account, const QString& jid); void renameContactRequest(const QString& account, const QString& jid, const QString& newName); void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups); - void setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value); void setRoomJoined(const QString& account, const QString& jid, bool joined); void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); @@ -122,11 +119,10 @@ public slots: void fileDownloadRequest(const QString& url); - void requestInfo(const QString& account, const QString& jid); - void updateInfo(const QString& account, const Shared::Info& info); + void requestVCard(const QString& account, const QString& jid); + void uploadVCard(const QString& account, const Shared::VCard& card); void responsePassword(const QString& account, const QString& password); void onLocalPathInvalid(const QString& path); - void changeDownloadsPath(const QString& path); private: typedef std::deque Accounts; @@ -136,13 +132,11 @@ private: AccountsMap amap; Shared::Availability state; NetworkAccess network; - bool isInitialized; + uint8_t waitingForAccounts; #ifdef WITH_KWALLET PSE::KWallet kwallet; #endif - - ClientCache clientCache; private slots: void addAccount( @@ -151,7 +145,6 @@ private slots: const QString& password, const QString& name, const QString& resource, - bool active, Shared::AccountPassword passwordType ); @@ -176,17 +169,24 @@ private slots: void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data); void onAccountRemoveRoomPresence(const QString& jid, const QString& nick); void onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data); - void onAccountNeedPassword(); void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText); void onWalletOpened(bool success); + void onWalletResponsePassword(const QString& login, const QString& password); void onWalletRejectPassword(const QString& login); - - void onAccountInfoDiscovered(const QString& address, const QString& node, const std::set& identities, const std::set& features); private: void readSettings(); + void accountReady(); + void parseAccount( + const QString& login, + const QString& server, + const QString& password, + const QString& name, + const QString& resource, + Shared::AccountPassword passwordType + ); static const quint64 passwordHash = 0x08d054225ac4871d; }; diff --git a/core/storage.cpp b/core/storage.cpp new file mode 100644 index 0000000..7ff0ef7 --- /dev/null +++ b/core/storage.cpp @@ -0,0 +1,184 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "storage.h" + +Core::Storage::Storage(const QString& p_name): + name(p_name), + opened(false), + environment(), + base() +{ +} + +Core::Storage::~Storage() +{ + close(); +} + +void Core::Storage::open() +{ + if (!opened) { + mdb_env_create(&environment); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + name; + QDir cache(path); + + if (!cache.exists()) { + bool res = cache.mkpath(path); + if (!res) { + throw Archive::Directory(path.toStdString()); + } + } + + mdb_env_set_maxdbs(environment, 1); + mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL); + mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + mdb_dbi_open(txn, "base", MDB_CREATE, &base); + mdb_txn_commit(txn); + opened = true; + } +} + +void Core::Storage::close() +{ + if (opened) { + mdb_dbi_close(environment, base); + mdb_env_close(environment); + opened = false; + } +} + +void Core::Storage::addRecord(const QString& key, const QString& value) +{ + if (!opened) { + throw Archive::Closed("addRecord", name.toStdString()); + } + const std::string& id = key.toStdString(); + const std::string& val = value.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = val.size(); + lmdbData.mv_data = (char*)val.c_str(); + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + int rc; + rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); + if (rc != 0) { + mdb_txn_abort(txn); + if (rc == MDB_KEYEXIST) { + throw Archive::Exist(name.toStdString(), id); + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } else { + mdb_txn_commit(txn); + } +} + +void Core::Storage::changeRecord(const QString& key, const QString& value) +{ + if (!opened) { + throw Archive::Closed("changeRecord", name.toStdString()); + } + const std::string& id = key.toStdString(); + const std::string& val = value.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = val.size(); + lmdbData.mv_data = (char*)val.c_str(); + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + int rc; + rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + mdb_txn_abort(txn); + if (rc) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } else { + mdb_txn_commit(txn); + } +} + +QString Core::Storage::getRecord(const QString& key) const +{ + if (!opened) { + throw Archive::Closed("addElement", name.toStdString()); + } + const std::string& id = key.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + MDB_txn *txn; + int rc; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + rc = mdb_get(txn, base, &lmdbKey, &lmdbData); + if (rc) { + mdb_txn_abort(txn); + if (rc == MDB_NOTFOUND) { + throw Archive::NotFound(id, name.toStdString()); + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } else { + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + QString value(sId.c_str()); + mdb_txn_abort(txn); + return value; + } +} + +void Core::Storage::removeRecord(const QString& key) +{ + if (!opened) { + throw Archive::Closed("addElement", name.toStdString()); + } + const std::string& id = key.toStdString(); + + MDB_val lmdbKey; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + MDB_txn *txn; + int rc; + mdb_txn_begin(environment, NULL, 0, &txn); + rc = mdb_del(txn, base, &lmdbKey, NULL); + if (rc) { + mdb_txn_abort(txn); + if (rc == MDB_NOTFOUND) { + throw Archive::NotFound(id, name.toStdString()); + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } else { + mdb_txn_commit(txn); + } +} diff --git a/ui/widgets/settings/settingslist.h b/core/storage.h similarity index 53% rename from ui/widgets/settings/settingslist.h rename to core/storage.h index 14ebf55..d2abfde 100644 --- a/ui/widgets/settings/settingslist.h +++ b/core/storage.h @@ -1,43 +1,56 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#pragma once +#ifndef CORE_STORAGE_H +#define CORE_STORAGE_H -#include -#include -#include +#include +#include -class SettingsList : public QListWidget { - Q_OBJECT +#include "archive.h" + +namespace Core { + +/** + * @todo write docs + */ +class Storage +{ public: - SettingsList(QWidget* parent = nullptr); - ~SettingsList(); - -protected: -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - void initViewItemOption(QStyleOptionViewItem* option) const override; -#else - QStyleOptionViewItem viewOptions() const override; -#endif - - void resizeEvent(QResizeEvent * event) override; - QRect visualRect(const QModelIndex & index) const override; - + Storage(const QString& name); + ~Storage(); + + void open(); + void close(); + + void addRecord(const QString& key, const QString& value); + void changeRecord(const QString& key, const QString& value); + void removeRecord(const QString& key); + QString getRecord(const QString& key) const; + + private: - int lastWidth; + QString name; + bool opened; + MDB_env* environment; + MDB_dbi base; }; + +} + +#endif // CORE_STORAGE_H diff --git a/core/urlstorage.cpp b/core/urlstorage.cpp new file mode 100644 index 0000000..f59ff62 --- /dev/null +++ b/core/urlstorage.cpp @@ -0,0 +1,491 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "urlstorage.h" + +Core::UrlStorage::UrlStorage(const QString& p_name): + name(p_name), + opened(false), + environment(), + base(), + map() +{ +} + +Core::UrlStorage::~UrlStorage() +{ + close(); +} + +void Core::UrlStorage::open() +{ + if (!opened) { + mdb_env_create(&environment); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + name; + QDir cache(path); + + if (!cache.exists()) { + bool res = cache.mkpath(path); + if (!res) { + throw Archive::Directory(path.toStdString()); + } + } + + mdb_env_set_maxdbs(environment, 2); + mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL); + mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + mdb_dbi_open(txn, "base", MDB_CREATE, &base); + mdb_dbi_open(txn, "map", MDB_CREATE, &map); + mdb_txn_commit(txn); + opened = true; + } +} + +void Core::UrlStorage::close() +{ + if (opened) { + mdb_dbi_close(environment, map); + mdb_dbi_close(environment, base); + mdb_env_close(environment); + opened = false; + } +} + +void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) +{ + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + + try { + writeInfo(key, info, txn, overwrite); + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } +} + +void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite) +{ + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + info.serialize(ds); + + const std::string& id = key.toStdString(); + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); + + int rc; + rc = mdb_put(txn, base, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE); + + if (rc != 0) { + if (rc == MDB_KEYEXIST) { + if (!overwrite) { + throw Archive::Exist(name.toStdString(), id); + } + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } + + if (info.hasPath()) { + std::string sp = info.getPath().toStdString(); + lmdbData.mv_size = sp.size(); + lmdbData.mv_data = (char*)sp.c_str(); + rc = mdb_put(txn, map, &lmdbData, &lmdbKey, 0); + if (rc != 0) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } +} + +void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info, MDB_txn* txn) +{ + const std::string& id = key.toStdString(); + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + int rc = mdb_get(txn, base, &lmdbKey, &lmdbData); + + if (rc == 0) { + QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + + info.deserialize(ds); + } else if (rc == MDB_NOTFOUND) { + throw Archive::NotFound(id, name.toStdString()); + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } +} + +void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info) +{ + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + try { + readInfo(key, info, txn); + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } +} + +void Core::UrlStorage::addFile(const QString& url) +{ + if (!opened) { + throw Archive::Closed("addFile(no message, no path)", name.toStdString()); + } + + addToInfo(url, "", "", ""); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& path) +{ + if (!opened) { + throw Archive::Closed("addFile(no message, with path)", name.toStdString()); + } + + addToInfo(url, "", "", "", path); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) +{ + if (!opened) { + throw Archive::Closed("addFile(with message, no path)", name.toStdString()); + } + + addToInfo(url, account, jid, id); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) +{ + if (!opened) { + throw Archive::Closed("addFile(with message, with path)", name.toStdString()); + } + + addToInfo(url, account, jid, id, path); +} + +void Core::UrlStorage::addFile(const std::list& msgs, const QString& url, const QString& path) +{ + if (!opened) { + throw Archive::Closed("addFile(with list)", name.toStdString()); + } + + UrlInfo info (path, msgs); + writeInfo(url, info, true);; +} + +QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) +{ + if (!opened) { + throw Archive::Closed("addMessageAndCheckForPath", name.toStdString()); + } + + return addToInfo(url, account, jid, id).getPath(); +} + +Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path) +{ + UrlInfo info; + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + + try { + readInfo(url, info, txn); + } catch (const Archive::NotFound& e) { + + } catch (...) { + mdb_txn_abort(txn); + throw; + } + + bool pathChange = false; + bool listChange = false; + if (path != "-s") { + if (info.getPath() != path) { + info.setPath(path); + pathChange = true; + } + } + + if (account.size() > 0 && jid.size() > 0 && id.size() > 0) { + listChange = info.addMessage(account, jid, id); + } + + if (pathChange || listChange) { + try { + writeInfo(url, info, txn, true); + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } + } else { + mdb_txn_abort(txn); + } + + return info; +} + +std::list Core::UrlStorage::setPath(const QString& url, const QString& path) +{ + std::list list; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + UrlInfo info; + + try { + readInfo(url, info, txn); + info.getMessages(list); + } catch (const Archive::NotFound& e) { + } catch (...) { + mdb_txn_abort(txn); + throw; + } + + info.setPath(path); + try { + writeInfo(url, info, txn, true); + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } + + return list; +} + +std::list Core::UrlStorage::removeFile(const QString& url) +{ + std::list list; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + UrlInfo info; + + try { + std::string id = url.toStdString(); + readInfo(url, info, txn); + info.getMessages(list); + + MDB_val lmdbKey; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + int rc = mdb_del(txn, base, &lmdbKey, NULL); + if (rc != 0) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + + if (info.hasPath()) { + std::string path = info.getPath().toStdString(); + lmdbKey.mv_size = path.size(); + lmdbKey.mv_data = (char*)path.c_str(); + + int rc = mdb_del(txn, map, &lmdbKey, NULL); + if (rc != 0) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } + + return list; +} + +std::list Core::UrlStorage::deletedFile(const QString& path) +{ + std::list list; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + + try { + std::string spath = path.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = spath.size(); + lmdbKey.mv_data = (char*)spath.c_str(); + + QString url; + int rc = mdb_get(txn, map, &lmdbKey, &lmdbData); + + if (rc == 0) { + std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size); + url = QString(surl.c_str()); + } else if (rc == MDB_NOTFOUND) { + qDebug() << "Have been asked to remove file" << path << ", which isn't in the database, skipping"; + mdb_txn_abort(txn); + return list; + } else { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + + UrlInfo info; + std::string id = url.toStdString(); + readInfo(url, info, txn); + info.getMessages(list); + info.setPath(QString()); + writeInfo(url, info, txn, true); + + rc = mdb_del(txn, map, &lmdbKey, NULL); + if (rc != 0) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + + mdb_txn_commit(txn); + } catch (...) { + mdb_txn_abort(txn); + throw; + } + + return list; +} + + +QString Core::UrlStorage::getUrl(const QString& path) +{ + std::list list; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + + std::string spath = path.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = spath.size(); + lmdbKey.mv_data = (char*)spath.c_str(); + + QString url; + int rc = mdb_get(txn, map, &lmdbKey, &lmdbData); + + if (rc == 0) { + std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size); + url = QString(surl.c_str()); + + mdb_txn_abort(txn); + return url; + } else if (rc == MDB_NOTFOUND) { + mdb_txn_abort(txn); + throw Archive::NotFound(spath, name.toStdString()); + } else { + mdb_txn_abort(txn); + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } +} + +std::pair> Core::UrlStorage::getPath(const QString& url) +{ + UrlInfo info; + readInfo(url, info); + std::list container; + info.getMessages(container); + return std::make_pair(info.getPath(), container); +} + +Core::UrlStorage::UrlInfo::UrlInfo(): + localPath(), + messages() {} + +Core::UrlStorage::UrlInfo::UrlInfo(const QString& path): + localPath(path), + messages() {} + +Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list& msgs): + localPath(path), + messages(msgs) {} + +Core::UrlStorage::UrlInfo::~UrlInfo() {} + +bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) +{ + for (const Shared::MessageInfo& info : messages) { + if (info.account == acc && info.jid == jid && info.messageId == id) { + return false; + } + } + messages.emplace_back(acc, jid, id); + return true; +} + +void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const +{ + data << localPath; + std::list::size_type size = messages.size(); + data << quint32(size); + for (const Shared::MessageInfo& info : messages) { + data << info.account; + data << info.jid; + data << info.messageId; + } +} + +void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) +{ + data >> localPath; + quint32 size; + data >> size; + for (quint32 i = 0; i < size; ++i) { + messages.emplace_back(); + Shared::MessageInfo& info = messages.back(); + data >> info.account; + data >> info.jid; + data >> info.messageId; + } +} + +void Core::UrlStorage::UrlInfo::getMessages(std::list& container) const +{ + for (const Shared::MessageInfo& info : messages) { + container.emplace_back(info); + } +} + +QString Core::UrlStorage::UrlInfo::getPath() const +{ + return localPath; +} + +bool Core::UrlStorage::UrlInfo::hasPath() const +{ + return localPath.size() > 0; +} + + +void Core::UrlStorage::UrlInfo::setPath(const QString& path) +{ + localPath = path; +} diff --git a/core/components/urlstorage.h b/core/urlstorage.h similarity index 85% rename from core/components/urlstorage.h rename to core/urlstorage.h index 6fb536a..3dc5c21 100644 --- a/core/components/urlstorage.h +++ b/core/urlstorage.h @@ -16,23 +16,25 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_URLSTORAGE_H +#define CORE_URLSTORAGE_H #include #include - +#include #include -#include - +#include "archive.h" #include namespace Core { -class UrlStorage { -public: +/** + * @todo write docs + */ +class UrlStorage +{ class UrlInfo; - public: UrlStorage(const QString& name); ~UrlStorage(); @@ -53,16 +55,20 @@ public: std::pair> getPath(const QString& url); private: - LMDBAL::Base base; - LMDBAL::Storage* urlToInfo; - LMDBAL::Storage* pathToUrl; + QString name; + bool opened; + MDB_env* environment; + MDB_dbi base; + MDB_dbi map; private: void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false); - void writeInfo(const QString& key, const UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite = false); + void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false); + void readInfo(const QString& key, UrlInfo& info); + void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn); UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s"); -public: +private: class UrlInfo { public: UrlInfo(const QString& path); @@ -90,5 +96,4 @@ public: } -QDataStream& operator >> (QDataStream &in, Core::UrlStorage::UrlInfo& info); -QDataStream& operator << (QDataStream &out, const Core::UrlStorage::UrlInfo& info); +#endif // CORE_URLSTORAGE_H diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt deleted file mode 100644 index e030130..0000000 --- a/core/utils/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(SOURCE_FILES - jammer.cpp -) - -set(HEADER_FILES - jammer.h -) - -target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/utils/jammer.cpp b/core/utils/jammer.cpp deleted file mode 100644 index 1714c6b..0000000 --- a/core/utils/jammer.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "jammer.h" - -#include - -#include -#include - -struct CipherCtxDeleter { - void operator()(EVP_CIPHER_CTX* ctx) const { - EVP_CIPHER_CTX_free(ctx); - } -}; -typedef std::unique_ptr CipherCtx; - -QString Core::Jammer::encrypt(const QString& plaintext, qint64 key) { - QByteArray encryptedData = process(plaintext.toUtf8(), intToKey(key), true); - - return QString::fromUtf8(encryptedData.toHex()); -} - -QString Core::Jammer::decrypt(const QString& ciphertext, qint64 key) { - QByteArray encryptedData = QByteArray::fromHex(ciphertext.toUtf8()); - QByteArray decryptedData = process(encryptedData, intToKey(key), false); - - return QString::fromUtf8(decryptedData); -} - -std::string Core::Jammer::getOpenSSLErrorString() { - unsigned long errCode = ERR_get_error(); - if (errCode == 0) { - return "No OpenSSL error"; - } - const char *errMsg = ERR_reason_error_string(errCode); - return errMsg ? std::string(errMsg) : "Unknown OpenSSL error"; -} - -QByteArray Core::Jammer::process(const QByteArray& input, const QByteArray& key, bool encrypt) { - CipherCtx ctx(EVP_CIPHER_CTX_new()); - if (!ctx) - throw std::runtime_error("Failed to create password jammer context"); - - QByteArray output(input.size() + 16, 0); - int outputLength = 0; - int finalLength = 0; - - if (!ctx) - throw std::runtime_error("Failed to create EVP_CIPHER_CTX: " + getOpenSSLErrorString()); - - if (EVP_CipherInit_ex(ctx.get(), EVP_chacha20(), nullptr, toUChar(key), nullptr, encrypt) != 1) - throw std::runtime_error("EVP_CipherInit_ex failed. " + getOpenSSLErrorString()); - - if (EVP_CipherUpdate(ctx.get(), toUChar(output), &outputLength, toUChar(input), input.size()) != 1) - throw std::runtime_error("EVP_CipherUpdate failed. " + getOpenSSLErrorString()); - - if (EVP_CipherFinal_ex(ctx.get(), toUChar(output) + outputLength, &finalLength) != 1) - throw std::runtime_error("EVP_CipherFinal_ex failed. " + getOpenSSLErrorString()); - - output.resize(outputLength + finalLength); - - return output; -} - -QByteArray Core::Jammer::intToKey(qint64 key, int keySize) { - QByteArray keyBytes(reinterpret_cast(&key), sizeof(key)); - while (keyBytes.size() < keySize) - keyBytes.append(keyBytes); - - keyBytes.truncate(keySize); - return keyBytes; -} - -unsigned char* Core::Jammer::toUChar(QByteArray& data) { - return reinterpret_cast(data.data());} - -const unsigned char* Core::Jammer::toUChar(const QByteArray& data) { - return reinterpret_cast(data.constData());} diff --git a/core/utils/jammer.h b/core/utils/jammer.h deleted file mode 100644 index 456c14b..0000000 --- a/core/utils/jammer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include -#include - -namespace Core { - -class Jammer { -public: - static QString encrypt(const QString& plaintext, qint64 key); - static QString decrypt(const QString& ciphertext, qint64 key); - -private: - Jammer() = delete; - - static QByteArray process(const QByteArray& input, const QByteArray& key, bool encrypt); - static QByteArray intToKey(qint64 key, int keySize = 32); - - static unsigned char* toUChar(QByteArray& data); - static const unsigned char* toUChar(const QByteArray& data); - static std::string getOpenSSLErrorString(); -}; - -} diff --git a/external/lmdbal b/external/lmdbal deleted file mode 160000 index 3ae1fd1..0000000 --- a/external/lmdbal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3ae1fd15c0f4f753227d6fd5bafa4968c7310b92 diff --git a/external/qxmpp b/external/qxmpp index 0cd7379..fe83e9c 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit 0cd7379bd78aa01af7e84f2fad6269ef0c0ba49c +Subproject commit fe83e9c3d42c3becf682e2b5ecfc9d77b24c614f diff --git a/external/simpleCrypt/CMakeLists.txt b/external/simpleCrypt/CMakeLists.txt new file mode 100644 index 0000000..274d304 --- /dev/null +++ b/external/simpleCrypt/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.0) +project(simplecrypt LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +find_package(Qt5 COMPONENTS Core REQUIRED) + +add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h) + +target_link_libraries(simpleCrypt Qt5::Core) diff --git a/external/simpleCrypt/simplecrypt.cpp b/external/simpleCrypt/simplecrypt.cpp new file mode 100644 index 0000000..19a96be --- /dev/null +++ b/external/simpleCrypt/simplecrypt.cpp @@ -0,0 +1,248 @@ +/* + Copyright (c) 2011, Andre Somers + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "simplecrypt.h" +#include +#include +#include +#include +#include +#include + +SimpleCrypt::SimpleCrypt(): +m_key(0), +m_compressionMode(CompressionAuto), +m_protectionMode(ProtectionChecksum), +m_lastError(ErrorNoError) {} + +SimpleCrypt::SimpleCrypt(quint64 key): +m_key(key), +m_compressionMode(CompressionAuto), +m_protectionMode(ProtectionChecksum), +m_lastError(ErrorNoError) +{ + splitKey(); +} + +void SimpleCrypt::setKey(quint64 key) +{ + m_key = key; + splitKey(); +} + +void SimpleCrypt::splitKey() +{ + m_keyParts.clear(); + m_keyParts.resize(8); + for (int i=0;i<8;i++) { + quint64 part = m_key; + for (int j=i; j>0; j--) + part = part >> 8; + part = part & 0xff; + m_keyParts[i] = static_cast(part); + } +} + +QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) +{ + QByteArray plaintextArray = plaintext.toUtf8(); + return encryptToByteArray(plaintextArray); +} + +QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) +{ + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + + QByteArray ba = plaintext; + + CryptoFlags flags = CryptoFlagNone; + if (m_compressionMode == CompressionAlways) { + ba = qCompress(ba, 9); //maximum compression + flags |= CryptoFlagCompression; + } else if (m_compressionMode == CompressionAuto) { + QByteArray compressed = qCompress(ba, 9); + if (compressed.count() < ba.count()) { + ba = compressed; + flags |= CryptoFlagCompression; + } + } + + QByteArray integrityProtection; + if (m_protectionMode == ProtectionChecksum) { + flags |= CryptoFlagChecksum; + QDataStream s(&integrityProtection, QIODevice::WriteOnly); + s << qChecksum(ba.constData(), ba.length()); + } else if (m_protectionMode == ProtectionHash) { + flags |= CryptoFlagHash; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + + integrityProtection += hash.result(); + } + + //prepend a random char to the string + char randomChar = char(QRandomGenerator::global()->generate() & 0xFF); + ba = randomChar + integrityProtection + ba; + + int pos(0); + char lastChar(0); + + int cnt = ba.count(); + + while (pos < cnt) { + ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; + lastChar = ba.at(pos); + ++pos; + } + + QByteArray resultArray; + resultArray.append(char(0x03)); //version for future updates to algorithm + resultArray.append(char(flags)); //encryption flags + resultArray.append(ba); + + m_lastError = ErrorNoError; + return resultArray; +} + +QString SimpleCrypt::encryptToString(const QString& plaintext) +{ + QByteArray plaintextArray = plaintext.toUtf8(); + QByteArray cypher = encryptToByteArray(plaintextArray); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::encryptToString(QByteArray plaintext) +{ + QByteArray cypher = encryptToByteArray(plaintext); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::decryptToString(const QString &cyphertext) +{ + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray plaintextArray = decryptToByteArray(cyphertextArray); + QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); + + return plaintext; +} + +QString SimpleCrypt::decryptToString(QByteArray cypher) +{ + QByteArray ba = decryptToByteArray(cypher); + QString plaintext = QString::fromUtf8(ba, ba.size()); + + return plaintext; +} + +QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) +{ + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray ba = decryptToByteArray(cyphertextArray); + + return ba; +} + +QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) +{ + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = cypher; + + if( cypher.count() < 3 ) + return QByteArray(); + + char version = ba.at(0); + + if (version !=3) { //we only work with version 3 + m_lastError = ErrorUnknownVersion; + qWarning() << "Invalid version or not a cyphertext."; + return QByteArray(); + } + + CryptoFlags flags = CryptoFlags(ba.at(1)); + + ba = ba.mid(2); + int pos(0); + int cnt(ba.count()); + char lastChar = 0; + + while (pos < cnt) { + char currentChar = ba[pos]; + ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); + lastChar = currentChar; + ++pos; + } + + ba = ba.mid(1); //chop off the random number at the start + + bool integrityOk(true); + if (flags.testFlag(CryptoFlagChecksum)) { + if (ba.length() < 2) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + quint16 storedChecksum; + { + QDataStream s(&ba, QIODevice::ReadOnly); + s >> storedChecksum; + } + ba = ba.mid(2); + quint16 checksum = qChecksum(ba.constData(), ba.length()); + integrityOk = (checksum == storedChecksum); + } else if (flags.testFlag(CryptoFlagHash)) { + if (ba.length() < 20) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + QByteArray storedHash = ba.left(20); + ba = ba.mid(20); + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityOk = (hash.result() == storedHash); + } + + if (!integrityOk) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + if (flags.testFlag(CryptoFlagCompression)) + ba = qUncompress(ba); + + m_lastError = ErrorNoError; + return ba; +} diff --git a/external/simpleCrypt/simplecrypt.h b/external/simpleCrypt/simplecrypt.h new file mode 100644 index 0000000..d75bdc2 --- /dev/null +++ b/external/simpleCrypt/simplecrypt.h @@ -0,0 +1,226 @@ +/* + Copyright (c) 2011, Andre Somers + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SIMPLECRYPT_H +#define SIMPLECRYPT_H +#include +#include +#include +#include + +/** + @ short Simple encrypt*ion and decryption of strings and byte arrays + + This class provides a simple implementation of encryption and decryption + of strings and byte arrays. + + @warning The encryption provided by this class is NOT strong encryption. It may + help to shield things from curious eyes, but it will NOT stand up to someone + determined to break the encryption. Don't say you were not warned. + + The class uses a 64 bit key. Simply create an instance of the class, set the key, + and use the encryptToString() method to calculate an encrypted version of the input string. + To decrypt that string again, use an instance of SimpleCrypt initialized with + the same key, and call the decryptToString() method with the encrypted string. If the key + matches, the decrypted version of the string will be returned again. + + If you do not provide a key, or if something else is wrong, the encryption and + decryption function will return an empty string or will return a string containing nonsense. + lastError() will return a value indicating if the method was succesful, and if not, why not. + + SimpleCrypt is prepared for the case that the encryption and decryption + algorithm is changed in a later version, by prepending a version identifier to the cypertext. + */ +class SimpleCrypt +{ +public: + /** + CompressionMode describes if compression will be applied to the data to be + encrypted. + */ + enum CompressionMode { + CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ + CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ + CompressionNever /*!< Never apply compression. */ + }; + /** + IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data + or wrong decryption keys. + + Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This + increases the length of the resulting cypertext, but makes it possible to check if the plaintext + appears to be valid after decryption. + */ + enum IntegrityProtectionMode { + ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ + ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ + ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ + }; + /** + Error describes t*he type of error that occured. + */ + enum Error { + ErrorNoError, /*!< No error occurred. */ + ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ + ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ + ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ + }; + + /** + Constructor. * + + Constructs a SimpleCrypt instance without a valid key set on it. + */ + SimpleCrypt(); + /** + Constructor. * + + Constructs a SimpleCrypt instance and initializes it with the given @arg key. + */ + explicit SimpleCrypt(quint64 key); + + /** + ( Re-) initializes* the key with the given @arg key. + */ + void setKey(quint64 key); + /** + Returns true if SimpleCrypt has been initialized with a key. + */ + bool hasKey() const {return !m_keyParts.isEmpty();} + + /** + Sets the compress*ion mode to use when encrypting data. The default mode is Auto. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} + /** + Returns the CompressionMode that is currently in use. + */ + CompressionMode compressionMode() const {return m_compressionMode;} + + /** + Sets the integrity mode to use when encrypting data. The default mode is Checksum. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} + /** + Returns the IntegrityProtectionMode that is currently in use. + */ + IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} + + /** + Returns the last *error that occurred. + */ + Error lastError() const {return m_lastError;} + + /** + Encrypts the @arg* plaintext string with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the string, so it can be stored easily in a text format. + */ + QString encryptToString(const QString& plaintext) ; + /** + Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the encryption, so it can be stored easily in a text format. + */ + QString encryptToString(QByteArray plaintext) ; + /** + Encrypts the @arg* plaintext string with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(const QString& plaintext) ; + /** + Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(QByteArray plaintext) ; + + /** + Decrypts a cypher*text string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(const QString& cyphertext) ; + /** + Decrypts a cypher*text string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(const QString& cyphertext) ; + /** + Decrypts a cypher*text binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(QByteArray cypher) ; + /** + Decrypts a cypher*text binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(QByteArray cypher) ; + + //enum to describe options that have been used for the encryption. Currently only one, but + //that only leaves room for future extensions like adding a cryptographic hash... + enum CryptoFlag{CryptoFlagNone = 0, + CryptoFlagCompression = 0x01, + CryptoFlagChecksum = 0x02, + CryptoFlagHash = 0x04 + }; + Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); +private: + + void splitKey(); + + quint64 m_key; + QVector m_keyParts; + CompressionMode m_compressionMode; + IntegrityProtectionMode m_protectionMode; + Error m_lastError; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) + +#endif // SimpleCrypt_H diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt deleted file mode 100644 index e92710f..0000000 --- a/main/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(SOURCE_FILES - main.cpp - application.cpp - dialogqueue.cpp - root.cpp -) - -set(HEADER_FILES - application.h - dialogqueue.h - root.h -) - -target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/main/application.cpp b/main/application.cpp deleted file mode 100644 index 15e9208..0000000 --- a/main/application.cpp +++ /dev/null @@ -1,666 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "application.h" - -Application::Application(Core::Squawk* p_core): - QObject(), - availability(Shared::Availability::offline), - core(p_core), - squawk(nullptr), - notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"), - roster(), - conversations(), - dialogueQueue(roster), - nowQuitting(false), - destroyingSquawk(false), - storage(), - trayIcon(nullptr), - actionQuit(Shared::icon("application-exit"), tr("Quit")), - actionToggle(QApplication::windowIcon(), tr("Minimize to tray")), - expandedPaths() -{ - connect(&actionQuit, &QAction::triggered, this, &Application::quit); - connect(&actionToggle, &QAction::triggered, this, &Application::toggleSquawk); - - connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify); - connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged); - connect(&roster, &Models::Roster::addedElement, this, &Application::onAddedElement); - - - //connecting myself to the backend - connect(this, &Application::changeState, core, &Core::Squawk::changeState); - connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined); - connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin); - connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact); - connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact); - connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage); - connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage); - connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage); - connect(this, &Application::setEncryption, core, &Core::Squawk::setContactEncryption); - connect(&roster, &Models::Roster::requestArchive, - std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3)); - - connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest); - connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword); - connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount); - - connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest); - connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid); - - - //coonecting backend to myself - connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged); - - connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage); - connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive); - connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage); - - connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount); - connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount); - connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount); - - connect(core, &Core::Squawk::addContact, &roster, &Models::Roster::addContact); - connect(core, &Core::Squawk::addGroup, this, &Application::addGroup); - connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup); - connect(core, qOverload(&Core::Squawk::removeContact), - &roster, qOverload(&Models::Roster::removeContact)); - connect(core, qOverload(&Core::Squawk::removeContact), - &roster, qOverload(&Models::Roster::removeContact)); - connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact); - connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence); - connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence); - - connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom); - connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom); - connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom); - connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant); - connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant); - connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant); - - - connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false)); - connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true)); - connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress); - connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError); - - connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword); - connect(core, &Core::Squawk::ready, this, &Application::readSettings); - - QDBusConnection sys = QDBusConnection::sessionBus(); - sys.connect( - "org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - "NotificationClosed", - this, - SLOT(onNotificationClosed(quint32, quint32)) - ); - sys.connect( - "org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - "ActionInvoked", - this, - SLOT(onNotificationInvoked(quint32, const QString&)) - ); -} - -Application::~Application() {} - -void Application::quit() { - if (!nowQuitting) { - nowQuitting = true; - emit quitting(); - - writeSettings(); - unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people - for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { - disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed); - itr->second->close(); - } - conversations.clear(); - dialogueQueue.quit(); - - if (squawk != nullptr) - squawk->close(); - - if (trayIcon != nullptr) { - trayIcon->deleteLater(); - trayIcon = nullptr; - } - - if (!destroyingSquawk) - checkForTheLastWindow(); - } -} - -void Application::checkForTheLastWindow() { - if (QApplication::topLevelWidgets().size() > 0) - emit readyToQuit(); - else - connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit); -} - -void Application::createMainWindow() { - if (squawk == nullptr) { - squawk = new Squawk(roster); - - connect(squawk, &Squawk::notify, this, &Application::notify); - connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription); - connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation); - connect(squawk, &Squawk::openConversation, this, &Application::openConversation); - connect(squawk, &Squawk::changeState, this, &Application::setState); - connect(squawk, &Squawk::changeTray, this, &Application::onChangeTray); - connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded); - connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed); - connect(squawk, &Squawk::quit, this, &Application::quit); - connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing); - - connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest); - connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest); - connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest); - connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount); - connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount); - - connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest); - connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest); - connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest); - connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest); - connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest); - connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest); - connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest); - connect(squawk, &Squawk::requestInfo, core, &Core::Squawk::requestInfo); - connect(squawk, &Squawk::updateInfo, core, &Core::Squawk::updateInfo); - connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath); - - connect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); - - dialogueQueue.setParentWidnow(squawk); - squawk->stateChanged(availability); - squawk->raise(); - squawk->show(); - squawk->activateWindow(); - - for (const std::list& entry : expandedPaths) { - QModelIndex ind = roster.getIndexByPath(entry); - if (ind.isValid()) - squawk->expand(ind); - } - - connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded); - connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed); - } -} - -void Application::onSquawkClosing() { - dialogueQueue.setParentWidnow(nullptr); - - if (!nowQuitting) { - disconnect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); - } - - destroyingSquawk = true; - squawk->deleteLater(); - squawk = nullptr; - - QSettings settings; - if (!nowQuitting && QSystemTrayIcon::isSystemTrayAvailable() && settings.value("tray", false).toBool()) { - if (settings.value("hideTray", false).toBool()) { - createTrayIcon(); - } - actionToggle.setText(tr("Show Squawk")); - } else { - quit(); - } -} - -void Application::onSquawkDestroyed() { - destroyingSquawk = false; - if (nowQuitting) - checkForTheLastWindow(); -} - -void Application::onChangeTray(bool enabled, bool hide) { - if (enabled) { - if (trayIcon == nullptr) { - if (!hide || squawk == nullptr) - createTrayIcon(); - } else { - if (hide && squawk != nullptr) { - trayIcon->deleteLater(); - trayIcon = nullptr; - } - } - } else if (trayIcon != nullptr) { - trayIcon->deleteLater(); - trayIcon = nullptr; - } -} - -void Application::createTrayIcon() { - trayIcon = new QSystemTrayIcon(); - - QMenu* trayIconMenu = new QMenu(); - trayIconMenu->addAction(&actionToggle); - trayIconMenu->addAction(&actionQuit); - - trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QApplication::windowIcon().pixmap(32, 32)); - trayIcon->setToolTip(QApplication::applicationDisplayName()); - - connect(trayIcon, &QSystemTrayIcon::activated, this, &Application::trayClicked); - connect(trayIcon, &QSystemTrayIcon::destroyed, trayIconMenu, &QMenu::deleteLater); - - trayIcon->show(); -} - -void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) { - switch (reason) { - case QSystemTrayIcon::Trigger: - case QSystemTrayIcon::DoubleClick: - case QSystemTrayIcon::MiddleClick: - toggleSquawk(); - break; - default: - break; - } -} - -void Application::toggleSquawk() { - QSettings settings; - if (squawk == nullptr) { - createMainWindow(); - if (settings.value("hideTray", false).toBool()) { - trayIcon->deleteLater(); - trayIcon = nullptr; - } - - actionToggle.setText(tr("Minimize to tray")); - } else { - squawk->close(); - } -} - -void Application::onItemCollapsed(const QModelIndex& index) { - std::list address = roster.getItemPath(index); - if (address.size() > 0) - expandedPaths.erase(address); -} - -void Application::onItemExpanded(const QModelIndex& index) { - std::list address = roster.getItemPath(index); - if (address.size() > 0) - expandedPaths.insert(address); -} - -void Application::onAddedElement(const std::list& path) { - if (squawk != nullptr && expandedPaths.count(path) > 0) { - QModelIndex index = roster.getIndexByPath(path); - if (index.isValid()) - squawk->expand(index); - } -} - -void Application::notify(const QString& account, const Shared::Message& msg) { - QString jid = msg.getPenPalJid(); - QString name = QString(roster.getContactName(account, jid)); - QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource())); - QVariantList args; - args << QString(); - - uint32_t notificationId = qHash(msg.getId()); - args << notificationId; - if (path.size() > 0) - args << path; - else - args << QString("mail-message"); //TODO should here better be unknown user icon? - - if (msg.getType() == Shared::Message::groupChat) - args << msg.getFromResource() + tr(" from ") + name; - else - args << name; - - QString body(msg.getBody()); - QString oob(msg.getOutOfBandUrl()); - if (body == oob) { - body = tr("Attached file"); - } - - args << body; - args << QStringList({ - "markAsRead", tr("Mark as Read"), - "openConversation", tr("Open conversation") - }); - args << QVariantMap({ - {"desktop-entry", qApp->desktopFileName()}, - {"category", QString("message")}, - {"urgency", 1}, - // {"sound-file", "/path/to/macaw/squawk"}, - {"sound-name", QString("message-new-instant")} - }); - args << -1; - notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args); - - storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId()))); - - if (squawk != nullptr) - QApplication::alert(squawk); -} - -void Application::onNotificationClosed(quint32 id, quint32 reason) { - Notifications::const_iterator itr = storage.find(id); - if (itr != storage.end()) { - if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html) - //TODO may ba also mark as read? - } - if (reason != 1) { //just expired, can be activated again from history, so no removing for now - storage.erase(id); - qDebug() << "Notification" << id << "was closed"; - } - } -} - -void Application::onNotificationInvoked(quint32 id, const QString& action) { - qDebug() << "Notification" << id << action << "request"; - Notifications::const_iterator itr = storage.find(id); - if (itr != storage.end()) { - if (action == "markAsRead") - roster.markMessageAsRead(itr->second.first, itr->second.second); - else if (action == "openConversation") - focusConversation(itr->second.first, "", itr->second.second); - } -} - -void Application::unreadMessagesCountChanged(int count) { - QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update"); - signal << qApp->desktopFileName() + QLatin1String(".desktop"); - signal << QVariantMap ({ - {"count-visible", count != 0}, - {"count", count} - }); - QDBusConnection::sessionBus().send(signal); -} - -void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) { - if (squawk != nullptr) { - if (squawk->currentConversationId() != id) { - QModelIndex index = roster.getContactIndex(id.account, id.name, resource); - squawk->select(index); - } - - if (squawk->isMinimized()) - squawk->showNormal(); - else - squawk->show(); - - squawk->raise(); - squawk->activateWindow(); - } else { - openConversation(id, resource); - } - - SHARED_UNUSED(messageId); - //TODO focus messageId; -} - -void Application::setState(Shared::Availability p_availability) { - if (availability != p_availability) { - availability = p_availability; - emit changeState(availability); - } -} - -void Application::stateChanged(Shared::Availability state) { - availability = state; - if (squawk != nullptr) - squawk->stateChanged(state); -} - -void Application::readSettings() { - QSettings settings; - settings.beginGroup("ui"); - int avail; - if (settings.contains("availability")) - avail = settings.value("availability").toInt(); - else - avail = static_cast(Shared::Availability::online); - - settings.beginGroup("roster"); - QStringList entries = settings.allKeys(); - for (const QString& entry : entries) { - QStringList p = entry.split("/"); - if (p.last() == "expanded" && settings.value(entry, false).toBool()) { - p.pop_back(); - expandedPaths.emplace(p.begin(), p.end()); - } - } - - settings.endGroup(); - settings.endGroup(); - - setState(Shared::Global::fromInt(avail)); - createMainWindow(); - - if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) - createTrayIcon(); -} - -void Application::writeSettings() { - QSettings settings; - settings.beginGroup("ui"); - settings.setValue("availability", static_cast(availability)); - - settings.remove("roster"); - settings.beginGroup("roster"); - for (const std::list& address : expandedPaths) { - QString path = ""; - for (const QString& hop : address) - path += hop + "/"; - - path += "expanded"; - settings.setValue(path, true); - } - - settings.endGroup(); - settings.endGroup(); -} - -void Application::requestPassword(const QString& account, bool authenticationError) { - if (authenticationError) - dialogueQueue.addAction(account, DialogQueue::askCredentials); - else - dialogueQueue.addAction(account, DialogQueue::askPassword); -} - -void Application::onConversationClosed() { - Conversation* conv = static_cast(sender()); - Models::Roster::ElId id(conv->getAccount(), conv->getJid()); - Conversations::const_iterator itr = conversations.find(id); - if (itr != conversations.end()) - conversations.erase(itr); - - if (conv->isMuc) { - Room* room = static_cast(conv); - if (!room->autoJoined()) - emit setRoomJoined(id.account, id.name, false); - } -} - -void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) { - Models::Item::Type type = roster.getContactType(id); - - switch (type) { - case Models::Item::contact: - if (subscribe) - emit subscribeContact(id.account, id.name, ""); - else - emit unsubscribeContact(id.account, id.name, ""); - - break; - case Models::Item::room: - setRoomAutoJoin(id.account, id.name, subscribe); - if (!isConverstationOpened(id)) - emit setRoomJoined(id.account, id.name, subscribe); - - break; - default: - break; - } -} - -void Application::subscribeConversation(Conversation* conv) { - connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); - connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage); - connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage); - connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend); - connect(conv, &Conversation::setEncryption, this, &Application::onConversationSetEncryption); - connect(conv, &Conversation::notifyableMessage, this, &Application::notify); -} - -void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) { - Conversations::const_iterator itr = conversations.find(id); - Models::Account* acc = roster.getAccount(id.account); - Conversation* conv = nullptr; - bool created = false; - if (itr != conversations.end()) { - conv = itr->second; - } else { - Models::Element* el = roster.getElement(id); - if (el != nullptr) { - if (el->type == Models::Item::room) { - created = true; - Models::Room* room = static_cast(el); - conv = new Room(acc, room); - if (!room->getJoined()) - emit setRoomJoined(id.account, id.name, true); - } else if (el->type == Models::Item::contact) { - created = true; - conv = new Chat(acc, static_cast(el)); - } - } - } - - if (conv != nullptr) { - if (created) { - conv->setAttribute(Qt::WA_DeleteOnClose); - subscribeConversation(conv); - conversations.insert(std::make_pair(id, conv)); - } - - conv->show(); - conv->raise(); - conv->activateWindow(); - - if (resource.size() > 0) - conv->setPalResource(resource); - } -} - -void Application::onConversationMessage(const Shared::Message& msg) { - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - - roster.addMessage(acc, msg); - emit sendMessage(acc, msg); -} - -void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) { - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - - roster.changeMessage(acc, msg.getPenPalJid(), originalId, { - {"state", static_cast(Shared::Message::State::pending)} - }); - emit replaceMessage(acc, originalId, msg); -} - -void Application::onConversationSetEncryption(Shared::EncryptionProtocol value) { - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - QString jid = conv->getJid(); - - emit setEncryption(acc, jid, value); -} - -void Application::onConversationResend(const QString& id) { - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - QString jid = conv->getJid(); - - emit resendMessage(acc, jid, id); -} - -void Application::onSquawkOpenedConversation() { - subscribeConversation(squawk->currentConversation); - Models::Roster::ElId id = squawk->currentConversationId(); - - const Models::Element* el = roster.getElementConst(id); - if (el != nullptr && el->isRoom() && !static_cast(el)->getJoined()) { - emit setRoomJoined(id.account, id.name, true); - } -} - -void Application::removeAccount(const QString& account) { - Conversations::const_iterator itr = conversations.begin(); - while (itr != conversations.end()) { - if (itr->first.account == account) { - Conversations::const_iterator lItr = itr; - ++itr; - Conversation* conv = lItr->second; - disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); - conv->close(); - conversations.erase(lItr); - } else { - ++itr; - } - } - - if (squawk != nullptr && squawk->currentConversationId().account == account) - squawk->closeCurrentConversation(); - - roster.removeAccount(account); -} - -void Application::changeAccount(const QString& account, const QMap& data) { - for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { - QString attr = itr.key(); - roster.updateAccount(account, attr, *itr); - } -} - -void Application::addGroup(const QString& account, const QString& name) { - roster.addGroup(account, name); - - if (squawk != nullptr) { - QSettings settings; - settings.beginGroup("ui"); - settings.beginGroup("roster"); - settings.beginGroup(account); - if (settings.value("expanded", false).toBool()) { - QModelIndex ind = roster.getAccountIndex(account); - squawk->expand(ind); - if (settings.value(name + "/expanded", false).toBool()) - squawk->expand(roster.getGroupIndex(account, name)); - } - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - } -} - -bool Application::isConverstationOpened(const Models::Roster::ElId& id) const { - return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);} diff --git a/main/application.h b/main/application.h deleted file mode 100644 index 408572b..0000000 --- a/main/application.h +++ /dev/null @@ -1,134 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef APPLICATION_H -#define APPLICATION_H - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "dialogqueue.h" -#include "core/squawk.h" - -#include "ui/squawk.h" -#include "ui/models/roster.h" -#include "ui/widgets/conversation.h" - -#include "shared/message.h" -#include "shared/enums.h" - -/** - * @todo write docs - */ -class Application : public QObject -{ - Q_OBJECT -public: - Application(Core::Squawk* core); - ~Application(); - - bool isConverstationOpened(const Models::Roster::ElId& id) const; - -signals: - void sendMessage(const QString& account, const Shared::Message& data); - void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); - void resendMessage(const QString& account, const QString& jid, const QString& id); - - void changeState(Shared::Availability state); - - void setRoomJoined(const QString& account, const QString& jid, bool joined); - void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); - void subscribeContact(const QString& account, const QString& jid, const QString& reason); - void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); - void setEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value); - - void quitting(); - void readyToQuit(); - -public slots: - void readSettings(); - void quit(); - -protected slots: - void notify(const QString& account, const Shared::Message& msg); - void unreadMessagesCountChanged(int count); - void setState(Shared::Availability availability); - - void changeAccount(const QString& account, const QMap& data); - void removeAccount(const QString& account); - void openConversation(const Models::Roster::ElId& id, const QString& resource = ""); - - void addGroup(const QString& account, const QString& name); - - void requestPassword(const QString& account, bool authenticationError); - - void writeSettings(); - -private slots: - void onConversationClosed(); - void changeSubscription(const Models::Roster::ElId& id, bool subscribe); - void onSquawkOpenedConversation(); - void onConversationMessage(const Shared::Message& msg); - void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg); - void onConversationResend(const QString& id); - void stateChanged(Shared::Availability state); - void onSquawkClosing(); - void onSquawkDestroyed(); - void onNotificationClosed(quint32 id, quint32 reason); - void onNotificationInvoked(quint32 id, const QString& action); - void onChangeTray(bool enabled, bool hide); - void trayClicked(QSystemTrayIcon::ActivationReason reason); - void toggleSquawk(); - void onItemExpanded(const QModelIndex& index); - void onItemCollapsed(const QModelIndex& index); - void onAddedElement(const std::list& path); - void onConversationSetEncryption(Shared::EncryptionProtocol value); - -private: - void createMainWindow(); - void subscribeConversation(Conversation* conv); - void checkForTheLastWindow(); - void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = ""); - void createTrayIcon(); - -private: - typedef std::map Conversations; - typedef std::map> Notifications; - - Shared::Availability availability; - Core::Squawk* core; - Squawk* squawk; - QDBusInterface notifications; - Models::Roster roster; - Conversations conversations; - DialogQueue dialogueQueue; - bool nowQuitting; - bool destroyingSquawk; - Notifications storage; - QSystemTrayIcon* trayIcon; - QAction actionQuit; - QAction actionToggle; - std::set> expandedPaths; -}; - -#endif // APPLICATION_H diff --git a/main/dialogqueue.cpp b/main/dialogqueue.cpp deleted file mode 100644 index d7b4570..0000000 --- a/main/dialogqueue.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "dialogqueue.h" -#include - -DialogQueue::DialogQueue(const Models::Roster& p_roster): - QObject(), - currentSource(), - currentAction(none), - queue(), - collection(queue.get<0>()), - sequence(queue.get<1>()), - prompt(nullptr), - parent(nullptr), - roster(p_roster) -{ -} - -DialogQueue::~DialogQueue() -{ -} - -void DialogQueue::quit() -{ - queue.clear(); - if (currentAction != none) { - actionDone(); - } -} - -void DialogQueue::setParentWidnow(QMainWindow* p_parent) -{ - parent = p_parent; -} - -bool DialogQueue::addAction(const QString& source, DialogQueue::Action action) -{ - if (action == none) { - return false; - } - if (currentAction == none) { - currentAction = action; - currentSource = source; - performNextAction(); - return true; - } else { - if (currentAction != action || currentSource != source) { - std::pair result = queue.emplace(source, action); - return result.second; - } else { - return false; - } - } -} - -bool DialogQueue::cancelAction(const QString& source, DialogQueue::Action action) -{ - if (source == currentSource && action == currentAction) { - actionDone(); - return true; - } else { - Collection::iterator itr = collection.find(ActionId{source, action}); - if (itr != collection.end()) { - collection.erase(itr); - return true; - } else { - return false; - } - } -} - -void DialogQueue::performNextAction() -{ - switch (currentAction) { - case none: - actionDone(); - break; - case askPassword: { - QInputDialog* dialog = new QInputDialog(parent); - prompt = dialog; - connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); - connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); - dialog->setInputMode(QInputDialog::TextInput); - dialog->setTextEchoMode(QLineEdit::Password); - dialog->setLabelText(tr("Input the password for account %1").arg(currentSource)); - dialog->setWindowTitle(tr("Password for account %1").arg(currentSource)); - dialog->setTextValue(""); - dialog->exec(); - } - break; - case askCredentials: { - CredentialsPrompt* dialog = new CredentialsPrompt(parent); - prompt = dialog; - connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); - connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); - const Models::Account* acc = roster.getAccountConst(currentSource); - dialog->setAccount(currentSource); - dialog->setLogin(acc->getLogin()); - dialog->setPassword(acc->getPassword()); - dialog->exec(); - } - break; - } -} - -void DialogQueue::onPropmtAccepted() -{ - switch (currentAction) { - case none: - break; - case askPassword: { - QInputDialog* dialog = static_cast(prompt); - emit responsePassword(currentSource, dialog->textValue()); - } - break; - case askCredentials: { - CredentialsPrompt* dialog = static_cast(prompt); - emit modifyAccountRequest(currentSource, { - {"login", dialog->getLogin()}, - {"password", dialog->getPassword()} - }); - } - break; - } - actionDone(); -} - -void DialogQueue::onPropmtRejected() -{ - switch (currentAction) { - case none: - break; - case askPassword: - case askCredentials: - emit disconnectAccount(currentSource); - break; - } - actionDone(); -} - -void DialogQueue::actionDone() -{ - prompt->deleteLater(); - prompt = nullptr; - - if (queue.empty()) { - currentAction = none; - currentSource = ""; - } else { - Sequence::iterator itr = sequence.begin(); - currentAction = itr->action; - currentSource = itr->source; - sequence.erase(itr); - performNextAction(); - } -} - -DialogQueue::ActionId::ActionId(const QString& p_source, DialogQueue::Action p_action): - source(p_source), - action(p_action) {} - -bool DialogQueue::ActionId::operator < (const DialogQueue::ActionId& other) const -{ - if (action == other.action) { - return source < other.source; - } else { - return action < other.action; - } -} - -DialogQueue::ActionId::ActionId(const DialogQueue::ActionId& other): - source(other.source), - action(other.action) {} diff --git a/main/dialogqueue.h b/main/dialogqueue.h deleted file mode 100644 index b0da9dc..0000000 --- a/main/dialogqueue.h +++ /dev/null @@ -1,101 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef DIALOGQUEUE_H -#define DIALOGQUEUE_H - -#include -#include -#include - -#include -#include -#include - -#include -#include - -class DialogQueue : public QObject -{ - Q_OBJECT -public: - enum Action { - none, - askPassword, - askCredentials - }; - - DialogQueue(const Models::Roster& roster); - ~DialogQueue(); - - bool addAction(const QString& source, Action action); - bool cancelAction(const QString& source, Action action); - -signals: - void modifyAccountRequest(const QString&, const QMap&); - void responsePassword(const QString& account, const QString& password); - void disconnectAccount(const QString&); - -public: - void setParentWidnow(QMainWindow* parent); - void quit(); - -private: - void performNextAction(); - void actionDone(); - -private slots: - void onPropmtAccepted(); - void onPropmtRejected(); - -private: - QString currentSource; - Action currentAction; - - struct ActionId { - public: - ActionId(const QString& p_source, Action p_action); - ActionId(const ActionId& other); - - const QString source; - const Action action; - - bool operator < (const ActionId& other) const; - }; - - typedef boost::multi_index_container < - ActionId, - boost::multi_index::indexed_by < - boost::multi_index::ordered_unique < - boost::multi_index::identity - >, - boost::multi_index::sequenced<> - > - > Queue; - - typedef Queue::nth_index<0>::type Collection; - typedef Queue::nth_index<1>::type Sequence; - - Queue queue; - Collection& collection; - Sequence& sequence; - - QDialog* prompt; - QMainWindow* parent; - const Models::Roster& roster; -}; - -#endif // DIALOGQUEUE_H diff --git a/main/main.cpp b/main/main.cpp deleted file mode 100644 index f810dbb..0000000 --- a/main/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "root.h" -#include "shared/messageinfo.h" -#include "shared/identity.h" -#include "shared/info.h" - -#include - -#ifdef WITH_OMEMO -#include -#endif - -int main(int argc, char *argv[]) { - qRegisterMetaType("Shared::Message"); - qRegisterMetaType("Shared::MessageInfo"); - qRegisterMetaType("Shared::VCard"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::set"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("QSet"); - qRegisterMetaType("Shared::ConnectionState"); - qRegisterMetaType("Shared::Availability"); - qRegisterMetaType("Shared::EncryptionProtocol"); - qRegisterMetaType("Shared::KeyInfo"); - qRegisterMetaType("Shared::Info"); - qRegisterMetaType("Shared::TrustLevel"); -#ifdef WITH_OMEMO - qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); - qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); - qRegisterMetaType("QXmppOmemoStorage::Device"); -#endif - - Root app(argc, argv); - if (!app.initializeSettings()) - return -1; - - return app.run(); -} - diff --git a/main/root.cpp b/main/root.cpp deleted file mode 100644 index 87d97bb..0000000 --- a/main/root.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "root.h" -#include -#include -#include -#include - -#include - -#include - -const std::vector Root::appIconSizes({ - 16, 24, 32, 48, 64, 96, 128, 256, 512 -}); - -Root::Root(int& argc, char *argv[]) : - QApplication(argc, argv), - signalCatcher(this), - defaultTranslator(), - currentTranslator(), - appIcon(), - settings(), - componentsInitialized(false), - global(nullptr), - coreThread(nullptr), - core(nullptr), - gui(nullptr) -{ - setApplicationName("squawk"); - setOrganizationName("macaw.me"); - setApplicationDisplayName("Squawk"); - setApplicationVersion("0.2.3"); - setDesktopFileName("squawk"); - - initializeTranslation(); - initializeAppIcon(); - - global = new Shared::Global(); //important to instantiate after initialization of translations; -} - -Root::~Root() { - if (componentsInitialized) { - delete gui; - if (core != nullptr) - delete core; - - delete coreThread; - } - delete global; -} - - -void Root::initializeTranslation() { - defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - installTranslator(&defaultTranslator); - - QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - bool found = false; - for (QString share : shares) { - found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); - if (found) - break; - } - - if (!found) - currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); - - - installTranslator(¤tTranslator); -} - -void Root::initializeAppIcon() { - for (std::vector::size_type i = 0; i < appIconSizes.size(); ++i) - appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i])); - - Root::setWindowIcon(appIcon); -} - -bool Root::initializeSettings() { - QVariant vs = settings.value("style"); - if (vs.isValid()) { - QString style = vs.toString().toLower(); - if (style != "system") - Shared::Global::setStyle(style); - } - - if (Shared::Global::supported("colorSchemeTools")) { - QVariant vt = settings.value("theme"); - if (vt.isValid()) { - QString theme = vt.toString(); - if (theme.toLower() != "system") - Shared::Global::setTheme(theme); - } - } - - QString path = Shared::downloadsPathCheck(); - if (path.size() > 0) { - settings.setValue("downloadsPath", path); - } else { - qDebug() << "couldn't initialize directory for downloads, quitting"; - return false; - } - - return true; -} - -int Root::run() { - if (!componentsInitialized) - initializeComponents(); - - coreThread->start(); - int result = exec(); - qDebug("Event loop stopped"); - - if (result == 0) { - processEvents(); //I dont like all of this mess - if (coreThread->isRunning()) { //but it's the best solution for now - if (core != nullptr) { //Ideally, following line should never appear in the log - qDebug() << "Core is still seems to be running, killing manually"; - core->deleteLater(); - coreThread->quit(); - processEvents(); - core = nullptr; - } - coreThread->wait(); - } - } - - return result; -} - -void Root::initializeComponents() { - core = new Core::Squawk(); - coreThread = new QThread(); - core->moveToThread(coreThread); - gui = new Application(core); - - QObject::connect(&signalCatcher, &SignalCatcher::interrupt, gui, &Application::quit); - - QObject::connect(coreThread, &QThread::started, core, &Core::Squawk::start); - QObject::connect(gui, &Application::quitting, core, &Core::Squawk::stop); - - QObject::connect(core, &Core::Squawk::quit, core, &Core::Squawk::deleteLater); - QObject::connect(core, &Core::Squawk::destroyed, this, &Root::onCoreDestroyed); - QObject::connect(core, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection); - QObject::connect(coreThread, &QThread::finished, this, &Root::quit, Qt::QueuedConnection); - - componentsInitialized = true; -} - -bool Root::notify(QObject* receiver, QEvent* e) { - try { - return QApplication::notify(receiver, e); - } catch(const std::runtime_error& e) { - qDebug() << "std::runtime_error in thread:" << QThread::currentThreadId(); - qDebug() << "error message:" << e.what(); - } catch(const std::exception& e) { - qDebug() << "std::exception in thread:" << QThread::currentThreadId(); - qDebug() << "error message:" << e.what(); - } catch(const int& e) { - qDebug() << "integer exception in thread:" << QThread::currentThreadId(); - qDebug() << "thrown integer:" << std::to_string(e).c_str(); - } catch(...) { - qDebug() << "unhandled exception thread:" << QThread::currentThreadId(); - } - - qDebug() << "Squawk is crashing..."; - exit(1); - return false; -} - -void Root::onCoreDestroyed() { - core = nullptr; -} diff --git a/main/root.h b/main/root.h deleted file mode 100644 index 003d9cb..0000000 --- a/main/root.h +++ /dev/null @@ -1,70 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef ROOT_H -#define ROOT_H - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include "application.h" - -class Root : public QApplication { - Q_OBJECT -public: - Root(int& argc, char* argv[]); - ~Root(); - bool notify(QObject* receiver, QEvent* e) override; - int run(); - - bool initializeSettings(); - -private slots: - void onCoreDestroyed(); - -private: - void initializeTranslation(); - void initializeAppIcon(); - void initializeComponents(); - -private: - static const std::vector appIconSizes; - - SignalCatcher signalCatcher; - QTranslator defaultTranslator; - QTranslator currentTranslator; - QIcon appIcon; - QSettings settings; - bool componentsInitialized; - - Shared::Global* global; - QThread* coreThread; - Core::Squawk* core; - Application* gui; - -}; - -#endif // ROOT_H diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 0defdc3..20fea99 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,26 +1,23 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.4 +pkgver=0.1.5 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal-qt6' 'qxmpp') -makedepends=('cmake>=3.3' 'imagemagick' 'qt6-tools' 'boost') -optdepends=('kwallet6: secure password storage (requires rebuild)' - 'kconfig6: system themes support (requires rebuild)' - 'kconfigwidgets6: system themes support (requires rebuild)' - 'kio6: better show in folder action (requires rebuild)') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0') +makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools') +optdepends=('kwallet: secure password storage (requires rebuild)') -source=("$pkgname-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") -sha256sums=('SKIP') +source=("$pkgname-$pkgver.tar.gz") +sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release - cmake --build . + cmake --build . -j $nproc } package() { cd "$srcdir/squawk" - DESTDIR="$pkgdir/" cmake --install . + DESTDIR="$pkgdir/" cmake --build . --target install } diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 4bb9c87..4965b37 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -1,7 +1,3 @@ configure_file(squawk.desktop squawk.desktop COPYONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) - -configure_file(macaw.me.squawk.appdata.xml macaw.me.squawk.appdata.xml COPYONLY) - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/macaw.me.squawk.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) \ No newline at end of file diff --git a/packaging/macaw.me.squawk.appdata.xml b/packaging/macaw.me.squawk.appdata.xml deleted file mode 100644 index c188496..0000000 --- a/packaging/macaw.me.squawk.appdata.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - macaw.me.squawk - CC0-1.0 - GPL-3.0+ - Squawk - Desktop Qt based XMPP messenger - -

- Squawk is a lightweight XMPP desktop messenger. - The primary objective of this project is to offer - you a fast and user-friendly messaging experience - that closely aligns with your system’s style, while - also minimizing resource consumption. -

-

- Squawk is still at a very early stage and might not suit - everyone but you are welcome to try it out. -

-
- macaw.me.squawk.desktop - - - https://macaw.me/projects/squawk/0.2.2.png - View XMPP contacts and conversations - - - https://macaw.me/projects/squawk/ - - squawk - - blue@macaw.me -
diff --git a/packaging/squawk.desktop b/packaging/squawk.desktop index c64f9ab..ba0f13c 100644 --- a/packaging/squawk.desktop +++ b/packaging/squawk.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Type=Application -Version=0.2.4 +Version=1.0 Name=Squawk GenericName=Instant Messenger GenericName[ru]=Мгновенные сообщения diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9eb9070..84fc09b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,14 +1,4 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) - target_link_libraries(openFileManagerWindowJob PRIVATE KF${QT_VERSION_MAJOR}::KIOWidgets) - - install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) + target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) endif () - -if (WITH_KCONFIG) - add_library(colorSchemeTools SHARED colorschemetools.cpp) - target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigCore) - target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigWidgets) - - install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) -endif() diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp deleted file mode 100644 index 76eba9b..0000000 --- a/plugins/colorschemetools.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include -#include -#include - -QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection); - -extern "C" QIcon* createPreview(const QString& path) { - KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path); - QIcon* result = new QIcon(); - KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig); - KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig); - KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig); - KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig); - - result->addPixmap(createPixmap(16, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background())); - result->addPixmap(createPixmap(24, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background())); - - return result; -} - -extern "C" void deletePreview(QIcon* icon) { - delete icon; -} - -extern "C" void colorSchemeName(const QString& path, QString& answer) { - KSharedConfigPtr config = KSharedConfig::openConfig(path); - KConfigGroup group(config, QStringLiteral("General")); - answer = group.readEntry("Name", QFileInfo(path).baseName()); -} - -extern "C" void createPalette(const QString& path, QPalette& answer) { - KSharedConfigPtr config = KSharedConfig::openConfig(path); - answer = KColorScheme::createApplicationPalette(config); -} - -QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection) { - QPixmap pix(size, size); - pix.fill(Qt::black); - QPainter p; - p.begin(&pix); - const int itemSize = size / 2 - 1; - p.fillRect(1, 1, itemSize, itemSize, window); - p.fillRect(1 + itemSize, 1, itemSize, itemSize, button); - p.fillRect(1, 1 + itemSize, itemSize, itemSize, view); - p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, selection); - p.end(); - return pix; -} diff --git a/plugins/openfilemanagerwindowjob.cpp b/plugins/openfilemanagerwindowjob.cpp index 4335410..904fbcf 100644 --- a/plugins/openfilemanagerwindowjob.cpp +++ b/plugins/openfilemanagerwindowjob.cpp @@ -1,21 +1,3 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include #include #include diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 717abf2..9288650 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -3,8 +3,11 @@ target_sources(squawk PRIVATE resources.qrc) configure_file(images/logo.svg squawk.svg COPYONLY) configure_file(squawk.rc squawk.rc COPYONLY) -set(CONVERT_BIN magick) - +if(WIN32) + set(CONVERT_BIN magick convert) +else(WIN32) + set(CONVERT_BIN convert) +endif(WIN32) execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) @@ -15,20 +18,20 @@ if (WIN32) set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc") set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE) target_sources(squawk PRIVATE ${SQUAWK_WIN_RC}) -endif (WIN32) +endif(WIN32) if (APPLE) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset") - execute_process(COMMAND ${CONVERT_BIN} -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND ${CONVERT_BIN} -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set(MACOSX_BUNDLE_ICON_FILE squawk.icns) set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE) @@ -44,8 +47,8 @@ if (APPLE) MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO MACOSX_BUNDLE_BUNDLE_NAME "Squawk" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in) - endif (APPLE) - endif () + endif(APPLE) + endif() endif (APPLE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) diff --git a/resources/images/fallback/dark/big/lock.svg b/resources/images/fallback/dark/big/lock.svg deleted file mode 100644 index d7835b3..0000000 --- a/resources/images/fallback/dark/big/lock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/dark/big/shield.svg b/resources/images/fallback/dark/big/shield.svg deleted file mode 100644 index c63b275..0000000 --- a/resources/images/fallback/dark/big/shield.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/dark/big/unlock.svg b/resources/images/fallback/dark/big/unlock.svg deleted file mode 100644 index a5e4a4c..0000000 --- a/resources/images/fallback/dark/big/unlock.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/dark/small/lock.svg b/resources/images/fallback/dark/small/lock.svg deleted file mode 100644 index 6d36522..0000000 --- a/resources/images/fallback/dark/small/lock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/dark/small/shield.svg b/resources/images/fallback/dark/small/shield.svg deleted file mode 100644 index c63b275..0000000 --- a/resources/images/fallback/dark/small/shield.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/dark/small/unlock.svg b/resources/images/fallback/dark/small/unlock.svg deleted file mode 100644 index 9f2f121..0000000 --- a/resources/images/fallback/dark/small/unlock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/big/lock.svg b/resources/images/fallback/light/big/lock.svg deleted file mode 100644 index 5c7f4e1..0000000 --- a/resources/images/fallback/light/big/lock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/big/shield.svg b/resources/images/fallback/light/big/shield.svg deleted file mode 100644 index 52f9c34..0000000 --- a/resources/images/fallback/light/big/shield.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/big/unlock.svg b/resources/images/fallback/light/big/unlock.svg deleted file mode 100644 index 0548efc..0000000 --- a/resources/images/fallback/light/big/unlock.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/small/lock.svg b/resources/images/fallback/light/small/lock.svg deleted file mode 100644 index a566da0..0000000 --- a/resources/images/fallback/light/small/lock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/small/shield.svg b/resources/images/fallback/light/small/shield.svg deleted file mode 100644 index 52f9c34..0000000 --- a/resources/images/fallback/light/small/shield.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/images/fallback/light/small/unlock.svg b/resources/images/fallback/light/small/unlock.svg deleted file mode 100644 index ff91b8e..0000000 --- a/resources/images/fallback/light/small/unlock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/resources/resources.qrc b/resources/resources.qrc index 993255b..58565fc 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -42,9 +42,6 @@ images/fallback/dark/big/add.svg images/fallback/dark/big/folder.svg images/fallback/dark/big/document-preview.svg - images/fallback/dark/big/shield.svg - images/fallback/dark/big/lock.svg - images/fallback/dark/big/unlock.svg images/fallback/dark/small/absent.svg @@ -87,9 +84,6 @@ images/fallback/dark/small/add.svg images/fallback/dark/small/folder.svg images/fallback/dark/small/document-preview.svg - images/fallback/dark/small/shield.svg - images/fallback/dark/small/lock.svg - images/fallback/dark/small/unlock.svg images/fallback/light/big/absent.svg @@ -132,9 +126,6 @@ images/fallback/light/big/add.svg images/fallback/light/big/folder.svg images/fallback/light/big/document-preview.svg - images/fallback/light/big/shield.svg - images/fallback/light/big/lock.svg - images/fallback/light/big/unlock.svg images/fallback/light/small/absent.svg @@ -177,8 +168,5 @@ images/fallback/light/small/add.svg images/fallback/light/small/folder.svg images/fallback/light/small/document-preview.svg - images/fallback/light/small/shield.svg - images/fallback/light/small/lock.svg - images/fallback/light/small/unlock.svg diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 45fcfd0..a36b516 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -1,42 +1,19 @@ -set(SOURCE_FILES - global.cpp - exception.cpp - icons.cpp - message.cpp - messageinfo.cpp - utils.cpp - vcard.cpp - pathcheck.cpp - clientinfo.cpp - identity.cpp - form.cpp - field.cpp - keyinfo.cpp - info.cpp - clientid.cpp - trustsummary.cpp -) - -set(HEADER_FILES - shared.h +target_sources(squawk PRIVATE enums.h + global.cpp global.h + exception.cpp exception.h + icons.cpp icons.h + message.cpp message.h + messageinfo.cpp messageinfo.h + order.h + shared.h + utils.cpp utils.h + vcard.cpp vcard.h - pathcheck.h - clientinfo.h - identity.h - form.h - field.h - keyinfo.h - info.h - clientid.h - trustsummary.h - defines.h -) - -target_sources(squawk PRIVATE ${SOURCE_FILES} ${HEADER_FILES}) + ) diff --git a/shared/clientid.cpp b/shared/clientid.cpp deleted file mode 100644 index b050df6..0000000 --- a/shared/clientid.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "clientid.h" - -Shared::ClientId::ClientId(): - node(), - verification(), - hash() -{} - -Shared::ClientId::ClientId(const QString& p_node, const QString& p_ver, const QString& p_hash): - node(p_node), - verification(p_ver), - hash(p_hash) -{} - - -Shared::ClientId::ClientId(const Shared::ClientId& other): - node(other.node), - verification(other.verification), - hash(other.hash) -{} - -Shared::ClientId & Shared::ClientId::operator=(const Shared::ClientId& other) { - node = other.node; - verification = other.verification; - hash = other.hash; - - return *this; -} - -bool Shared::ClientId::operator==(const Shared::ClientId& other) const { - return hash == other.hash && verification == other.verification && node == other.node; -} - -bool Shared::ClientId::operator!=(const Shared::ClientId& other) const { - return hash != other.hash && verification != other.verification && node != other.node; -} - -bool Shared::ClientId::operator<(const Shared::ClientId& other) const { - if (hash < other.hash) - return true; - - if (hash > other.hash) - return false; - - if (verification < other.verification) - return true; - - if (verification > other.verification) - return false; - - if (node < other.node) - return true; - - return false; -} - -bool Shared::ClientId::operator>(const Shared::ClientId& other) const { - if (hash > other.hash) - return true; - - if (hash < other.hash) - return false; - - if (verification > other.verification) - return true; - - if (verification < other.verification) - return false; - - if (node > other.node) - return true; - - return false; -} - -bool Shared::ClientId::operator<=(const Shared::ClientId& other) const { - if (hash < other.hash) - return true; - - if (hash > other.hash) - return false; - - if (verification < other.verification) - return true; - - if (verification > other.verification) - return false; - - if (node < other.node) - return true; - - if (node > other.node) - return false; - - return true; -} - -bool Shared::ClientId::operator>=(const Shared::ClientId& other) const { - if (hash > other.hash) - return true; - - if (hash < other.hash) - return false; - - if (verification > other.verification) - return true; - - if (verification < other.verification) - return false; - - if (node > other.node) - return true; - - if (node < other.node) - return false; - - return true; -} - -QString Shared::ClientId::getId() const { - return node + "/" + verification; -} - -bool Shared::ClientId::valid() const { - return node.size() > 0 && verification.size() > 0 && hash.size() > 0; -} - -QDataStream & Shared::ClientId::operator<<(QDataStream& stream) { - stream >> node; - stream >> verification; - stream >> hash; - return stream; -} - -QDataStream & Shared::ClientId::operator>>(QDataStream& stream) const { - stream << node; - stream << verification; - stream << hash; - return stream; -} - -QDataStream & operator<<(QDataStream& stream, const Shared::ClientId& info) { - info >> stream; - return stream; -} - -QDataStream & operator>>(QDataStream& stream, Shared::ClientId& info) { - info << stream; - return stream; -} - diff --git a/shared/clientid.h b/shared/clientid.h deleted file mode 100644 index 05bed45..0000000 --- a/shared/clientid.h +++ /dev/null @@ -1,56 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include -#include -#include - -namespace Shared { - -class ClientId { -public: - ClientId(); - ClientId(const QString& node, const QString& verification, const QString& hash); - ClientId(const ClientId& other); - ClientId& operator = (const ClientId& other); - - bool operator == (const ClientId& other) const; - bool operator != (const ClientId& other) const; - bool operator < (const ClientId& other) const; - bool operator > (const ClientId& other) const; - bool operator <= (const ClientId& other) const; - bool operator >= (const ClientId& other) const; - - bool valid() const; - QString getId() const; - - QDataStream& operator << (QDataStream& stream); - QDataStream& operator >> (QDataStream& stream) const; - -public: - QString node; - QString verification; - QString hash; -}; - -} - -Q_DECLARE_METATYPE(Shared::ClientId) - -QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info); -QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info); diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp deleted file mode 100644 index 9b84755..0000000 --- a/shared/clientinfo.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "clientinfo.h" - -const std::map Shared::ClientInfo::hashes = { - //md2 is missing - {"md5", QCryptographicHash::Md5}, - {"sha-1", QCryptographicHash::Sha1}, - {"sha-224", QCryptographicHash::Sha224}, - {"sha-256", QCryptographicHash::Sha256}, - {"sha-384", QCryptographicHash::Sha384}, - {"sha-512", QCryptographicHash::Sha512}, - //shake128 is missing - //shake256 is missing - -}; - -Shared::ClientInfo::ClientInfo(): - identities(), - extensions(), - id(), - specificPresence() {} - -Shared::ClientInfo::ClientInfo(const QString& p_node, const QString& p_ver, const QString& p_hash) : - identities(), - extensions(), - id(p_node, p_ver, p_hash), - specificPresence() {} - -Shared::ClientInfo::ClientInfo(const Shared::ClientId& p_id) : - identities(), - extensions(), - id(p_id), - specificPresence() {} - -QString Shared::ClientInfo::getId() const { - return id.getId(); -} - -QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { - stream << id; - stream << (quint8)identities.size(); - for (const Shared::Identity& identity : identities) { - stream << identity; - } - stream << (quint8)extensions.size(); - for (const QString& ext : extensions) { - stream << ext; - } - - return stream; -} - -QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) { - stream >> id; - - quint8 size; - stream >> size; - for (quint8 i = 0; i < size; ++i) { - Shared::Identity identity; - stream >> identity; - identities.insert(identity); - } - - stream >> size; - for (quint8 i = 0; i < size; ++i) { - QString ext; - stream >> ext; - extensions.insert(ext); - } - - return stream; -} - -bool Shared::ClientInfo::valid() const { - std::map::const_iterator itr = hashes.find(id.hash); - if (itr == hashes.end()) { - return false; - } - - QCryptographicHash calc(itr->second); - QString validationString = ""; - for (const Identity& identity : identities) { - calc.addData((identity.category + "/" + identity.type + "/" + identity.language + "/" + identity.name + "<").toUtf8()); - } - for (const QString& ext : extensions) { - calc.addData((ext + "<").toUtf8()); - } - - QString result = calc.result().toBase64(); - - return result == id.verification; -} - -QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info) { - info >> stream; - return stream; -} -QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info) { - info << stream; - return stream; -} diff --git a/shared/clientinfo.h b/shared/clientinfo.h deleted file mode 100644 index ca66c6d..0000000 --- a/shared/clientinfo.h +++ /dev/null @@ -1,56 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include -#include - -#include -#include -#include - -#include -#include - -namespace Shared { - -class ClientInfo { -public: - ClientInfo(); - ClientInfo(const ClientId& id); - ClientInfo(const QString& node, const QString& verification, const QString& hash); - - QString getId() const; - bool valid() const; - - QDataStream& operator << (QDataStream& stream); - QDataStream& operator >> (QDataStream& stream) const; - -public: - std::set identities; - std::set extensions; - ClientId id; - QString specificPresence; - -private: - static const std::map hashes; -}; - -} - -QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info); -QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info); diff --git a/shared/defines.h b/shared/defines.h deleted file mode 100644 index 8ead3f6..0000000 --- a/shared/defines.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#define SHARED_UNUSED(x) (void)(x) diff --git a/shared/enums.h b/shared/enums.h index 6f5e9db..cb41443 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHARED_ENUMS_H +#define SHARED_ENUMS_H #include @@ -28,13 +29,12 @@ Q_NAMESPACE enum class ConnectionState { disconnected, - scheduled, connecting, connected, error }; Q_ENUM_NS(ConnectionState) -static const std::deque connectionStateThemeIcons = {"state-offline", "state-sync", "state-sync", "state-ok", "state-error"}; +static const std::deque connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; static const ConnectionState ConnectionStateHighest = ConnectionState::error; static const ConnectionState ConnectionStateLowest = ConnectionState::disconnected; @@ -101,8 +101,8 @@ enum class Avatar { valid }; Q_ENUM_NS(Avatar) -static const Avatar AvatarHighest = Avatar::valid; -static const Avatar AvatarLowest = Avatar::empty; +static const Avatar AvatarHighest = Avatar::valid; +static const Avatar AvatarLowest = Avatar::empty; static const std::deque messageStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; @@ -117,63 +117,5 @@ Q_ENUM_NS(AccountPassword) static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordLowest = AccountPassword::plain; -enum class EntryType { - none, - ownAccount, - contact, - conference, - presence, - participant -}; -Q_ENUM_NS(EntryType) -static const EntryType EntryTypeHighest = EntryType::participant; -static const EntryType EntryTypeLowest = EntryType::none; - -enum class Support { - unknown, - supported, - unsupported -}; -Q_ENUM_NS(Support) - -enum class Possible { - unknown, - discovering, - present, - abscent -}; -Q_ENUM_NS(Possible) - -enum class TrustLevel { - /// The key's trust is not decided. - undecided, - /// The key is automatically distrusted (e.g., by the security policy TOAKAFA). - /// \see SecurityPolicy - automaticallyDistrusted, - /// The key is manually distrusted (e.g., by clicking a button or \xep{0450, Automatic Trust - /// Management (ATM)}). - manuallyDistrusted, - /// The key is automatically trusted (e.g., by the client for all keys of a bare JID until one - /// of it is authenticated). - automaticallyTrusted, - /// The key is manually trusted (e.g., by clicking a button). - manuallyTrusted, - /// The key is authenticated (e.g., by QR code scanning or \xep{0450, Automatic Trust - /// Management (ATM)}). - authenticated -}; -Q_ENUM_NS(TrustLevel) -static const TrustLevel TrustLevelHighest = TrustLevel::undecided; -static const TrustLevel TrustLevelLowest = TrustLevel::authenticated; - -enum class EncryptionProtocol { - none, - omemo, - omemo1, - omemo2 -}; -Q_ENUM_NS(EncryptionProtocol) -static const EncryptionProtocol EncryptionProtocolHighest = EncryptionProtocol::none; -static const EncryptionProtocol EncryptionProtocolLowest = EncryptionProtocol::omemo2; - } +#endif // SHARED_ENUMS_H diff --git a/shared/exception.cpp b/shared/exception.cpp index 3dee9b3..342593c 100644 --- a/shared/exception.cpp +++ b/shared/exception.cpp @@ -28,6 +28,5 @@ Utils::Exception::~Exception() const char* Utils::Exception::what() const noexcept( true ) { - std::string* msg = new std::string(getMessage()); - return msg->c_str(); + return getMessage().c_str(); } diff --git a/shared/exception.h b/shared/exception.h index 48d7eda..4c66c2d 100644 --- a/shared/exception.h +++ b/shared/exception.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef EXCEPTION_H +#define EXCEPTION_H #include #include @@ -35,3 +36,5 @@ namespace Utils const char* what() const noexcept( true ); }; } + +#endif // EXCEPTION_H diff --git a/shared/field.cpp b/shared/field.cpp deleted file mode 100644 index f469646..0000000 --- a/shared/field.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "field.h" - -Shared::Field::Field(Shared::Field::Type fieldTtype): - type(fieldTtype), - key(), - label(), - description(), - required(false), - options(), - value() -{ -} diff --git a/shared/field.h b/shared/field.h deleted file mode 100644 index e7c9a02..0000000 --- a/shared/field.h +++ /dev/null @@ -1,61 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef SHARED_FIELD_H -#define SHARED_FIELD_H - -#include - -#include -#include - -namespace Shared { - -/** - * @todo write docs - */ -class Field -{ -public: - enum class Type { - boolean, - fixed, - hidden, - jidMultiple, - jidSingle, - listMultiple, - listSingle, - textMultiple, - textPrivate, - textSingle - }; - - Field(Type fieldType); - -public: - const Type type; - QString key; - QString label; - QString description; - bool required; - std::list> options; - QVariant value; - -}; - -} - -#endif // SHARED_FIELD_H diff --git a/shared/form.cpp b/shared/form.cpp deleted file mode 100644 index bf309ca..0000000 --- a/shared/form.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "form.h" - -Shared::Form::Form(Shared::Form::Type formType): - type(formType), - title(), - instructions(), - fields() {} diff --git a/shared/form.h b/shared/form.h deleted file mode 100644 index 08c8c95..0000000 --- a/shared/form.h +++ /dev/null @@ -1,48 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef SHARED_FORM_H -#define SHARED_FORM_H - -#include - -#include - -namespace Shared { - -class Form -{ -public: - enum class Type { - none, - form, - submit, - cancel, - result - }; - - Form(Type formType); - -public: - const Type type; - QString title; - QString instructions; - std::list fields; -}; - -} - -#endif // SHARED_FORM_H diff --git a/shared/global.cpp b/shared/global.cpp index f0e3230..d6f2169 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -17,142 +17,76 @@ */ #include "global.h" -#include -#include "defines.h" #include "enums.h" -#include "ui/models/roster.h" - -#ifdef WITH_OMEMO -constexpr bool OMEMO_SUPPORT = true; -#else -constexpr bool OMEMO_SUPPORT = false; -#endif - -QFont getFont (QFontDatabase::SystemFont type, bool bold = false, bool italic = false, qreal factor = 1.0) { - QFont font = QFontDatabase::systemFont(type); - if (bold) - font.setBold(true); - if (italic) - font.setItalic(true); - - if (factor != 1.0) { - float ps = font.pointSizeF(); - if (ps != -1) - font.setPointSizeF(ps * factor); - else - font.setPointSize(font.pointSize() * factor); - } - - return font; -} Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; #ifdef WITH_KIO -QLibrary Shared::Global::openFileManagerWindowJob(QString("%1/openFileManagerWindowJob").arg(PLUGIN_PATH)); +QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob"); Shared::Global::HighlightInFileManager Shared::Global::hfm = 0; #endif -#ifdef WITH_KCONFIG -QLibrary Shared::Global::colorSchemeTools(QString("%1/colorSchemeTools").arg(PLUGIN_PATH)); -Shared::Global::CreatePreview Shared::Global::createPreview = 0; -Shared::Global::DeletePreview Shared::Global::deletePreview = 0; -Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0; -Shared::Global::CreatePalette Shared::Global::createPalette = 0; -#endif - - - Shared::Global::Global(): availability({ - QCoreApplication::translate("Global", "Online", "Availability"), - QCoreApplication::translate("Global", "Away", "Availability"), - QCoreApplication::translate("Global", "Absent", "Availability"), - QCoreApplication::translate("Global", "Busy", "Availability"), - QCoreApplication::translate("Global", "Chatty", "Availability"), - QCoreApplication::translate("Global", "Invisible", "Availability"), - QCoreApplication::translate("Global", "Offline", "Availability") + tr("Online", "Availability"), + tr("Away", "Availability"), + tr("Absent", "Availability"), + tr("Busy", "Availability"), + tr("Chatty", "Availability"), + tr("Invisible", "Availability"), + tr("Offline", "Availability") }), connectionState({ - QCoreApplication::translate("Global", "Disconnected", "ConnectionState"), - QCoreApplication::translate("Global", "Scheduled", "ConnectionState"), - QCoreApplication::translate("Global", "Connecting", "ConnectionState"), - QCoreApplication::translate("Global", "Connected", "ConnectionState"), - QCoreApplication::translate("Global", "Error", "ConnectionState") + tr("Disconnected", "ConnectionState"), + tr("Connecting", "ConnectionState"), + tr("Connected", "ConnectionState"), + tr("Error", "ConnectionState") }), subscriptionState({ - QCoreApplication::translate("Global", "None", "SubscriptionState"), - QCoreApplication::translate("Global", "From", "SubscriptionState"), - QCoreApplication::translate("Global", "To", "SubscriptionState"), - QCoreApplication::translate("Global", "Both", "SubscriptionState"), - QCoreApplication::translate("Global", "Unknown", "SubscriptionState") + tr("None", "SubscriptionState"), + tr("From", "SubscriptionState"), + tr("To", "SubscriptionState"), + tr("Both", "SubscriptionState"), + tr("Unknown", "SubscriptionState") }), affiliation({ - QCoreApplication::translate("Global", "Unspecified", "Affiliation"), - QCoreApplication::translate("Global", "Outcast", "Affiliation"), - QCoreApplication::translate("Global", "Nobody", "Affiliation"), - QCoreApplication::translate("Global", "Member", "Affiliation"), - QCoreApplication::translate("Global", "Admin", "Affiliation"), - QCoreApplication::translate("Global", "Owner", "Affiliation") + tr("Unspecified", "Affiliation"), + tr("Outcast", "Affiliation"), + tr("Nobody", "Affiliation"), + tr("Member", "Affiliation"), + tr("Admin", "Affiliation"), + tr("Owner", "Affiliation") }), role({ - QCoreApplication::translate("Global", "Unspecified", "Role"), - QCoreApplication::translate("Global", "Nobody", "Role"), - QCoreApplication::translate("Global", "Visitor", "Role"), - QCoreApplication::translate("Global", "Participant", "Role"), - QCoreApplication::translate("Global", "Moderator", "Role") + tr("Unspecified", "Role"), + tr("Nobody", "Role"), + tr("Visitor", "Role"), + tr("Participant", "Role"), + tr("Moderator", "Role") }), messageState({ - QCoreApplication::translate("Global", "Pending", "MessageState"), - QCoreApplication::translate("Global", "Sent", "MessageState"), - QCoreApplication::translate("Global", "Delivered", "MessageState"), - QCoreApplication::translate("Global", "Error", "MessageState") + tr("Pending", "MessageState"), + tr("Sent", "MessageState"), + tr("Delivered", "MessageState"), + tr("Error", "MessageState") }), accountPassword({ - QCoreApplication::translate("Global", "Plain", "AccountPassword"), - QCoreApplication::translate("Global", "Jammed", "AccountPassword"), - QCoreApplication::translate("Global", "Always Ask", "AccountPassword"), - QCoreApplication::translate("Global", "KWallet", "AccountPassword") - }), - trustLevel({ - QCoreApplication::translate("Global", "Undecided", "TrustLevel"), - QCoreApplication::translate("Global", "Automatically distrusted", "TrustLevel"), - QCoreApplication::translate("Global", "Manually distrusted", "TrustLevel"), - QCoreApplication::translate("Global", "Automatically trusted", "TrustLevel"), - QCoreApplication::translate("Global", "Manually trusted", "TrustLevel"), - QCoreApplication::translate("Global", "Authenticated", "TrustLevel") - }), - encryptionProtocols({ - QCoreApplication::translate("Global", "None", "EncryptionProtocol"), - QCoreApplication::translate("Global", "OMEMO 0", "EncryptionProtocol"), - QCoreApplication::translate("Global", "OMEMO 1", "EncryptionProtocol"), - QCoreApplication::translate("Global", "OMEMO 2", "EncryptionProtocol") + tr("Plain", "AccountPassword"), + tr("Jammed", "AccountPassword"), + tr("Always Ask", "AccountPassword"), + tr("KWallet", "AccountPassword") }), accountPasswordDescription({ - QCoreApplication::translate("Global", "Your password is going to be stored in config file in plain text", "AccountPasswordDescription"), - QCoreApplication::translate("Global", "Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"), - QCoreApplication::translate("Global", "Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"), - QCoreApplication::translate("Global", "Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") + tr("Your password is going to be stored in config file in plain text", "AccountPasswordDescription"), + tr("Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"), + tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"), + tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") }), - defaultSystemStyle(QApplication::style()->objectName()), - defaultSystemPalette(QApplication::palette()), - omemoSupport(OMEMO_SUPPORT), - defaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)), - monospaceFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), - smallFont(getFont(QFontDatabase::SmallestReadableFont, false, true)), - headerFont(getFont(QFontDatabase::TitleFont, true, false, 1.1)), - titleFont(getFont(QFontDatabase::TitleFont, true, false, 1.3)), - defaultFontMetrics(defaultFont), - monospaceMetrics(monospaceFont), - smallFontMetrics(smallFont), - headerFontMetrics(headerFont), - titleFontMetrics(titleFont), - optionalFeatures({ + pluginSupport({ {"KWallet", false}, - {"openFileManagerWindowJob", false}, - {"colorSchemeTools", false} + {"openFileManagerWindowJob", false} }), fileCache() { @@ -176,29 +110,12 @@ Shared::Global::Global(): qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString(); } #endif - -#ifdef WITH_KCONFIG - colorSchemeTools.load(); - if (colorSchemeTools.isLoaded()) { - createPreview = (CreatePreview) colorSchemeTools.resolve("createPreview"); - deletePreview = (DeletePreview) colorSchemeTools.resolve("deletePreview"); - colorSchemeName = (ColorSchemeName) colorSchemeTools.resolve("colorSchemeName"); - createPalette = (CreatePalette) colorSchemeTools.resolve("createPalette"); - if (createPreview && deletePreview && colorSchemeName && createPalette) { - setSupported("colorSchemeTools", true); - qDebug() << "Color Schemes support enabled"; - } else { - qDebug() << "Color Schemes support disabled: couldn't resolve required methods in the library"; - } - } else { - qDebug() << "Color Schemes support disabled: couldn't load the library" << colorSchemeTools.errorString(); - } -#endif } static const QSize defaultIconFileInfoHeight(50, 50); -Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) { +Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) +{ std::map::const_iterator itr = instance->fileCache.find(path); if (itr == instance->fileCache.end()) { QMimeDatabase db; @@ -227,69 +144,72 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) { size = defaultIconFileInfoHeight; } - itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.absoluteFilePath(), info.fileName(), size, type, p}))).first; + itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.fileName(), size, type, p}))).first; } return itr->second; } -Shared::Global * Shared::Global::getInstance() { +Shared::Global * Shared::Global::getInstance() +{ return instance; } -QString Shared::Global::getName(Message::State rl) { +QString Shared::Global::getName(Message::State rl) +{ return instance->messageState[static_cast(rl)]; } -QString Shared::Global::getName(Shared::Affiliation af) { +QString Shared::Global::getName(Shared::Affiliation af) +{ return instance->affiliation[static_cast(af)]; } -QString Shared::Global::getName(Shared::Availability av) { +QString Shared::Global::getName(Shared::Availability av) +{ return instance->availability[static_cast(av)]; } -QString Shared::Global::getName(Shared::ConnectionState cs) { +QString Shared::Global::getName(Shared::ConnectionState cs) +{ return instance->connectionState[static_cast(cs)]; } -QString Shared::Global::getName(Shared::Role rl) { +QString Shared::Global::getName(Shared::Role rl) +{ return instance->role[static_cast(rl)]; } -QString Shared::Global::getName(Shared::SubscriptionState ss) { +QString Shared::Global::getName(Shared::SubscriptionState ss) +{ return instance->subscriptionState[static_cast(ss)]; } -QString Shared::Global::getName(Shared::AccountPassword ap) { +QString Shared::Global::getName(Shared::AccountPassword ap) +{ return instance->accountPassword[static_cast(ap)]; } -QString Shared::Global::getName(Shared::TrustLevel tl) { - return instance->trustLevel[static_cast(tl)]; -} - -QString Shared::Global::getName(EncryptionProtocol ep) { - return instance->encryptionProtocols[static_cast(ep)]; -} - -void Shared::Global::setSupported(const QString& pluginName, bool support) { - std::map::iterator itr = instance->optionalFeatures.find(pluginName); - if (itr != instance->optionalFeatures.end()) { +void Shared::Global::setSupported(const QString& pluginName, bool support) +{ + std::map::iterator itr = instance->pluginSupport.find(pluginName); + if (itr != instance->pluginSupport.end()) { itr->second = support; } } -bool Shared::Global::supported(const QString& pluginName) { - std::map::iterator itr = instance->optionalFeatures.find(pluginName); - if (itr != instance->optionalFeatures.end()) +bool Shared::Global::supported(const QString& pluginName) +{ + std::map::iterator itr = instance->pluginSupport.find(pluginName); + if (itr != instance->pluginSupport.end()) { return itr->second; - + } return false; } -QString Shared::Global::getDescription(Shared::AccountPassword ap) { +QString Shared::Global::getDescription(Shared::AccountPassword ap) +{ return instance->accountPasswordDescription[static_cast(ap)]; } @@ -317,7 +237,7 @@ void Shared::Global::highlightInFileManager(const QString& path) qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: squawk wasn't compiled to support it, trying fallback"; #endif - QFileInfo info(path); + QFileInfo info = path; if (info.exists()) { QProcess proc; proc.start("xdg-mime", query); @@ -325,10 +245,11 @@ void Shared::Global::highlightInFileManager(const QString& path) QString output = proc.readLine().simplified(); QString folder; - if (info.isDir()) + if (info.isDir()) { folder = info.canonicalFilePath(); - else + } else { folder = info.canonicalPath(); + } if (output.contains(dolphinReg)) { //there is a bug on current (21.04.0) dolphin, it works correct only if you already have dolphin launched @@ -354,62 +275,14 @@ void Shared::Global::highlightInFileManager(const QString& path) } } -QIcon Shared::Global::createThemePreview(const QString& path) { -#ifdef WITH_KCONFIG - if (supported("colorSchemeTools")) { - QIcon* icon = createPreview(path); - QIcon localIcon = *icon; - deletePreview(icon); - return localIcon; - } -#endif - - return QIcon(); -} - -QString Shared::Global::getColorSchemeName(const QString& path) { -#ifdef WITH_KCONFIG - if (supported("colorSchemeTools")) { - QString res; - colorSchemeName(path, res); - return res; - } -#endif - - return ""; -} - -void Shared::Global::setTheme(const QString& path) { -#ifdef WITH_KCONFIG - if (supported("colorSchemeTools")) { - if (path.toLower() == "system") { - QApplication::setPalette(getInstance()->defaultSystemPalette); - } else { - QPalette pallete; - createPalette(path, pallete); - QApplication::setPalette(pallete); - } - } -#else - SHARED_UNUSED(path); - qDebug("setTheme() was called, but this version of squawk was compiled without KConfig support, ignoring"); -#endif -} - -void Shared::Global::setStyle(const QString& style) { - if (style.toLower() == "system") - QApplication::setStyle(getInstance()->defaultSystemStyle); - else - QApplication::setStyle(style); -} #define FROM_INT_INPL(Enum) \ template<> \ Enum Shared::Global::fromInt(int src) \ { \ - if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) \ + if (src < static_cast(Enum##Lowest) && src > static_cast(Enum##Highest)) { \ throw EnumOutOfRange(#Enum); \ - \ + } \ return static_cast(src); \ } \ template<> \ @@ -423,5 +296,3 @@ FROM_INT_INPL(Shared::SubscriptionState) FROM_INT_INPL(Shared::AccountPassword) FROM_INT_INPL(Shared::Avatar) FROM_INT_INPL(Shared::Availability) -FROM_INT_INPL(Shared::TrustLevel) -FROM_INT_INPL(Shared::EncryptionProtocol) diff --git a/shared/global.h b/shared/global.h index 627903f..03cf84d 100644 --- a/shared/global.h +++ b/shared/global.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHARED_GLOBAL_H +#define SHARED_GLOBAL_H #include "enums.h" #include "message.h" @@ -26,8 +27,7 @@ #include #include -#include -#include +#include #include #include #include @@ -41,12 +41,12 @@ #include #include #include -#include -#include namespace Shared { class Global { + Q_DECLARE_TR_FUNCTIONS(Global) + public: struct FileInfo { enum class Preview { @@ -55,7 +55,6 @@ namespace Shared { animation }; - QString path; QString name; QSize size; QMimeType mime; @@ -63,7 +62,7 @@ namespace Shared { }; Global(); - + static Global* getInstance(); static QString getName(Availability av); static QString getName(ConnectionState cs); @@ -72,8 +71,6 @@ namespace Shared { static QString getName(Role rl); static QString getName(Message::State rl); static QString getName(AccountPassword ap); - static QString getName(TrustLevel tl); - static QString getName(EncryptionProtocol ep); static QString getDescription(AccountPassword ap); @@ -84,13 +81,8 @@ namespace Shared { const std::deque role; const std::deque messageState; const std::deque accountPassword; - const std::deque trustLevel; - const std::deque encryptionProtocols; const std::deque accountPasswordDescription; - - const QString defaultSystemStyle; - const QPalette defaultSystemPalette; static bool supported(const QString& pluginName); static void setSupported(const QString& pluginName, bool support); @@ -99,21 +91,6 @@ namespace Shared { static FileInfo getFileInfo(const QString& path); static void highlightInFileManager(const QString& path); - static QIcon createThemePreview(const QString& path); - static QString getColorSchemeName(const QString& path); - static void setTheme(const QString& path); - static void setStyle(const QString& style); - const bool omemoSupport; - QFont defaultFont; - QFont monospaceFont; - QFont smallFont; - QFont headerFont; - QFont titleFont; - QFontMetrics defaultFontMetrics; - QFontMetrics monospaceMetrics; - QFontMetrics smallFontMetrics; - QFontMetrics headerFontMetrics; - QFontMetrics titleFontMetrics; template static T fromInt(int src); @@ -121,10 +98,12 @@ namespace Shared { template static T fromInt(unsigned int src); - class EnumOutOfRange: public Utils::Exception { + class EnumOutOfRange: + public Utils::Exception + { public: EnumOutOfRange(const std::string& p_name):Exception(), name(p_name) {} - + std::string getMessage() const{ return "An attempt to get enum " + name + " from integer out of range of that enum"; } @@ -135,7 +114,7 @@ namespace Shared { private: static Global* instance; - std::map optionalFeatures; + std::map pluginSupport; std::map fileCache; #ifdef WITH_KIO @@ -145,19 +124,7 @@ namespace Shared { static HighlightInFileManager hfm; #endif - -#ifdef WITH_KCONFIG - static QLibrary colorSchemeTools; - - typedef QIcon* (*CreatePreview)(const QString&); - typedef void (*DeletePreview)(QIcon*); - typedef void (*ColorSchemeName)(const QString&, QString&); - typedef void (*CreatePalette)(const QString&, QPalette&); - - static CreatePreview createPreview; - static DeletePreview deletePreview; - static ColorSchemeName colorSchemeName; - static CreatePalette createPalette; -#endif }; } + +#endif // SHARED_GLOBAL_H diff --git a/shared/icons.h b/shared/icons.h index dadb87e..540d3e9 100644 --- a/shared/icons.h +++ b/shared/icons.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHARED_ICONS_H +#define SHARED_ICONS_H #include @@ -47,7 +48,6 @@ static const std::deque fallbackSubscriptionStateThemeIconsLightBig = { static const std::deque fallbackConnectionStateThemeIconsLightBig = { ":images/fallback/light/big/state-offline.svg", ":images/fallback/light/big/state-sync.svg", - ":images/fallback/light/big/state-sync.svg", ":images/fallback/light/big/state-ok.svg", ":images/fallback/light/big/state-error.svg" }; @@ -73,7 +73,6 @@ static const std::deque fallbackSubscriptionStateThemeIconsLightSmall = static const std::deque fallbackConnectionStateThemeIconsLightSmall = { ":images/fallback/light/small/state-offline.svg", ":images/fallback/light/small/state-sync.svg", - ":images/fallback/light/small/state-sync.svg", ":images/fallback/light/small/state-ok.svg", ":images/fallback/light/small/state-error.svg" }; @@ -99,7 +98,6 @@ static const std::deque fallbackSubscriptionStateThemeIconsDarkBig = { static const std::deque fallbackConnectionStateThemeIconsDarkBig = { ":images/fallback/dark/big/state-offline.svg", ":images/fallback/dark/big/state-sync.svg", - ":images/fallback/dark/big/state-sync.svg", ":images/fallback/dark/big/state-ok.svg", ":images/fallback/dark/big/state-error.svg" }; @@ -125,7 +123,6 @@ static const std::deque fallbackSubscriptionStateThemeIconsDarkSmall = static const std::deque fallbackConnectionStateThemeIconsDarkSmall = { ":images/fallback/dark/small/state-offline.svg", ":images/fallback/dark/small/state-sync.svg", - ":images/fallback/dark/small/state-sync.svg", ":images/fallback/dark/small/state-ok.svg", ":images/fallback/dark/small/state-error.svg" }; @@ -174,9 +171,8 @@ static const std::map> icons = { {"unfavorite", {"draw-star", "unfavorite"}}, {"list-add", {"list-add", "add"}}, {"folder", {"folder", "folder"}}, - {"document-preview", {"document-preview", "document-preview"}}, - {"secure", {"security-high", "shield"}}, - {"lock", {"lock", "lock"}}, - {"unlock", {"unlock", "unlock"}} + {"document-preview", {"document-preview", "document-preview"}} }; } + +#endif // SHARED_ICONS_H diff --git a/shared/identity.cpp b/shared/identity.cpp deleted file mode 100644 index ef616d8..0000000 --- a/shared/identity.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "identity.h" - -Shared::Identity::Identity(): - category(), - type(), - language(), - name() {} - -bool Shared::Identity::operator==(const Shared::Identity& other) const { - return category == other.category && type == other.type && language == other.language && name == other.name; -} - -bool Shared::Identity::operator!=(const Shared::Identity& other) const { - return category != other.category || type != other.type || language != other.language || name != other.name; -} - -bool Shared::Identity::operator > (const Shared::Identity& other) const { - if (category > other.category) { - return true; - } else if (category < other.category) { - return false; - } else if (type > other.type) { - return true; - } else if (type < other.type) { - return false; - } else if (language > other.language) { - return true; - } else if (language < other.language) { - return false; - } else if (name > other.name) { - return true; - } else { - return false; - } -} - -bool Shared::Identity::operator < (const Shared::Identity& other) const { - if (category < other.category) { - return true; - } else if (category > other.category) { - return false; - } else if (type < other.type) { - return true; - } else if (type > other.type) { - return false; - } else if (language < other.language) { - return true; - } else if (language > other.language) { - return false; - } else if (name < other.name) { - return true; - } else { - return false; - } -} - -bool Shared::Identity::operator >= (const Shared::Identity& other) const { - if (category > other.category) { - return true; - } else if (category < other.category) { - return false; - } else if (type > other.type) { - return true; - } else if (type < other.type) { - return false; - } else if (language > other.language) { - return true; - } else if (language < other.language) { - return false; - } else if (name > other.name) { - return true; - } else if (name < other.name) { - return false; - } else { - return true; - } -} - -bool Shared::Identity::operator <= (const Shared::Identity& other) const { - if (category < other.category) { - return true; - } else if (category > other.category) { - return false; - } else if (type < other.type) { - return true; - } else if (type > other.type) { - return false; - } else if (language < other.language) { - return true; - } else if (language > other.language) { - return false; - } else if (name < other.name) { - return true; - } else if (name > other.name) { - return false; - } else { - return true; - } -} - -QDataStream & Shared::Identity::operator >> (QDataStream& stream) const { - stream << category; - stream << type; - stream << language; - stream << name; - - return stream; -} - -QDataStream & Shared::Identity::operator << (QDataStream& stream) { - stream >> category; - stream >> type; - stream >> language; - stream >> name; - - return stream; -} - -QDataStream & operator >> (QDataStream& stream, Shared::Identity& identity) { - identity << stream; - return stream; -} - -QDataStream & operator << (QDataStream& stream, const Shared::Identity& identity) { - identity >> stream; - return stream; -} - diff --git a/shared/identity.h b/shared/identity.h deleted file mode 100644 index c19102b..0000000 --- a/shared/identity.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef SHARED_IDENTITY_H -#define SHARED_IDENTITY_H - -#include -#include - -namespace Shared { - -class Identity { -public: - Identity(); - - QDataStream& operator << (QDataStream& stream); - QDataStream& operator >> (QDataStream& stream) const; - - bool operator < (const Identity& other) const; - bool operator > (const Identity& other) const; - bool operator >= (const Identity& other) const; - bool operator <= (const Identity& other) const; - bool operator == (const Identity& other) const; - bool operator != (const Identity& other) const; -public: - QString category; - QString type; - QString language; - QString name; -}; - -} - -QDataStream& operator << (QDataStream& stream, const Shared::Identity& identity); -QDataStream& operator >> (QDataStream& stream, Shared::Identity& identity); - -#endif //SHARED_IDENTITY_H diff --git a/shared/info.cpp b/shared/info.cpp deleted file mode 100644 index 2dd9181..0000000 --- a/shared/info.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "info.h" -Shared::Info::Info (const QString& addr, EntryType tp): - type(tp), - address(addr), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) -{ - switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(); - activeKeys = new std::list(); - inactiveKeys = new std::list(); - break; - default: - throw 352; - } -} - -Shared::Info::Info (): - type(EntryType::none), - address(""), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) {} - -Shared::Info::Info (const Shared::Info& other): - type(other.type), - address(other.address), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) -{ - switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(other.getVCardRef()); - activeKeys = new std::list(other.getActiveKeysRef()); - inactiveKeys = new std::list(other.getInactiveKeysRef()); - break; - default: - throw 353; - } -} - -Shared::Info::Info (Info&& other): - type(other.type), - address(other.address), - vcard(other.vcard), - activeKeys(other.activeKeys), - inactiveKeys(other.inactiveKeys) -{ - other.type = EntryType::none; -} - -Shared::Info& Shared::Info::operator = (Info&& other) { - type = other.type; - address = other.address; - vcard = other.vcard; - activeKeys = other.activeKeys; - inactiveKeys = other.inactiveKeys; - other.type = EntryType::none; - return *this; -} - -Shared::Info& Shared::Info::operator = (const Info& other) { - type = other.type; - address = other.address; - switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(other.getVCardRef()); - activeKeys = new std::list(other.getActiveKeysRef()); - inactiveKeys = new std::list(other.getInactiveKeysRef()); - break; - default: - throw 351; - } - return *this; -} - -Shared::Info::~Info () -{ - turnIntoNone(); -} - -void Shared::Info::turnIntoNone () { - switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - vcard = nullptr; - delete activeKeys; - activeKeys = nullptr; - delete inactiveKeys; - inactiveKeys = nullptr; - break; - default: - break; - } - type = EntryType::none; -} - -void Shared::Info::turnIntoContact (const Shared::VCard& crd, const std::list& aks, const std::list& iaks) { - switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; - } - type = EntryType::contact; -} - -void Shared::Info::turnIntoContact (Shared::VCard* crd, std::list* aks, std::list* iaks) { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; - } - type = EntryType::contact; -} - -void Shared::Info::turnIntoOwnAccount (const Shared::VCard& crd, const std::list& aks, const std::list& iaks) { - switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; - } - type = EntryType::ownAccount; -} - -void Shared::Info::turnIntoOwnAccount (Shared::VCard* crd, std::list* aks, std::list* iaks) { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; - } - type = EntryType::ownAccount; -} - -void Shared::Info::setAddress (const QString& addr) { - address = addr; -} - -QString Shared::Info::getAddress () const { - return address; -} - -const QString& Shared::Info::getAddressRef () const { - return address; -} - -Shared::EntryType Shared::Info::getType () const { - return type; -} - -std::list& Shared::Info::getActiveKeysRef () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 354; - } -} - -const std::list& Shared::Info::getActiveKeysRef () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 355; - } -} - -std::list* Shared::Info::getActiveKeys () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 356; - } -} - -const std::list* Shared::Info::getActiveKeys () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 357; - } -} - -std::list& Shared::Info::getInactiveKeysRef () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 358; - } -} - -const std::list& Shared::Info::getInactiveKeysRef () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 359; - } -} - -std::list* Shared::Info::getInactiveKeys () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 360; - } -} - -const std::list* Shared::Info::getInactiveKeys () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 361; - } -} - -const Shared::VCard& Shared::Info::getVCardRef () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 362; - } -} - -Shared::VCard& Shared::Info::getVCardRef () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 363; - } -} - -const Shared::VCard* Shared::Info::getVCard () const { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 364; - } -} - -Shared::VCard* Shared::Info::getVCard () { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 365; - } -} - -void Shared::Info::setActiveKeys (std::list* keys) { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - activeKeys = keys; - break; - default: - throw 366; - } -} - -void Shared::Info::setVCard (Shared::VCard* card) { - switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - vcard = card; - break; - default: - throw 367; - } -} diff --git a/shared/info.h b/shared/info.h deleted file mode 100644 index ae34ef2..0000000 --- a/shared/info.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "vcard.h" -#include "keyinfo.h" -#include "enums.h" - -#include - -namespace Shared { -/** - * This class should contain all nessesary data to display - * roster element info (contact, or out of roster contact, or MUC, or MIX in the future) - * - * under development yet - */ -class Info { -public: - Info (); - Info (const QString& address, EntryType = EntryType::none); - Info (const Info& other); - Info (Info&& other); - virtual ~Info (); - - Info& operator = (const Info& other); - Info& operator = (Info&& other); - QString getAddress () const; - const QString& getAddressRef () const; - void setAddress (const QString& address); - EntryType getType () const; - void turnIntoNone (); - void turnIntoContact (const VCard& card = VCard(), const std::list& activeKeys = {}, const std::list& inactiveKeys = {}); - void turnIntoContact (VCard* card = new VCard, std::list* activeKeys = new std::list, std::list* inactiveKeys = new std::list); - void turnIntoOwnAccount (const VCard& card = VCard(), const std::list& activeKeys = {}, const std::list& inactiveKeys = {}); - void turnIntoOwnAccount (VCard* card = new VCard, std::list* activeKeys = new std::list, std::list* inactiveKeys = new std::list); - const VCard& getVCardRef () const; - VCard& getVCardRef (); - const VCard* getVCard () const; - VCard* getVCard (); - void setVCard (Shared::VCard* card); - const std::list& getActiveKeysRef () const; - std::list& getActiveKeysRef (); - const std::list* getActiveKeys () const; - std::list* getActiveKeys (); - void setActiveKeys (std::list* keys); - const std::list& getInactiveKeysRef () const; - std::list& getInactiveKeysRef (); - const std::list* getInactiveKeys () const; - std::list* getInactiveKeys (); - -private: - EntryType type; - QString address; - VCard* vcard; - std::list* activeKeys; - std::list* inactiveKeys; -}; -} diff --git a/shared/keyinfo.cpp b/shared/keyinfo.cpp deleted file mode 100644 index f7d9e90..0000000 --- a/shared/keyinfo.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "keyinfo.h" - -using namespace Shared; - -Shared::KeyInfo::KeyInfo( - uint32_t p_id, - const QByteArray& p_fingerPrint, - const QString& p_label, - const QDateTime& p_lastInteraction, - Shared::TrustLevel p_trustLevel, - Shared::EncryptionProtocol p_protocol, - bool p_currentDevice -): - id(p_id), - fingerPrint(p_fingerPrint), - label(p_label), - lastInteraction(p_lastInteraction), - trustLevel(p_trustLevel), - protocol(p_protocol), - currentDevice(p_currentDevice) -{ -} - -Shared::KeyInfo::KeyInfo(): - id(0), - fingerPrint(), - label(), - lastInteraction(), - trustLevel(TrustLevel::undecided), - protocol(EncryptionProtocol::omemo2), - currentDevice(false) -{ -} - -Shared::KeyInfo::KeyInfo(const Shared::KeyInfo& other): - id(other.id), - fingerPrint(other.fingerPrint), - label(other.label), - lastInteraction(other.lastInteraction), - trustLevel(other.trustLevel), - protocol(other.protocol), - currentDevice(other.currentDevice) -{ -} - -Shared::KeyInfo & Shared::KeyInfo::operator=(const Shared::KeyInfo& other) { - id = other.id; - fingerPrint = other.fingerPrint; - label = other.label; - lastInteraction = other.lastInteraction; - trustLevel = other.trustLevel; - protocol = other.protocol; - currentDevice = other.currentDevice; - - return *this; -} diff --git a/shared/keyinfo.h b/shared/keyinfo.h deleted file mode 100644 index 500a2c1..0000000 --- a/shared/keyinfo.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include - -#include - -#include "enums.h" - -namespace Shared { - -class KeyInfo { -public: - KeyInfo( - uint32_t id, - const QByteArray& fingerPrint, - const QString& label, - const QDateTime& lastInteraction, - TrustLevel trustLevel, - EncryptionProtocol protocol = EncryptionProtocol::omemo2, - bool currentDevice = false - ); - KeyInfo(); - KeyInfo(const KeyInfo& other); - - KeyInfo& operator=(const KeyInfo& other); - -public: - uint32_t id; - QByteArray fingerPrint; - QString label; - QDateTime lastInteraction; - TrustLevel trustLevel; - EncryptionProtocol protocol; - bool currentDevice; - -}; - -} diff --git a/shared/message.cpp b/shared/message.cpp index 70fae63..e6b47b2 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -37,8 +37,7 @@ Shared::Message::Message(Shared::Message::Type p_type): originalMessage(), lastModified(), stanzaId(), - attachPath(), - encryption(EncryptionProtocol::none) + attachPath() {} Shared::Message::Message(): @@ -59,46 +58,53 @@ Shared::Message::Message(): originalMessage(), lastModified(), stanzaId(), - attachPath(), - encryption(EncryptionProtocol::none) + attachPath() {} -QString Shared::Message::getBody() const { +QString Shared::Message::getBody() const +{ return body; } -QString Shared::Message::getFrom() const { +QString Shared::Message::getFrom() const +{ QString from = jFrom; - if (rFrom.size() > 0) + if (rFrom.size() > 0) { from += "/" + rFrom; - + } return from; } -QString Shared::Message::getTo() const { +QString Shared::Message::getTo() const +{ QString to = jTo; - if (rTo.size() > 0) + if (rTo.size() > 0) { to += "/" + rTo; - + } return to; } -QString Shared::Message::getId() const { - if (id.size() > 0) +QString Shared::Message::getId() const +{ + if (id.size() > 0) { return id; - else + } else { return stanzaId; + } } -QDateTime Shared::Message::getTime() const { +QDateTime Shared::Message::getTime() const +{ return time; } -void Shared::Message::setBody(const QString& p_body) { +void Shared::Message::setBody(const QString& p_body) +{ body = p_body; } -void Shared::Message::setFrom(const QString& from) { +void Shared::Message::setFrom(const QString& from) +{ QStringList list = from.split("/"); if (list.size() == 1) { jFrom = from.toLower(); @@ -108,7 +114,8 @@ void Shared::Message::setFrom(const QString& from) { } } -void Shared::Message::setTo(const QString& to) { +void Shared::Message::setTo(const QString& to) +{ QStringList list = to.split("/"); if (list.size() == 1) { jTo = to.toLower(); @@ -118,237 +125,250 @@ void Shared::Message::setTo(const QString& to) { } } -void Shared::Message::setId(const QString& p_id) { +void Shared::Message::setId(const QString& p_id) +{ id = p_id; } -void Shared::Message::setTime(const QDateTime& p_time) { +void Shared::Message::setTime(const QDateTime& p_time) +{ time = p_time; } -QString Shared::Message::getFromJid() const { +QString Shared::Message::getFromJid() const +{ return jFrom; } -QString Shared::Message::getFromResource() const { +QString Shared::Message::getFromResource() const +{ return rFrom; } -QString Shared::Message::getToJid() const { +QString Shared::Message::getToJid() const +{ return jTo; } -QString Shared::Message::getToResource() const { +QString Shared::Message::getToResource() const +{ return rTo; } -QString Shared::Message::getErrorText() const { +QString Shared::Message::getErrorText() const +{ return errorText; } -QString Shared::Message::getPenPalJid() const { - if (outgoing) +QString Shared::Message::getPenPalJid() const +{ + if (outgoing) { return jTo; - else + } else { return jFrom; + } } -QString Shared::Message::getPenPalResource() const { - if (outgoing) +QString Shared::Message::getPenPalResource() const +{ + if (outgoing) { return rTo; - else + } else { return rFrom; + } } -Shared::Message::State Shared::Message::getState() const { +Shared::Message::State Shared::Message::getState() const +{ return state; } -bool Shared::Message::getEdited() const { +bool Shared::Message::getEdited() const +{ return edited; } -void Shared::Message::setFromJid(const QString& from) { +void Shared::Message::setFromJid(const QString& from) +{ jFrom = from.toLower(); } -void Shared::Message::setFromResource(const QString& from) { +void Shared::Message::setFromResource(const QString& from) +{ rFrom = from; } -void Shared::Message::setToJid(const QString& to) { +void Shared::Message::setToJid(const QString& to) +{ jTo = to.toLower(); } -void Shared::Message::setToResource(const QString& to) { +void Shared::Message::setToResource(const QString& to) +{ rTo = to; } -void Shared::Message::setErrorText(const QString& err) { - if (state == State::error) +void Shared::Message::setErrorText(const QString& err) +{ + if (state == State::error) { errorText = err; + } } -bool Shared::Message::getOutgoing() const { +bool Shared::Message::getOutgoing() const +{ return outgoing; } -void Shared::Message::setOutgoing(bool og) { +void Shared::Message::setOutgoing(bool og) +{ outgoing = og; } -bool Shared::Message::getForwarded() const { +bool Shared::Message::getForwarded() const +{ return forwarded; } -void Shared::Message::generateRandomId() { +void Shared::Message::generateRandomId() +{ id = generateUUID(); } -QString Shared::Message::getThread() const { +QString Shared::Message::getThread() const +{ return thread; } -void Shared::Message::setForwarded(bool fwd) { +void Shared::Message::setForwarded(bool fwd) +{ forwarded = fwd; } -void Shared::Message::setThread(const QString& p_body) { +void Shared::Message::setThread(const QString& p_body) +{ thread = p_body; } -QDateTime Shared::Message::getLastModified() const { +QDateTime Shared::Message::getLastModified() const +{ return lastModified; } -QString Shared::Message::getOriginalBody() const { +QString Shared::Message::getOriginalBody() const +{ return originalMessage; } -Shared::Message::Type Shared::Message::getType() const { +Shared::Message::Type Shared::Message::getType() const +{ return type; } -void Shared::Message::setType(Shared::Message::Type t) { +void Shared::Message::setType(Shared::Message::Type t) +{ type = t; } -void Shared::Message::setState(Shared::Message::State p_state) { +void Shared::Message::setState(Shared::Message::State p_state) +{ state = p_state; - if (state != State::error) + if (state != State::error) { errorText = ""; + } } -bool Shared::Message::serverStored() const { +bool Shared::Message::serverStored() const +{ return state == State::delivered || state == State::sent; } -void Shared::Message::setEdited(bool p_edited) { +void Shared::Message::setEdited(bool p_edited) +{ edited = p_edited; } -Shared::EncryptionProtocol Shared::Message::getEncryption() const { - return encryption; -} - -void Shared::Message::setEncryption(EncryptionProtocol encryption) { - Shared::Message::encryption = encryption; -} - -void Shared::Message::setError(const QString& text) { - state = State::error; - errorText = text; -} - -QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { - out << info.jFrom; - out << info.rFrom; - out << info.jTo; - out << info.rTo; - out << info.id; - out << info.body; - quint64 msecs = info.time.toMSecsSinceEpoch(); - out << msecs; - - out << info.thread; - out << (quint8)info.type; - out << info.outgoing; - out << info.forwarded; - out << info.oob; - out << (quint8)info.state; - out << info.edited; - if (info.state == Shared::Message::State::error) - out << info.errorText; - - if (info.edited) { - out << info.originalMessage; - out << info.lastModified; +void Shared::Message::serialize(QDataStream& data) const +{ + data << jFrom; + data << rFrom; + data << jTo; + data << rTo; + data << id; + data << body; + data << time; + data << thread; + data << (quint8)type; + data << outgoing; + data << forwarded; + data << oob; + data << (quint8)state; + data << edited; + if (state == State::error) { + data << errorText; } - out << info.stanzaId; - out << info.attachPath; - out << (quint8)info.encryption; - - return out; + if (edited) { + data << originalMessage; + data << lastModified; + } + data << stanzaId; + data << attachPath; } -QDataStream & operator>>(QDataStream& in, Shared::Message& info) { - in >> info.jFrom; - in >> info.rFrom; - in >> info.jTo; - in >> info.rTo; - in >> info.id; - in >> info.body; - - quint64 msecs; - in >> msecs; - info.time = QDateTime::fromMSecsSinceEpoch(msecs); - - in >> info.thread; +void Shared::Message::deserialize(QDataStream& data) +{ + data >> jFrom; + data >> rFrom; + data >> jTo; + data >> rTo; + data >> id; + data >> body; + data >> time; + data >> thread; quint8 t; - in >> t; - info.type = static_cast(t); - in >> info.outgoing; - in >> info.forwarded; - in >> info.oob; + data >> t; + type = static_cast(t); + data >> outgoing; + data >> forwarded; + data >> oob; quint8 s; - in >> s; - info.state = static_cast(s); - in >> info.edited; - if (info.state == Shared::Message::State::error) - in >> info.errorText; - - if (info.edited) { - in >> info.originalMessage; - in >> info.lastModified; + data >> s; + state = static_cast(s); + data >> edited; + if (state == State::error) { + data >> errorText; } - in >> info.stanzaId; - in >> info.attachPath; - quint8 e; - in >> e; - info.encryption = static_cast(e); - - return in; + if (edited) { + data >> originalMessage; + data >> lastModified; + } + data >> stanzaId; + data >> attachPath; } bool Shared::Message::change(const QMap& data) { QMap::const_iterator itr = data.find("state"); - if (itr != data.end()) + if (itr != data.end()) { setState(static_cast(itr.value().toUInt())); + } itr = data.find("outOfBandUrl"); - if (itr != data.end()) + if (itr != data.end()) { setOutOfBandUrl(itr.value().toString()); + } itr = data.find("attachPath"); - if (itr != data.end()) + if (itr != data.end()) { setAttachPath(itr.value().toString()); + } if (state == State::error) { itr = data.find("errorText"); - if (itr != data.end()) + if (itr != data.end()) { setErrorText(itr.value().toString()); + } } bool idChanged = false; @@ -366,8 +386,9 @@ bool Shared::Message::change(const QMap& data) QString newId = itr.value().toString(); if (stanzaId != newId) { setStanzaId(newId); - if (id.size() == 0) + if (id.size() == 0) { idChanged = true; + } } } @@ -377,17 +398,15 @@ bool Shared::Message::change(const QMap& data) if (body != b) { QMap::const_iterator dItr = data.find("stamp"); QDateTime correctionDate; - if (dItr != data.end()) + if (dItr != data.end()) { correctionDate = dItr.value().toDateTime(); - else + } else { correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied - + } if (!edited || lastModified < correctionDate) { - if (!edited) - originalMessage = body; - + originalMessage = body; lastModified = correctionDate; - setBody(b); + setBody(body); setEdited(true); } } @@ -395,47 +414,57 @@ bool Shared::Message::change(const QMap& data) QMap::const_iterator dItr = data.find("stamp"); if (dItr != data.end()) { QDateTime ntime = dItr.value().toDateTime(); - if (time != ntime) + if (time != ntime) { setTime(ntime); + } } } return idChanged; } -void Shared::Message::setCurrentTime() { +void Shared::Message::setCurrentTime() +{ time = QDateTime::currentDateTimeUtc(); } -QString Shared::Message::getOutOfBandUrl() const { +QString Shared::Message::getOutOfBandUrl() const +{ return oob; } -bool Shared::Message::hasOutOfBandUrl() const { +bool Shared::Message::hasOutOfBandUrl() const +{ return oob.size() > 0; } -void Shared::Message::setOutOfBandUrl(const QString& url) { +void Shared::Message::setOutOfBandUrl(const QString& url) +{ oob = url; } -bool Shared::Message::storable() const { +bool Shared::Message::storable() const +{ return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0); } -void Shared::Message::setStanzaId(const QString& sid) { +void Shared::Message::setStanzaId(const QString& sid) +{ stanzaId = sid; } -QString Shared::Message::getStanzaId() const { +QString Shared::Message::getStanzaId() const +{ return stanzaId; } -QString Shared::Message::getAttachPath() const { +QString Shared::Message::getAttachPath() const +{ return attachPath; } -void Shared::Message::setAttachPath(const QString& path) { +void Shared::Message::setAttachPath(const QString& path) +{ attachPath = path; } @@ -443,15 +472,18 @@ Shared::Message::Change::Change(const QMap& _data): data(_data), idModified(false) {} -void Shared::Message::Change::operator()(Shared::Message& msg) { +void Shared::Message::Change::operator()(Shared::Message& msg) +{ idModified = msg.change(data); } -void Shared::Message::Change::operator()(Shared::Message* msg) { +void Shared::Message::Change::operator()(Shared::Message* msg) +{ idModified = msg->change(data); } -bool Shared::Message::Change::hasIdBeenModified() const { +bool Shared::Message::Change::hasIdBeenModified() const +{ return idModified; } diff --git a/shared/message.h b/shared/message.h index b81881b..aa91af6 100644 --- a/shared/message.h +++ b/shared/message.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHAPER_MESSAGE_H +#define SHAPER_MESSAGE_H #include #include @@ -24,22 +25,12 @@ #include #include -#include "enums.h" - -namespace Shared { - class Message; -} - -QDataStream& operator << (QDataStream& out, const Shared::Message& info); -QDataStream& operator >> (QDataStream& in, Shared::Message& info); namespace Shared { /** * @todo write docs */ class Message { - friend QDataStream& ::operator << (QDataStream& out, const Shared::Message& info); - friend QDataStream& ::operator >> (QDataStream& in, Shared::Message& info); public: enum Type { error, @@ -95,9 +86,6 @@ public: bool change(const QMap& data); void setStanzaId(const QString& sid); void setAttachPath(const QString& path); - void setEncryption(EncryptionProtocol encryption); - - void setError(const QString& text); QString getFrom() const; QString getFromJid() const; @@ -127,7 +115,9 @@ public: QString getOriginalBody() const; QString getStanzaId() const; QString getAttachPath() const; - EncryptionProtocol getEncryption() const; + + void serialize(QDataStream& data) const; + void deserialize(QDataStream& data); private: QString jFrom; @@ -149,7 +139,8 @@ private: QDateTime lastModified; QString stanzaId; QString attachPath; - EncryptionProtocol encryption; }; } + +#endif // SHAPER_MESSAGE_H diff --git a/shared/messageinfo.cpp b/shared/messageinfo.cpp index a26f23f..7502a6e 100644 --- a/shared/messageinfo.cpp +++ b/shared/messageinfo.cpp @@ -43,19 +43,3 @@ Shared::MessageInfo & Shared::MessageInfo::operator=(const Shared::MessageInfo& return *this; } - -QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info) { - in >> info.account; - in >> info.jid; - in >> info.messageId; - - return in; -} - -QDataStream& operator <<( QDataStream& out, const Shared::MessageInfo& info) { - out << info.account; - out << info.jid; - out << info.messageId; - - return out; -} diff --git a/shared/messageinfo.h b/shared/messageinfo.h index f06371b..942d88c 100644 --- a/shared/messageinfo.h +++ b/shared/messageinfo.h @@ -16,12 +16,16 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHARED_MESSAGEINFO_H +#define SHARED_MESSAGEINFO_H #include -#include namespace Shared { + +/** + * @todo write docs + */ struct MessageInfo { MessageInfo(); MessageInfo(const QString& acc, const QString& j, const QString& id); @@ -36,5 +40,4 @@ struct MessageInfo { } -QDataStream& operator << (QDataStream& out, const Shared::MessageInfo& info); -QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info); +#endif // SHARED_MESSAGEINFO_H diff --git a/shared/order.h b/shared/order.h new file mode 100644 index 0000000..fa9379b --- /dev/null +++ b/shared/order.h @@ -0,0 +1,154 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ORDER_H +#define ORDER_H + +#include +#include + +#include "exception.h" + +namespace W +{ + template > + class Order + { + public: + class Duplicates: + public Utils::Exception + { + public: + Duplicates():Exception(){} + + std::string getMessage() const{return "Inserting element duplicates existing";} + }; + + class NotFound: + public Utils::Exception + { + public: + NotFound():Exception(){} + + std::string getMessage() const{return "Erasing element haven't been found";} + }; + + protected: + typedef std::list List; + + public: + typedef typename List::size_type size_type; + typedef typename List::const_iterator const_iterator; + typedef typename List::iterator iterator; + + protected: + typedef std::map Map; + typedef typename Map::const_iterator m_const_itr; + typedef typename Map::iterator m_itr; + + public: + Order(): + order(), + r_map() + {} + ~Order() {}; + + size_type size() const { + return order.size(); + } + + void push_back(data_type element) { + m_const_itr m_itr = r_map.find(element); + if (m_itr != r_map.end()) { + throw Duplicates(); + } + + const_iterator itr = order.insert(order.end(), element); + r_map.insert(std::make_pair(element, itr)); + } + + void erase(data_type element) { + m_const_itr itr = r_map.find(element); + if (itr == r_map.end()) { + throw NotFound(); + } + order.erase(itr->second); + r_map.erase(itr); + + } + + void clear() { + order.clear(); + r_map.clear(); + } + + void insert(const_iterator pos, data_type element) { + m_const_itr m_itr = r_map.find(element); + if (m_itr != r_map.end()) { + throw Duplicates(); + } + + const_iterator itr = order.insert(pos, element); + r_map.insert(std::make_pair(element, itr)); + } + + void insert(iterator pos, data_type element) { + m_const_itr m_itr = r_map.find(element); + if (m_itr != r_map.end()) { + throw Duplicates(); + } + + const_iterator itr = order.insert(pos, element); + r_map.insert(std::make_pair(element, itr)); + } + + const_iterator find(data_type element) const { + m_const_itr itr = r_map.find(element); + + if (itr == r_map.end()) { + return end(); + } else { + return itr->second; + } + } + + const_iterator begin() const { + return order.begin(); + } + + const_iterator end() const { + return order.end(); + } + + iterator begin() { + return order.begin(); + } + + iterator end() { + return order.end(); + } + + private: + List order; + Map r_map; + }; +} + + + +#endif // ORDER_H diff --git a/shared/pathcheck.cpp b/shared/pathcheck.cpp deleted file mode 100644 index c32f96c..0000000 --- a/shared/pathcheck.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "pathcheck.h" - -QRegularExpression squawk("^squawk:\\/\\/"); -QString Shared::downloadsPathCheck() -{ - QSettings settings; - QVariant dpv = settings.value("downloadsPath"); - QString path; - if (!dpv.isValid()) { - path = defaultDownloadsPath(); - qDebug() << "no downloadsPath variable in config, using default" << path; - path = getAbsoluteWritablePath(path); - return path; - } else { - path = dpv.toString(); - path = getAbsoluteWritablePath(path); - if (path.size() == 0) { - path = defaultDownloadsPath(); - qDebug() << "falling back to the default downloads path" << path; - path = getAbsoluteWritablePath(path); - } - return path; - } -} - -QString Shared::defaultDownloadsPath() -{ - return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + QApplication::applicationName(); -} - -QString Shared::getAbsoluteWritablePath(const QString& path) -{ - QDir location(path); - if (!location.exists()) { - bool res = location.mkpath(location.absolutePath()); - if (!res) { - qDebug() << "couldn't create directory" << path; - return ""; - } - } - QFileInfo info(location.absolutePath()); - if (info.isWritable()) { - return location.absolutePath(); - } else { - qDebug() << "directory" << path << "is not writable"; - return ""; - } -} - -QString Shared::resolvePath(QString path) -{ - QSettings settings; - QVariant dpv = settings.value("downloadsPath"); - return path.replace(squawk, dpv.toString() + "/"); -} - -QString Shared::squawkifyPath(QString path) -{ - QSettings settings; - QString current = settings.value("downloadsPath").toString(); - - if (path.startsWith(current)) { - path.replace(0, current.size() + 1, "squawk://"); - } - - return path; -} - -bool Shared::isSubdirectoryOfSettings(const QString& path) -{ - - QSettings settings; - QDir oldPath(settings.value("downloadsPath").toString()); - QDir newPath(path); - - return newPath.canonicalPath().startsWith(oldPath.canonicalPath()); -} - diff --git a/shared/pathcheck.h b/shared/pathcheck.h deleted file mode 100644 index 1608241..0000000 --- a/shared/pathcheck.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Shared { - -QString downloadsPathCheck(); -QString downloadsPathCheck(QString path); -QString defaultDownloadsPath(); - -QString getAbsoluteWritablePath(const QString& path); -QString resolvePath(QString path); -QString squawkifyPath(QString path); -bool isSubdirectoryOfSettings(const QString& path); - -} diff --git a/shared/shared.h b/shared/shared.h index ba9a2ed..1e86c5a 100644 --- a/shared/shared.h +++ b/shared/shared.h @@ -19,7 +19,6 @@ #ifndef SHARED_H #define SHARED_H -#include "defines.h" #include "enums.h" #include "global.h" #include "icons.h" @@ -27,6 +26,5 @@ #include "messageinfo.h" #include "utils.h" #include "vcard.h" -#include "keyinfo.h" #endif // SHARED_H diff --git a/shared/trustsummary.cpp b/shared/trustsummary.cpp deleted file mode 100644 index 47abb16..0000000 --- a/shared/trustsummary.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "trustsummary.h" - -const std::set Shared::TrustSummary::trustedLevels({ - Shared::TrustLevel::authenticated, - Shared::TrustLevel::automaticallyTrusted, - Shared::TrustLevel::manuallyTrusted -}); -const std::set Shared::TrustSummary::untrustedLevels({ - Shared::TrustLevel::undecided, - Shared::TrustLevel::automaticallyDistrusted, - Shared::TrustLevel::manuallyDistrusted -}); - -const std::map Shared::TrustSummary::protocolKeys({ - {Shared::EncryptionProtocol::none, "none"}, - {Shared::EncryptionProtocol::omemo, "eu.siacs.conversations.axolotl"}, - {Shared::EncryptionProtocol::omemo1, "urn:xmpp:omemo:1"}, - {Shared::EncryptionProtocol::omemo2, "urn:xmpp:omemo:2"} -}); -const std::map Shared::TrustSummary::protocolValues({ - {"none", Shared::EncryptionProtocol::none}, - {"eu.siacs.conversations.axolotl", Shared::EncryptionProtocol::omemo}, - {"urn:xmpp:omemo:1", Shared::EncryptionProtocol::omemo1}, - {"urn:xmpp:omemo:2", Shared::EncryptionProtocol::omemo2} -}); - -Shared::TrustSummary::TrustSummary(): - data() -{} - -void Shared::TrustSummary::set(Shared::EncryptionProtocol protocol, Shared::TrustLevel level, uint8_t amount) { - Data::iterator itr = data.find(protocol); - if (itr == data.end()) { - if (amount == 0) - return; - - itr = data.insert(std::make_pair(protocol, Amounts())).first; - } - - Amounts& am = itr->second; - Amounts::iterator aitr = am.find(level); - if (aitr == am.end()) { - if (amount == 0) - return; - - am.emplace(level, amount); - return; - } - if (amount == 0) { - if (am.size() == 1) - data.erase(itr); - else - am.erase(aitr); - - return; - } - aitr->second = amount; -} - -uint8_t Shared::TrustSummary::amount(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) const { - Data::const_iterator itr = data.find(protocol); - if (itr == data.end()) - return 0; - - const Amounts& am = itr->second; - Amounts::const_iterator aitr = am.find(level); - if (aitr == am.end()) - return 0; - - return aitr->second; -} - -uint8_t Shared::TrustSummary::increment(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) { - Data::iterator itr = data.find(protocol); - if (itr == data.end()) - itr = data.insert(std::make_pair(protocol, Amounts())).first; - - Amounts& am = itr->second; - Amounts::iterator aitr = am.find(level); - if (aitr == am.end()) { - am.emplace(level, 1); - return 1; - } - uint8_t& value = aitr->second; - return ++value; -} - -uint8_t Shared::TrustSummary::decrement(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) { - Data::iterator itr = data.find(protocol); - if (itr == data.end()) - return 0; //should never happen, shall I better throw an exception? - - Amounts& am = itr->second; - Amounts::iterator aitr = am.find(level); - if (aitr == am.end()) - return 0; //should never happen, shall I better throw an exception? - - uint8_t& value = aitr->second; - uint8_t result = --value; - if (value == 0) { - if (am.size() == 1) - data.erase(itr); - else - am.erase(aitr); - } - return result; -} - -bool Shared::TrustSummary::hasKeys(Shared::EncryptionProtocol protocol) const { - return data.count(protocol) > 0; -} - -bool Shared::TrustSummary::hasTrustedKeys(Shared::EncryptionProtocol protocol) const { - Data::const_iterator itr = data.find(protocol); - if (itr == data.end()) - return false; - - for (const std::pair& pair : itr->second) { - if (trustedLevels.count(pair.first) > 0) - return true; - } - - return false; -} - -bool Shared::TrustSummary::hasUntrustedKeys(Shared::EncryptionProtocol protocol) const { - Data::const_iterator itr = data.find(protocol); - if (itr == data.end()) - return false; - - for (const std::pair& pair : itr->second) { - if (untrustedLevels.count(pair.first) > 0) - return true; - } - - return false; -} - -bool Shared::TrustSummary::operator==(const Shared::TrustSummary& other) { - return data == other.data; -} - -bool Shared::TrustSummary::operator!=(const Shared::TrustSummary& other) { - return data != other.data; -} diff --git a/shared/trustsummary.h b/shared/trustsummary.h deleted file mode 100644 index e663a9d..0000000 --- a/shared/trustsummary.h +++ /dev/null @@ -1,64 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef SHARED_TRUSTSUMMARY_H -#define SHARED_TRUSTSUMMARY_H - -#include - -#include -#include - -#include "enums.h" - -namespace Shared { - -class TrustSummary { -public: - TrustSummary(); - - bool operator == (const TrustSummary& other); - bool operator != (const TrustSummary& other); - - void set(EncryptionProtocol protocol, TrustLevel level, uint8_t amount); - uint8_t increment(EncryptionProtocol protocol, TrustLevel level); - uint8_t decrement(EncryptionProtocol protocol, TrustLevel level); - - uint8_t amount(EncryptionProtocol protocol, TrustLevel level) const; - bool hasKeys(EncryptionProtocol protocol) const; - bool hasTrustedKeys(EncryptionProtocol protocol) const; - bool hasUntrustedKeys(EncryptionProtocol protocol) const; - -private: - typedef std::map Amounts; - typedef std::map Data; - - Data data; - -public: - static const std::map protocolKeys; - static const std::map protocolValues; - -private: - static const std::set trustedLevels; - static const std::set untrustedLevels; -}; - -} - -Q_DECLARE_METATYPE(Shared::TrustSummary) - -#endif // SHARED_TRUSTSUMMARY_H diff --git a/shared/utils.cpp b/shared/utils.cpp index 518d288..a7a4ecb 100644 --- a/shared/utils.cpp +++ b/shared/utils.cpp @@ -40,5 +40,5 @@ QString Shared::processMessageBody(const QString& msg) { QString processed = msg.toHtmlEscaped(); processed.replace(urlReg, "\\1"); - return "

" + processed + "

"; + return "

" + processed + "

"; } diff --git a/shared/utils.h b/shared/utils.h index 019f611..6dcb141 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -16,13 +16,16 @@ * along with this program. If not, see . */ -#pragma once +#ifndef SHARED_UTILS_H +#define SHARED_UTILS_H #include #include #include #include +// #include "KIO/OpenFileManagerWindowJob" + #include namespace Shared { @@ -68,10 +71,6 @@ static const std::vector colorPalette = { QColor(17, 17, 80), QColor(54, 54, 94) }; - -enum class Hover { - nothing, - text, - anchor -}; } + +#endif // SHARED_UTILS_H diff --git a/core/delayManager/owncardinternal.h b/signalcatcher_win32.cpp similarity index 63% rename from core/delayManager/owncardinternal.h rename to signalcatcher_win32.cpp index 30d7c56..ca7b5a2 100644 --- a/core/delayManager/owncardinternal.h +++ b/signalcatcher_win32.cpp @@ -1,6 +1,6 @@ /* * Squawk messenger. - * Copyright (C) 2019 Yury Gubich + * Copyright (C) 2021 Shunf4 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,21 +16,27 @@ * along with this program. If not, see . */ -#pragma once - -#include "job.h" - -namespace Core { -namespace DelayManager { - -class OwnCardInternal : public Job { -protected: - OwnCardInternal(Id id, Type type); - -public: - OwnCardInternal(Id id); - OwnCardInternal(const OwnCardInternal& other); -}; +#include "signalcatcher.h" +#include +SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): + QObject(parent), + app(p_app) +{ } + +SignalCatcher::~SignalCatcher() +{} + +void SignalCatcher::handleSigInt() +{ +} + +void SignalCatcher::intSignalHandler(int unused) +{ +} + +int SignalCatcher::setup_unix_signal_handlers() +{ + return 0; } diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 6bdb6c9..86d2a8c 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,12 +1,11 @@ -find_package(Qt${QT_VERSION_MAJOR}LinguistTools) +find_package(Qt5LinguistTools) set(TS_FILES - squawk.en.ts squawk.ru.ts squawk.pt_BR.ts ) -qt_add_translation(QM_FILES ${TS_FILES}) +qt5_add_translation(QM_FILES ${TS_FILES}) add_custom_target(translations ALL DEPENDS ${QM_FILES}) -install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n) +install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n) add_dependencies(${CMAKE_PROJECT_NAME} translations) diff --git a/translations/squawk.en.ts b/translations/squawk.en.ts deleted file mode 100644 index db178ac..0000000 --- a/translations/squawk.en.ts +++ /dev/null @@ -1,1340 +0,0 @@ - - - - - About - - - About Squawk - About window header - About Squawk - - - - Squawk - Squawk - - - - About - Tab title - About - - - - XMPP (jabber) messenger - XMPP (jabber) messenger - - - - (c) 2019 - 2022, Yury Gubich - (c) 2019 - 2022, Yury Gubich - - - - <a href="https://git.macaw.me/blue/squawk">Project site</a> - <a href="https://git.macaw.me/blue/squawk">Project site</a> - - - - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - - - - Components - Tab header - Components - - - - - - Version - Version - - - - - - 0.0.0 - 0.0.0 - - - - Report Bugs - Report Bugs - - - - Please report any bug you find! -To report bugs you can use: - Please report any bug you find! -To report bugs you can use: - - - - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - - - - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - - - - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - - - - Thanks To - Thanks to - - - - Vae - Vae - - - - Major refactoring, bug fixes, constructive criticism - Major refactoring, bug fixes, constructive criticism - - - - Shunf4 - Shunf4 - - - - Major refactoring, bug fixes, build adaptations for Windows and MacOS - Major refactoring, bug fixes, build adaptations for Windows and MacOS - - - - Bruno F. Fontes - Bruno F. Fontes - - - - Brazilian Portuguese translation - Brazilian Portuguese translation - - - - - (built against %1) - (built against %1) - - - - License - License - - - - Account - - - Account - Window title - Account - - - - Your account login - Tooltip - Your account login - - - - john_smith1987 - Login placeholder - john_smith1987 - - - - Server - Server - - - - A server address of your account. Like 404.city or macaw.me - Tooltip - A server address of your account. Like 404.city or macaw.me - - - - macaw.me - Placeholder - macaw.me - - - - Login - Login - - - - Password - Password - - - - Password of your account - Tooltip - Password of your account - - - - Name - Name - - - - Just a name how would you call this account, doesn't affect anything - Just a name how would you call this account, doesn't affect anything (cant be changed) - - - - John - Placeholder - John - - - - Resource - Resource - - - - A resource name like "Home" or "Work" - Tooltip - A resource name like "Home" or "Work" - - - - QXmpp - Default resource - QXmpp - - - - Password storage - Password storage - - - - Active - Active - - - - enable - enable - - - - Accounts - - - Accounts - Accounts - - - - Delete - Delete - - - - Add - Add - - - - Edit - Edit - - - - Change password - Change password - - - - Connect - Connect - - - - Deactivate - Deactivate - - - - - Activate - Activate - - - - Application - - - Quit - Quit - - - - - Minimize to tray - Minimize to tray - - - - Show Squawk - Show Squawk - - - - from - from - - - - Attached file - Attached file - - - - Mark as Read - Mark as Read - - - - Open conversation - Open conversation - - - - Conversation - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - Type your message here... - Placeholder - Type your message here... - - - - Paste Image - Paste Image - - - - Drop files here to attach them to your message - Drop files here to attach them to your message - - - - Chose a file to send - Chose a file to send - - - - Try sending again - Try sending again - - - - Copy selected - Copy selected - - - - Copy message - Copy message - - - - Open - Open - - - - Show in folder - Show in folder - - - - Edit - Edit - - - - Editing message... - Editing message... - - - - CredentialsPrompt - - - Authentication error: %1 - Window title - Authentication error: %1 - - - - Couldn't authenticate account %1: login or password is icorrect. -Would you like to check them and try again? - Couldn't authenticate account %1: login or password is incorrect. -Would you like to check them and try again? - - - - Login - Login - - - - Your account login (without @server.domain) - Tooltip - Your account login (without @server.domain) - - - - Password - Password - - - - Your password - Password - - - - DialogQueue - - - Input the password for account %1 - Input the password for account %1 - - - - Password for account %1 - Password for account %1 - - - - Global - - - Online - Availability - Online - - - - Away - Availability - Away - - - - Absent - Availability - Absent - - - - Busy - Availability - Busy - - - - Chatty - Availability - Chatty - - - - Invisible - Availability - Invisible - - - - Offline - Availability - Offline - - - - Disconnected - ConnectionState - Disconnected - - - - Connecting - ConnectionState - Connecting - - - - Connected - ConnectionState - Connected - - - - Error - ConnectionState - Error - - - - None - SubscriptionState - None - - - - From - SubscriptionState - From - - - - To - SubscriptionState - To - - - - Both - SubscriptionState - Both - - - - Unknown - SubscriptionState - Unknown - - - - Unspecified - Affiliation - Unspecified - - - - Outcast - Affiliation - Outcast - - - - Nobody - Affiliation - Nobody - - - - Member - Affiliation - Member - - - - Admin - Affiliation - Admin - - - - Owner - Affiliation - Owner - - - - Unspecified - Role - Unspecified - - - - Nobody - Role - Nobody - - - - Visitor - Role - Visitor - - - - Participant - Role - Participant - - - - Moderator - Role - Moderator - - - - Pending - MessageState - Pending - - - - Sent - MessageState - Sent - - - - Delivered - MessageState - Delivered - - - - Error - MessageState - Error - - - - Plain - AccountPassword - Plain - - - - Jammed - AccountPassword - Jammed - - - - Always Ask - AccountPassword - Always Ask - - - - KWallet - AccountPassword - KWallet - - - - Your password is going to be stored in config file in plain text - AccountPasswordDescription - Your password is going to be stored in config file in plain text - - - - Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not - AccountPasswordDescription - Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not - - - - Squawk is going to query you for the password on every start of the program - AccountPasswordDescription - Squawk is going to query you for the password on every start of the program - - - - Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions - AccountPasswordDescription - Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions - - - - JoinConference - - - Join new conference - Join new conference - - - - JID - JID - - - - Room JID - Room JID - - - - identifier@conference.server.org - identifier@conference.server.org - - - - Account - Account - - - - Join on login - Join on login - - - - If checked Squawk will try to join this conference on login - If checked Squawk will try to join this conference on login - - - - Nick name - Nick name - - - - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name - - - - John - John - - - - MessageLine - - - - Download - Download - - - - Models::Room - - - Subscribed - Subscribed - - - - Temporarily unsubscribed - Temporarily unsubscribed - - - - Temporarily subscribed - Temporarily subscribed - - - - Unsubscribed - Unsubscribed - - - - Models::Roster - - - New messages - New messages - - - - - - New messages: - New messages: - - - - - Jabber ID: - Jabber ID: - - - - - - Availability: - Availability: - - - - - - Status: - Status: - - - - - - Subscription: - Subscription: - - - - Affiliation: - Affiliation: - - - - Role: - Role: - - - - Online contacts: - Online contacts: - - - - Total contacts: - Total contacts: - - - - Members: - Members: - - - - NewContact - - - Add new contact - Window title - Add new contact - - - - Account - Account - - - - An account that is going to have new contact - An account that is going to have new contact - - - - JID - JID - - - - Jabber id of your new contact - Jabber id of your new contact - - - - name@server.dmn - Placeholder - name@server.dmn - - - - Name - Name - - - - The way this new contact will be labeled in your roster (optional) - The way this new contact will be labeled in your roster (optional) - - - - John Smith - John Smith - - - - PageAppearance - - - Theme - Style - - - - Color scheme - Color scheme - - - - - - - System - System - - - - PageGeneral - - - Downloads path - Downloads path - - - - Tray icon - Tray icon - - - - Mimimize Squawk to tray when closing main window - Mimimize Squawk to tray when closing main window - - - - Hide tray icon - Hide tray icon - - - - Hide tray icon when Squawk main window is visible - Hide tray icon when Squawk main window is visible - - - Close to tray icon - Close to tray icon - - - - Browse - Browse - - - - Tray is not available for your system - Tray is not available for your system - - - - Select where downloads folder is going to be - Select where downloads folder is going to be - - - - Settings - - - Preferences - Window title - Preferences - - - - - General - General - - - - Appearance - Appearance - - - - Apply - Apply - - - - Cancel - Cancel - - - - Ok - Ok - - - - Squawk - - - squawk - Squawk - - - - Please select a contact to start chatting - Please select a contact to start chatting - - - - Settings - Settings - - - - Squawk - Menu bar entry - Squawk - - - - Help - Help - - - - Accounts - Accounts - - - - Quit - Quit - - - - Add contact - Add contact - - - - Add conference - Join conference - - - - Preferences - Preferences - - - - About Squawk - About Squawk - - - - Deactivate - Deactivate - - - - Activate - Activate - - - - - VCard - VCard - - - - - - Remove - Remove - - - - Open dialog - Open dialog - - - - - Unsubscribe - Unsubscribe - - - - - Subscribe - Subscribe - - - - Rename - Rename - - - - Input new name for %1 -or leave it empty for the contact -to be displayed as %1 - Input new name for %1 -or leave it empty for the contact -to be displayed as %1 - - - - Renaming %1 - Renaming %1 - - - - Groups - Groups - - - - New group - New group - - - - New group name - New group name - - - - Add %1 to a new group - Add %1 to a new group - - - - Open conversation - Open conversation - - - - %1 account card - %1 account card - - - - %1 contact card - %1 contact card - - - - Downloading vCard - Downloading vCard - - - - VCard - - - Received 12.07.2007 at 17.35 - Never updated - - - - - General - General - - - - Organization - Organization - - - - Middle name - Middle name - - - - First name - First name - - - - Last name - Last name - - - - Nick name - Nick name - - - - Birthday - Birthday - - - - Organization name - Organization name - - - - Unit / Department - Unit / Department - - - - Role / Profession - Role / Profession - - - - Job title - Job title - - - - Full name - Full name - - - - Personal information - Personal information - - - - - Contact - Contact - - - - Addresses - Addresses - - - - E-Mail addresses - E-Mail addresses - - - - Jabber ID - Jabber ID - - - - Web site - Web site - - - - Phone numbers - Phone numbers - - - - - Description - Description - - - - Set avatar - Set avatar - - - - Clear avatar - Clear avatar - - - - Account %1 card - Account %1 card - - - - Contact %1 card - Contact %1 card - - - - Received %1 at %2 - Received %1 at %2 - - - - Chose your new avatar - Chose your new avatar - - - - Images (*.png *.jpg *.jpeg) - Images (*.png *.jpg *.jpeg) - - - - Add email address - Add email address - - - - Unset this email as preferred - Unset this email as preferred - - - - Set this email as preferred - Set this email as preferred - - - - Remove selected email addresses - Remove selected email addresses - - - - Copy selected emails to clipboard - Copy selected emails to clipboard - - - - Add phone number - Add phone number - - - - Unset this phone as preferred - Unset this phone as preferred - - - - Set this phone as preferred - Set this phone as preferred - - - - Remove selected phone numbers - Remove selected phone numbers - - - - Copy selected phones to clipboard - Copy selected phones to clipboard - - - diff --git a/translations/squawk.pt_BR.ts b/translations/squawk.pt_BR.ts index 5d76ade..4330979 100644 --- a/translations/squawk.pt_BR.ts +++ b/translations/squawk.pt_BR.ts @@ -1,679 +1,322 @@ - - About - - - About Squawk - Sorbe Squawk - - - - Squawk - Squawk - - - - About - Tab title - Sobre - - - - XMPP (jabber) messenger - XMPP (jabber) mensageiro - - - - (c) 2019 - 2022, Yury Gubich - (c) 2019 - 2022, Yury Gubich - - - - <a href="https://git.macaw.me/blue/squawk">Project site</a> - <a href="https://git.macaw.me/blue/squawk">Site do projeto</a> - - - - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Licença: GNU General Public License versão 3</a> - - - - Components - Componentes - - - - - - Version - Versão - - - - - - 0.0.0 - 0.0.0 - - - - Report Bugs - Relatório de erros - - - - Please report any bug you find! -To report bugs you can use: - Por favor reportar qualquer erro que você encontrar! -Para relatar bugs você pode usar: - - - - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - <a href="https://git.macaw.me/blue/squawk/issues">Rastreador de bugs do projeto</> - - - - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - - - - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - - - - Thanks To - Graças ao - - - - Vae - Vae - - - - Major refactoring, bug fixes, constructive criticism - Refatoração importante, correção de erros, críticas construtivas - - - - Shunf4 - Shunf4 - - - - Major refactoring, bug fixes, build adaptations for Windows and MacOS - Refatoração importante, correção de erros, adaptações de construção para Windows e MacOS - - - - Bruno F. Fontes - Bruno F. Fontes - - - - Brazilian Portuguese translation - Tradução para o português do Brasil - - - - - (built against %1) - (Versão durante a compilação %1) - - - - License - Licença - - Account - Account Window header Conta - Your account login - Tooltip Suas informações de login - john_smith1987 - Login placeholder josé_silva1987 - Server Servidor - A server address of your account. Like 404.city or macaw.me - Tooltip O endereço do servidor da sua conta, como o 404.city ou o macaw.me - macaw.me - Placeholder macaw.me - Login Usuário - Password Senha - Password of your account - Tooltip Senha da sua conta - Name Nome - Just a name how would you call this account, doesn't affect anything - Apenas um nome para identificar esta conta. Não influencia em nada (não pode ser mudado) + Apenas um nome para identificar esta conta. Não influencia em nada - John - Placeholder José - Resource Recurso - A resource name like "Home" or "Work" - Tooltip Um nome de recurso como "Casa" ou "Trabalho" - QXmpp Default resource QXmpp - Password storage Armazenamento de senha - - - Active - Ativo - - - - enable - habilitar - Accounts - Accounts Contas - Delete Apagar - Add Adicionar - Edit Editar - Change password Alterar senha - Connect Conectar Disconnect - Desconectar - - - - Deactivate - Desativar - - - - - Activate - Ativar - - - - Application - - - Quit - Sair - - - - - Minimize to tray - Minimizar para bandeja - - - - Show Squawk - Aparecer Squawk - - - - from - de - - - - Attached file - Arquivo anexado - - - - Mark as Read - Marcar como lido - - - - Open conversation - Abrir conversa + Desconectar Conversation - Type your message here... - Placeholder Digite sua mensagem aqui... - Chose a file to send Escolha um arquivo para enviar - - Drop files here to attach them to your message - Arraste seus arquivos aqui para anexá-los a sua mensagem - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + - - Paste Image - Colar imagem - - - - Try sending again - Tente enviar de novo - - - - Copy selected - Copiar selecionado - - - - Copy message - Copiar mensagem - - - - Open - Abrir - - - - Show in folder - Show in explorer - - - - Edit - Editar - - - - Editing message... - Messae está sendo editado... - - - - CredentialsPrompt - - - Authentication error: %1 - Window title - Erro de autenticação: %1 - - - - Couldn't authenticate account %1: login or password is icorrect. -Would you like to check them and try again? - Não foi possível autenticar a conta %1: login ou senha incorretos. -Deseja verificá-los e tentar novamente? - - - - Login - Usuário - - - - Your account login (without @server.domain) - Suas informações de login (sem @server.domain) - - - - Password - Senha - - - - Your password - Senha da sua conta - - - - DialogQueue - - - Input the password for account %1 - Digite a senha para a conta %1 - - - - Password for account %1 - Senha para a conta %1 + Drop files here to attach them to your message + Arraste seus arquivos aqui para anexá-los a sua mensagem Global - Online Availability Conectado - Away Availability Distante - Absent Availability Ausente - Busy Availability Ocupado - Chatty Availability Tagarela - Invisible Availability Invisível - Offline Availability Desconectado - Disconnected ConnectionState Desconectado - Connecting ConnectionState Connectando - Connected ConnectionState Conectado - Error ConnectionState Erro - None SubscriptionState Nenhum - From SubscriptionState De - To SubscriptionState Para - Both SubscriptionState Ambos - Unknown SubscriptionState Desconhecido - Unspecified Affiliation Não especificado - Outcast Affiliation Rejeitado - Nobody Affiliation Ninguém - Member Affiliation Membro - Admin Affiliation Administrador - Owner Affiliation Dono - Unspecified Role Não especificado - Nobody Role Ninguém - Visitor Role Visitante - Participant Role Participante - Moderator Role Moderador - Pending MessageState Aguardando - Sent MessageState Enviada - Delivered MessageState Entregue - Error MessageState Erro - Plain AccountPassword Texto simples - Jammed AccountPassword Embaralhado - Always Ask AccountPassword Sempre perguntar - KWallet AccountPassword KWallet - Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not AccountPasswordDescription Sua senha será armazenada em um arquivo de configurações, porém embaralhada com uma chave criptográfica constante que você pode encontrar no código fonte do programa. Parece criptografado, mas não é - Squawk is going to query you for the password on every start of the program AccountPasswordDescription O Squark vai requisitar sua senha a cada vez que você abrir o programa - Your password is going to be stored in config file in plain text AccountPasswordDescription Sua senha será armazenada em um arquivo de configurações em texto simples - Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions AccountPasswordDescription Sua senha será armazenada no KDE wallet (KWallet). Sua autorização será requerida @@ -682,53 +325,43 @@ Deseja verificá-los e tentar novamente? JoinConference - Join new conference Заголовок окна Entrar em uma nova conferência - JID JID - Room JID Sala JID - identifier@conference.server.org identifier@conference.server.org - Account Conta - Join on login Entrar ao se conectar - If checked Squawk will try to join this conference on login Se marcado, o Squawk tentará entrar nesta conferência automaticamente durante o login - Nick name Apelido - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Seu apelido para essa conferência. Se você deixar este campo em branco, seu nome de usuário será usado como apelido - John José @@ -737,67 +370,61 @@ Deseja verificá-los e tentar novamente? Message Open - Abrir + Abrir MessageLine Downloading... - Baixando... + Baixando... - - Download Baixar Error uploading file: %1 You can try again - Error fazendo upload do arquivo: + Error fazendo upload do arquivo: %1 Você pode tentar novamente Upload - Upload + Upload Error downloading file: %1 You can try again - Erro baixando arquivo: + Erro baixando arquivo: %1 Você pode tentar novamente Uploading... - Fazendo upload... + Fazendo upload... Push the button to download the file - Pressione o botão para baixar o arquivo + Pressione o botão para baixar o arquivo Models::Room - Subscribed Inscrito - Temporarily unsubscribed Inscrição temporariamente cancelada - Temporarily subscribed Temporariamente inscrito - Unsubscribed Não inscrito @@ -805,67 +432,47 @@ Você pode tentar novamente Models::Roster - New messages Novas mensagens - - - New messages: Novas mensagens: - - Jabber ID: ID Jabber: - - - Availability: Disponibilidade: - - - Status: Status: - - - Subscription: Inscrição: - Affiliation: Я правда не знаю, как это объяснить, не то что перевести Afiliação: - Role: Papel: - Online contacts: Contatos online: - Total contacts: Contatos totais: - Members: Membros: @@ -873,241 +480,107 @@ Você pode tentar novamente NewContact - Add new contact - Window title + Заголовок окна Adicionar novo contato - Account Conta - An account that is going to have new contact A conta que terá um novo contato - JID JID - Jabber id of your new contact ID Jabber do seu novo contato - name@server.dmn - Placeholder + Placeholder поля ввода JID nome@servidor.com.br - Name Nome - The way this new contact will be labeled in your roster (optional) A forma com que o novo contato será classificado em sua lista (opcional) - John Smith José Silva - - PageAppearance - - - Theme - Estilo - - - - Color scheme - Esquema de cores - - - - - - - System - Do sistema - - - - PageGeneral - - - Downloads path - Pasta de downloads - - - - Tray icon - Ícone da bandeja - - - - Mimimize Squawk to tray when closing main window - Minimizar Squawk para bandeja ao fechar a janela principal - - - - Hide tray icon - Esconder o ícone da bandeja - - - - Hide tray icon when Squawk main window is visible - Esconder o ícone da bandeja quando a janela principal não está fechada - - - Close to tray icon - Fechar ao ícone da bandeja - - - - Browse - 6 / 5,000 -Translation results -Navegar - - - - Tray is not available for your system - A bandeja não está disponível para seu sistema - - - - Select where downloads folder is going to be - Selecione onde a pasta de downloads ficará - - - - Settings - - - Preferences - Window title - Preferências - - - - - General - Geral - - - - Appearance - Aparência - - - - Apply - Aplicar - - - - Cancel - Cancelar - - - - Ok - Feito - - Squawk - squawk Squawk - Settings Configurações - Squawk - Menu bar entry Squawk - Accounts Contas - Quit Sair - Add contact Adicionar contato - Add conference Adicionar conferência Disconnect - Desconectar + Desconectar Connect - Conectar + Conectar - - VCard VCard - - - Remove Remover - Open dialog Abrir caixa de diálogo - - Unsubscribe Cancelar inscrição - - Subscribe Inscrever-se - Rename Renomear - Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -1117,285 +590,209 @@ ser exibido com %1 - Renaming %1 Renomeando %1 - Groups Grupos - New group Novo grupo - New group name Novo nome do grupo - Add %1 to a new group Adicionar %1 a um novo grupo - Open conversation Abrir conversa - %1 account card cartão da conta %1 - %1 contact card cartão de contato %1 - Downloading vCard Baixando vCard Attached file - Arquivo anexado + Arquivo anexado Input the password for account %1 - Digite a senha para a conta %1 + Digite a senha para a conta %1 Password for account %1 - Senha para a conta %1 + Senha para a conta %1 - Please select a contact to start chatting Por favor selecione um contato para começar a conversar - - - Help - Ajuda - - - - Preferences - Preferências - - - - About Squawk - Sorbe Squawk - - - - Deactivate - Desativar - - - - Activate - Ativar - VCard - Received 12.07.2007 at 17.35 - Nunca atualizado + Recebido 12/07/2007 às 17:35 - - General Geral - Organization Empresa - Middle name Nome do meio - First name Primeiro nome - Last name Sobrenome - Nick name Apelido - Birthday Data de aniversário - Organization name Nome da empresa - Unit / Department - Unidade / Departamento + Unidade/Departamento - Role / Profession Profissão - Job title Cargo - Full name Nome completo - Personal information Informações pessoais - Addresses Endereços - E-Mail addresses Endereços de e-mail - Phone numbers Números de telefone - - Contact Contato - Jabber ID ID Jabber - Web site Site web - - Description Descrição - Set avatar Definir avatar - Clear avatar Apagar avatar - Account %1 card Cartão da conta %1 - Contact %1 card Cartão do contato %1 - Received %1 at %2 Recebido %1 em %2 - Chose your new avatar Escolha um novo avatar - Images (*.png *.jpg *.jpeg) Imagens (*.png *.jpg *.jpeg) - Add email address Adicionar endereço de e-mail - Unset this email as preferred Desmarcar este e-mail como preferido - Set this email as preferred Marcar este e-mail como preferido - Remove selected email addresses Remover endereço de e-mail selecionado - Copy selected emails to clipboard Copiar endereços de e-mails selecionados para a área de transferência - Add phone number Adicionar número de telefone - Unset this phone as preferred Desmarcar este número de telefone como preferido - Set this phone as preferred Marcar este número de telefone como preferido - Remove selected phone numbers Remover os números de telefones selecionados - Copy selected phones to clipboard Copiar os números de telefone selecionados para a área de transferência diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index 421a2d2..e3b4d52 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -1,683 +1,322 @@ - - About - - - About Squawk - О Программе Squawk - - - - Squawk - Squawk - - - - About - Tab title - Общее - - - - XMPP (jabber) messenger - XMPP (jabber) мессенджер - - - - (c) 2019 - 2022, Yury Gubich - (c) 2019 - 2022, Юрий Губич - - - - <a href="https://git.macaw.me/blue/squawk">Project site</a> - <a href="https://git.macaw.me/blue/squawk">Сайт проекта</a> - - - - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Лицензия: GNU General Public License версия 3</a> - - - - Components - Компоненты - - - - - - Version - Версия - - - - - - 0.0.0 - 0.0.0 - - - - Report Bugs - Сообщать об ошибках - - - - Please report any bug you find! -To report bugs you can use: - Пожалуйста, сообщайте о любых ошибках! -Способы сообщить об ошибках: - - - - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - <a href="https://git.macaw.me/blue/squawk/issues">Баг-трекер проекта</> - - - - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - - - - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - - - - Thanks To - Благодарности - - - - Vae - Vae - - - - Major refactoring, bug fixes, constructive criticism - Крупный рефакторинг, исправление ошибок, конструктивная критика - - - - Shunf4 - Shunf4 - - - - Major refactoring, bug fixes, build adaptations for Windows and MacOS - Крупный рефакторинг, исправление ошибок, адаптация сборки под Windows and MacOS - - - - Bruno F. Fontes - Bruno F. Fontes - - - - Brazilian Portuguese translation - Перевод на Португальский (Бразилия) - - - - - (built against %1) - (версия при сборке %1) - - - - License - Лицензия - - Account - Account Заголовок окна Учетная запись - Your account login - Tooltip Имя пользователя Вашей учетной записи - john_smith1987 - Login placeholder ivan_ivanov1987 - Server Сервер - A server address of your account. Like 404.city or macaw.me - Tooltip Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me) - macaw.me - Placeholder macaw.me - Login Имя учетной записи - Password Пароль - Password of your account - Tooltip Пароль вашей учетной записи - Name Имя - Just a name how would you call this account, doesn't affect anything - Просто имя, то как Вы называете свою учетную запись, может быть любым (нельзя поменять) + Просто имя, то как Вы называете свою учетную запись, может быть любым - John - Placeholder Иван - Resource Ресурс - A resource name like "Home" or "Work" - Tooltip Имя этой программы для ваших контактов, может быть "Home" или "Phone" - QXmpp Ресурс по умолчанию QXmpp - Password storage Хранение пароля - - - Active - Активен - - - - enable - включен - Accounts - Accounts Учетные записи - Delete Удалить - Add Добавить - Edit Редактировать - Change password Изменить пароль - Connect Подключить Disconnect - Отключить - - - - Deactivate - Деактивировать - - - - - Activate - Активировать - - - - Application - - - Quit - Выйти - - - - - Minimize to tray - Свернуть в трей - - - - Show Squawk - Показать Squawk - - - - from - от - - - - Attached file - Прикрепленный файл - - - - Mark as Read - Пометить прочитанным - - - - Open conversation - Открыть окно беседы + Отключить Conversation - Type your message here... - Placeholder Введите сообщение... - Chose a file to send Выберите файл для отправки - - Drop files here to attach them to your message - Бросьте файлы сюда для того что бы прикрепить их к сообщению - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + - - Paste Image - Вставить изображение - - - - Try sending again - Отправить снова - - - - Copy selected - Скопировать выделенное - - - - Copy message - Скопировать сообщение - - - - Open - Открыть - - - - Show in folder - Показать в проводнике - - - - Edit - Редактировать - - - - Editing message... - Сообщение редактируется... - - - - CredentialsPrompt - - - Authentication error: %1 - Window title - Ошибка аутентификации: %1 - - - - Couldn't authenticate account %1: login or password is icorrect. -Would you like to check them and try again? - Не получилось аутентифицировать -учетную запись %1: -имя пользователя или пароль введены неверно. -Желаете ли проверить их и -попробовать аутентифицироваться еще раз? - - - - Login - Имя учетной записи - - - - Your account login (without @server.domain) - Tooltip - Имя вашей учтетной записи (без @server.domain) - - - - Password - Пароль - - - - Your password - Ваш пароль - - - - DialogQueue - - - Input the password for account %1 - Введите пароль для учетной записи %1 - - - - Password for account %1 - Пароль для учетной записи %1 + Drop files here to attach them to your message + Бросьте файлы сюда для того что бы прикрепить их к сообщению Global - Online Availability В сети - Away Availability Отошел - Absent Availability Недоступен - Busy Availability Занят - Chatty Availability Готов поболтать - Invisible Availability Невидимый - Offline Availability Отключен - Disconnected ConnectionState Отключен - Connecting ConnectionState Подключается - Connected ConnectionState Подключен - Error ConnectionState Ошибка - None SubscriptionState Нет - From SubscriptionState Входящая - To SubscriptionState Исходящая - Both SubscriptionState Взаимная - Unknown SubscriptionState Неизвестно - Unspecified Affiliation Не назначено - Outcast Affiliation Изгой - Nobody Affiliation Никто - Member Affiliation Участник - Admin Affiliation Администратор - Owner Affiliation Владелец - Unspecified Role Не назначено - Nobody Role Никто - Visitor Role Гость - Participant Role Участник - Moderator Role Модератор - Pending MessageState В процессе отправки - Sent MessageState Отправлено - Delivered MessageState Доставлено - Error MessageState Ошибка - Plain AccountPassword Открытый текст - Jammed AccountPassword Обфусцированный - Always Ask AccountPassword Всегда спрашивать - KWallet AccountPassword KWallet - Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not AccountPasswordDescription Ваш пароль будет храниться в обфусцированном виде в конфигурационном файле. Обфускация производится с помощью постоянного числа, которое можно найти в исходном коде программы. Это может и выглядит как шифрование но им не является - Squawk is going to query you for the password on every start of the program AccountPasswordDescription Squawk будет спрашивать пароль от этой учетной записи каждый раз при запуске - Your password is going to be stored in config file in plain text AccountPasswordDescription Ваш пароль будет храниться в конфигурационном файле открытым текстром - Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions AccountPasswordDescription Ваш пароль будет храниться в бумажнике KDE (KWallet). В первый раз программа попросит разрешения для доступа к бумажнику @@ -686,53 +325,43 @@ Would you like to check them and try again? JoinConference - Join new conference Заголовок окна Присоединиться к новой беседе - JID JID - Room JID Jabber-идентификатор беседы - identifier@conference.server.org identifier@conference.server.org - Account Учетная запись - Join on login Автовход - If checked Squawk will try to join this conference on login Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении - Nick name Псевдоним - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи - John Ivan @@ -741,67 +370,61 @@ Would you like to check them and try again? Message Open - Открыть + Открыть MessageLine Downloading... - Скачивается... + Скачивается... - - Download Скачать Error uploading file: %1 You can try again - Ошибка загрузки файла на сервер: + Ошибка загрузки файла на сервер: %1 Для того, что бы попробовать снова нажмите на кнопку Upload - Загрузить + Загрузить Error downloading file: %1 You can try again - Ошибка скачивания файла: + Ошибка скачивания файла: %1 Вы можете попробовать снова Uploading... - Загружается... + Загружается... Push the button to download the file - Нажмите на кнопку что бы загрузить файл + Нажмите на кнопку что бы загрузить файл Models::Room - Subscribed Вы состоите в беседе - Temporarily unsubscribed Вы временно не состоите в беседе - Temporarily subscribed Вы временно состоите в беседе - Unsubscribed Вы не состоите в беседе @@ -809,67 +432,47 @@ You can try again Models::Roster - New messages Есть непрочитанные сообщения - - - New messages: Новых сообщений: - - Jabber ID: Идентификатор: - - - Availability: Доступность: - - - Status: Статус: - - - Subscription: Подписка: - Affiliation: Я правда не знаю, как это объяснить, не то что перевести Причастность: - Role: Роль: - Online contacts: Контакстов в сети: - Total contacts: Всего контактов: - Members: Участников: @@ -877,239 +480,107 @@ You can try again NewContact - Add new contact - Window title + Заголовок окна Добавление нового контакта - Account Учетная запись - An account that is going to have new contact Учетная запись для которой будет добавлен контакт - JID JID - Jabber id of your new contact Jabber-идентификатор нового контакта - name@server.dmn - Placeholder + Placeholder поля ввода JID name@server.dmn - Name Имя - The way this new contact will be labeled in your roster (optional) То, как будет подписан контакт в вашем списке контактов (не обязательно) - John Smith Иван Иванов - - PageAppearance - - - Theme - Оформление - - - - Color scheme - Цветовая схема - - - - - - - System - Системная - - - - PageGeneral - - - Downloads path - Папка для сохраненных файлов - - - - Tray icon - Иконка трея - - - - Mimimize Squawk to tray when closing main window - Сворачивать Squawk в трей когда закрывается основное окно - - - - Hide tray icon - Скрывать иконку трея - - - - Hide tray icon when Squawk main window is visible - Прятать иконку трея если основное окно не закрыто - - - Close to tray icon - Закрывать в трей - - - - Browse - Выбрать - - - - Tray is not available for your system - На вашей системе недоступен трей - - - - Select where downloads folder is going to be - Выберете папку, в которую будут сохраняться файлы - - - - Settings - - - Preferences - Window title - Настройки - - - - - General - Общее - - - - Appearance - Внешний вид - - - - Apply - Применить - - - - Cancel - Отменить - - - - Ok - Готово - - Squawk - squawk Squawk - Settings Настройки - Squawk - Menu bar entry Squawk - Accounts Учетные записи - Quit Выйти - Add contact Добавить контакт - Add conference Присоединиться к беседе Disconnect - Отключить + Отключить Connect - Подключить + Подключить - - VCard Карточка - - - Remove Удалить - Open dialog Открыть диалог - - Unsubscribe Отписаться - - Subscribe Подписаться - Rename Переименовать - Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -1119,285 +590,209 @@ to be displayed as %1 %1 - Renaming %1 Назначение имени контакту %1 - Groups Группы - New group Создать новую группу - New group name Имя группы - Add %1 to a new group Добавление %1 в новую группу - Open conversation Открыть окно беседы - %1 account card Карточка учетной записи %1 - %1 contact card Карточка контакта %1 - Downloading vCard Получение карточки Attached file - Прикрепленный файл + Прикрепленный файл Input the password for account %1 - Введите пароль для учетной записи %1 + Введите пароль для учетной записи %1 Password for account %1 - Пароль для учетной записи %1 + Пароль для учетной записи %1 - Please select a contact to start chatting Выберите контакт или группу что бы начать переписку - - - Help - Помощь - - - - Preferences - Настройки - - - - About Squawk - О Программе Squawk - - - - Deactivate - Деактивировать - - - - Activate - Активировать - VCard - Received 12.07.2007 at 17.35 Не обновлялось - - General Общее - Organization Место работы - Middle name Среднее имя - First name Имя - Last name Фамилия - Nick name Псевдоним - Birthday Дата рождения - Organization name Название организации - Unit / Department Отдел - Role / Profession Профессия - Job title Наименование должности - Full name Полное имя - Personal information Личная информация - Addresses Адреса - E-Mail addresses Адреса электронной почты - Phone numbers Номера телефонов - - Contact Контактная информация - Jabber ID Jabber ID - Web site Веб сайт - - Description Описание - Set avatar Установить иконку - Clear avatar Убрать иконку - Account %1 card Карточка учетной записи %1 - Contact %1 card Карточка контакта %1 - Received %1 at %2 Получено %1 в %2 - Chose your new avatar Выберите новую иконку - Images (*.png *.jpg *.jpeg) Изображения (*.png *.jpg *.jpeg) - Add email address Добавить адрес электронной почты - Unset this email as preferred Убрать отметку "предпочтительный" с этого адреса - Set this email as preferred Отметить этот адрес как "предпочтительный" - Remove selected email addresses Удалить выбранные адреса - Copy selected emails to clipboard Скопировать выбранные адреса в буфер обмена - Add phone number Добавить номер телефона - Unset this phone as preferred Убрать отметку "предпочтительный" с этого номера - Set this phone as preferred Отметить этот номер как "предпочтительный" - Remove selected phone numbers Удалить выбранные телефонные номера - Copy selected phones to clipboard Скопировать выбранные телефонные номера в буфер обмена diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 296c289..36207b6 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,8 +1,4 @@ -target_sources(squawk PRIVATE - squawk.cpp - squawk.h - squawk.ui -) +target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui) add_subdirectory(models) add_subdirectory(utils) diff --git a/ui/models/CMakeLists.txt b/ui/models/CMakeLists.txt index 0e6d8ab..629db32 100644 --- a/ui/models/CMakeLists.txt +++ b/ui/models/CMakeLists.txt @@ -1,36 +1,26 @@ -set(SOURCE_FILES - abstractparticipant.cpp - account.cpp - accounts.cpp - contact.cpp - element.cpp - group.cpp - item.cpp - participant.cpp - presence.cpp - reference.cpp - room.cpp - roster.cpp -) - -set(HEADER_FILES - abstractparticipant.h - account.h - accounts.h - contact.h - element.h - group.h - item.h - participant.h - presence.h - reference.h - room.h - roster.h -) - target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) - -add_subdirectory(info) + abstractparticipant.cpp + abstractparticipant.h + account.cpp + account.h + accounts.cpp + accounts.h + contact.cpp + contact.h + element.cpp + element.h + group.cpp + group.h + item.cpp + item.h + participant.cpp + participant.h + presence.cpp + presence.h + reference.cpp + reference.h + room.cpp + room.h + roster.cpp + roster.h + ) diff --git a/ui/models/abstractparticipant.cpp b/ui/models/abstractparticipant.cpp index 240e5ba..029527d 100644 --- a/ui/models/abstractparticipant.cpp +++ b/ui/models/abstractparticipant.cpp @@ -18,38 +18,40 @@ #include "abstractparticipant.h" +using namespace Models; + Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, const QMap& data, Models::Item* parentItem): Item(p_type, data, parentItem), availability(Shared::Availability::offline), lastActivity(data.value("lastActivity").toDateTime()), - status(data.value("status").toString()), - client() + status(data.value("status").toString()) { QMap::const_iterator itr = data.find("availability"); - if (itr != data.end()) + if (itr != data.end()) { setAvailability(itr.value().toUInt()); - - itr = data.find("client"); - if (itr != data.end()) - setClient(itr.value().value()); + } } Models::AbstractParticipant::AbstractParticipant(const Models::AbstractParticipant& other): Item(other), availability(other.availability), lastActivity(other.lastActivity), - status(other.status), - client(other.client) -{} - - -Models::AbstractParticipant::~AbstractParticipant() {} - -int Models::AbstractParticipant::columnCount() const { - return 5; + status(other.status) +{ } -QVariant Models::AbstractParticipant::data(int column) const { + +Models::AbstractParticipant::~AbstractParticipant() +{ +} + +int Models::AbstractParticipant::columnCount() const +{ + return 4; +} + +QVariant Models::AbstractParticipant::data(int column) const +{ switch (column) { case 0: return Item::data(column); @@ -59,71 +61,62 @@ QVariant Models::AbstractParticipant::data(int column) const { return QVariant::fromValue(availability); case 3: return status; - case 4: - return QVariant::fromValue(client); default: return QVariant(); } } -Shared::Availability Models::AbstractParticipant::getAvailability() const { +Shared::Availability Models::AbstractParticipant::getAvailability() const +{ return availability; } -QDateTime Models::AbstractParticipant::getLastActivity() const { +QDateTime Models::AbstractParticipant::getLastActivity() const +{ return lastActivity; } -QString Models::AbstractParticipant::getStatus() const { +QString Models::AbstractParticipant::getStatus() const +{ return status; } -void Models::AbstractParticipant::setAvailability(Shared::Availability p_avail) { +void Models::AbstractParticipant::setAvailability(Shared::Availability p_avail) +{ if (availability != p_avail) { availability = p_avail; changed(2); } } -void Models::AbstractParticipant::setAvailability(unsigned int avail) { +void Models::AbstractParticipant::setAvailability(unsigned int avail) +{ setAvailability(Shared::Global::fromInt(avail)); } -void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time) { +void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time) +{ if (lastActivity != p_time) { lastActivity = p_time; changed(1); } } -void Models::AbstractParticipant::setStatus(const QString& p_state) { +void Models::AbstractParticipant::setStatus(const QString& p_state) +{ if (status != p_state) { status = p_state; changed(3); } } -Shared::ClientId Models::AbstractParticipant::getClient() const { - return client; -} - -QString Models::AbstractParticipant::getClientNode() const { - return client.node; -} - -void Models::AbstractParticipant::setClient(const Shared::ClientId& id) { - if (client != id) { - client = id; - changed(4); - } -} - - -QIcon Models::AbstractParticipant::getStatusIcon(bool big) const { +QIcon Models::AbstractParticipant::getStatusIcon(bool big) const +{ return Shared::availabilityIcon(availability, big); } -void Models::AbstractParticipant::update(const QString& key, const QVariant& value) { +void Models::AbstractParticipant::update(const QString& key, const QVariant& value) +{ if (key == "name") { setName(value.toString()); } else if (key == "status") { @@ -132,7 +125,5 @@ void Models::AbstractParticipant::update(const QString& key, const QVariant& val setAvailability(value.toUInt()); } else if (key == "lastActivity") { setLastActivity(value.toDateTime()); - } else if (key == "client") { - setClient(value.value()); } } diff --git a/ui/models/abstractparticipant.h b/ui/models/abstractparticipant.h index fe2f0b5..cb20788 100644 --- a/ui/models/abstractparticipant.h +++ b/ui/models/abstractparticipant.h @@ -21,10 +21,9 @@ #include "item.h" -#include -#include -#include -#include +#include "shared/enums.h" +#include "shared/icons.h" +#include "shared/global.h" #include #include @@ -52,10 +51,6 @@ public: QString getStatus() const; void setStatus(const QString& p_state); virtual QIcon getStatusIcon(bool big = false) const; - - Shared::ClientId getClient() const; - void setClient(const Shared::ClientId& id); - QString getClientNode() const; virtual void update(const QString& key, const QVariant& value); @@ -63,7 +58,6 @@ protected: Shared::Availability availability; QDateTime lastActivity; QString status; - Shared::ClientId client; }; } diff --git a/ui/models/account.cpp b/ui/models/account.cpp index cf1efb4..43cb3ed 100644 --- a/ui/models/account.cpp +++ b/ui/models/account.cpp @@ -32,8 +32,7 @@ Models::Account::Account(const QMap& data, Models::Item* pare state(Shared::ConnectionState::disconnected), availability(Shared::Availability::offline), passwordType(Shared::AccountPassword::plain), - wasEverConnected(false), - active(false) + wasEverConnected(false) { QMap::const_iterator sItr = data.find("state"); if (sItr != data.end()) { @@ -47,10 +46,6 @@ Models::Account::Account(const QMap& data, Models::Item* pare if (pItr != data.end()) { setPasswordType(pItr.value().toUInt()); } - QMap::const_iterator acItr = data.find("active"); - if (acItr != data.end()) { - setActive(acItr.value().toBool()); - } } Models::Account::~Account() @@ -181,8 +176,6 @@ QVariant Models::Account::data(int column) const return avatarPath; case 9: return Shared::Global::getName(passwordType); - case 10: - return active; default: return QVariant(); } @@ -190,7 +183,7 @@ QVariant Models::Account::data(int column) const int Models::Account::columnCount() const { - return 11; + return 10; } void Models::Account::update(const QString& field, const QVariant& value) @@ -215,8 +208,6 @@ void Models::Account::update(const QString& field, const QVariant& value) setAvatarPath(value.toString()); } else if (field == "passwordType") { setPasswordType(value.toUInt()); - } else if (field == "active") { - setActive(value.toBool()); } } @@ -290,16 +281,3 @@ void Models::Account::setPasswordType(unsigned int pt) { setPasswordType(Shared::Global::fromInt(pt)); } - -bool Models::Account::getActive() const -{ - return active; -} - -void Models::Account::setActive(bool p_active) -{ - if (active != p_active) { - active = p_active; - changed(10); - } -} diff --git a/ui/models/account.h b/ui/models/account.h index ab2b629..3d2310f 100644 --- a/ui/models/account.h +++ b/ui/models/account.h @@ -58,9 +58,6 @@ namespace Models { void setAvatarPath(const QString& path); QString getAvatarPath() const; - - void setActive(bool active); - bool getActive() const; void setAvailability(Shared::Availability p_avail); void setAvailability(unsigned int p_avail); @@ -94,7 +91,6 @@ namespace Models { Shared::Availability availability; Shared::AccountPassword passwordType; bool wasEverConnected; - bool active; protected slots: void toOfflineState() override; diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index e401b3a..f5ffce8 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -18,7 +18,6 @@ #include "accounts.h" #include "shared/icons.h" -#include "shared/defines.h" #include #include @@ -27,23 +26,27 @@ std::deque Models::Accounts::columns = {"Name", "Server", "State", "Err Models::Accounts::Accounts(QObject* parent): QAbstractTableModel(parent), - accs() {} + accs() +{ -Models::Accounts::~Accounts() {} +} -QVariant Models::Accounts::data (const QModelIndex& index, int role) const { +Models::Accounts::~Accounts() +{ + +} + +QVariant Models::Accounts::data (const QModelIndex& index, int role) const +{ QVariant answer; switch (role) { case Qt::DisplayRole: answer = accs[index.row()]->data(index.column()); break; case Qt::DecorationRole: - if (index.column() == 2) + if (index.column() == 2) { answer = Shared::connectionStateIcon(accs[index.row()]->getState()); - break; - case Qt::ForegroundRole: - if (!accs[index.row()]->getActive()) - answer = qApp->palette().brush(QPalette::Disabled, QPalette::Text); + } break; default: break; @@ -52,46 +55,36 @@ QVariant Models::Accounts::data (const QModelIndex& index, int role) const { return answer; } -int Models::Accounts::columnCount (const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int Models::Accounts::columnCount ( const QModelIndex& parent ) const +{ return columns.size(); } -int Models::Accounts::rowCount (const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int Models::Accounts::rowCount ( const QModelIndex& parent ) const +{ return accs.size(); } -unsigned int Models::Accounts::size() const { - return rowCount(QModelIndex()); -} - -unsigned int Models::Accounts::activeSize() const { - unsigned int size = 0; - for (const Models::Account* acc : accs) { - if (acc->getActive()) - ++size; - } - - return size; -} - -QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) +QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { return tr(columns[section].toLatin1()); - + } return QVariant(); } -void Models::Accounts::addAccount(Account* account) { +void Models::Accounts::addAccount(Account* account) +{ beginInsertRows(QModelIndex(), accs.size(), accs.size()); + int index = 0; std::deque::const_iterator before = accs.begin(); while (before != accs.end()) { Account* bfr = *before; - if (bfr->getDisplayedName() > account->getDisplayedName()) + if (bfr->getDisplayedName() > account->getDisplayedName()) { break; - + } + index++; before++; } @@ -100,26 +93,24 @@ void Models::Accounts::addAccount(Account* account) { endInsertRows(); emit sizeChanged(accs.size()); - emit changed(); } -void Models::Accounts::onAccountChanged(Item* item, int row, int col) { - if (row < 0) - return; - - if (static_cast::size_type>(row) < accs.size()) { +void Models::Accounts::onAccountChanged(Item* item, int row, int col) +{ + if (row < accs.size()) { Account* acc = getAccount(row); - if (item != acc) + if (item != acc) { return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that + } if (col == 0) { int newRow = 0; std::deque::const_iterator before = accs.begin(); while (before != accs.end()) { Item* bfr = *before; - if (bfr->getDisplayedName() > item->getDisplayedName()) + if (bfr->getDisplayedName() > item->getDisplayedName()) { break; - + } newRow++; before++; } @@ -136,18 +127,20 @@ void Models::Accounts::onAccountChanged(Item* item, int row, int col) { } } - if (col < columnCount(QModelIndex())) + if (col < columnCount(QModelIndex())) { emit dataChanged(createIndex(row, col), createIndex(row, col)); - + } emit changed(); } } -Models::Account * Models::Accounts::getAccount(int index) { +Models::Account * Models::Accounts::getAccount(int index) +{ return accs[index]; } -void Models::Accounts::removeAccount(int index) { +void Models::Accounts::removeAccount(int index) +{ Account* account = accs[index]; beginRemoveRows(QModelIndex(), index, index); disconnect(account, &Account::childChanged, this, &Accounts::onAccountChanged); @@ -157,13 +150,12 @@ void Models::Accounts::removeAccount(int index) { emit sizeChanged(accs.size()); } -std::deque Models::Accounts::getActiveNames() const { +std::deque Models::Accounts::getNames() const +{ std::deque res; - for (const Models::Account* acc : accs) { - if (acc->getActive()) - res.push_back(acc->getName()); - + for (std::deque::const_iterator i = accs.begin(), end = accs.end(); i != end; ++i) { + res.push_back((*i)->getName()); } return res; diff --git a/ui/models/accounts.h b/ui/models/accounts.h index c5d59af..e8be07c 100644 --- a/ui/models/accounts.h +++ b/ui/models/accounts.h @@ -39,13 +39,11 @@ public: QVariant data ( const QModelIndex& index, int role ) const override; int columnCount ( const QModelIndex& parent ) const override; int rowCount ( const QModelIndex& parent ) const override; - unsigned int size () const; - unsigned int activeSize () const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Account* getAccount(int index); - std::deque getActiveNames() const; + std::deque getNames() const; signals: void changed(); diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index c89724e..a0c70ac 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -24,61 +24,62 @@ Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap::const_iterator itr = data.find("state"); - if (itr != data.end()) + if (itr != data.end()) { setState(itr.value().toUInt()); - - itr = data.find("trust"); - if (itr != data.end()) - setTrust(itr.value().value()); - - itr = data.find("encryption"); - if (itr != data.end()) - setEncryption(itr.value().value()); + } } -Models::Contact::~Contact() {} +Models::Contact::~Contact() +{ +} -void Models::Contact::setAvailability(unsigned int p_state) { +void Models::Contact::setAvailability(unsigned int p_state) +{ setAvailability(Shared::Global::fromInt(p_state)); } -void Models::Contact::setState(unsigned int p_state) { +void Models::Contact::setState(unsigned int p_state) +{ setState(Shared::Global::fromInt(p_state)); } -Shared::Availability Models::Contact::getAvailability() const { +Shared::Availability Models::Contact::getAvailability() const +{ return availability; } -void Models::Contact::setAvailability(Shared::Availability p_state) { +void Models::Contact::setAvailability(Shared::Availability p_state) +{ if (availability != p_state) { availability = p_state; changed(3); } } -QString Models::Contact::getStatus() const { +QString Models::Contact::getStatus() const +{ return status; } -void Models::Contact::setStatus(const QString& p_state) { +void Models::Contact::setStatus(const QString& p_state) +{ if (status != p_state) { status = p_state; changed(5); } } -int Models::Contact::columnCount() const { - return 10; +int Models::Contact::columnCount() const +{ + return 8; } -QVariant Models::Contact::data(int column) const { +QVariant Models::Contact::data(int column) const +{ switch (column) { case 0: return getContactName(); @@ -96,39 +97,35 @@ QVariant Models::Contact::data(int column) const { return QVariant::fromValue(getAvatarState()); case 7: return getAvatarPath(); - case 8: - return QVariant::fromValue(getTrust()); - case 9: - return QVariant::fromValue(getEncryption()); default: return QVariant(); } } -QString Models::Contact::getContactName() const { - if (name == "") +QString Models::Contact::getContactName() const +{ + if (name == "") { return jid; - else + } else { return name; + } } -void Models::Contact::update(const QString& field, const QVariant& value) { +void Models::Contact::update(const QString& field, const QVariant& value) +{ if (field == "name") { setName(value.toString()); } else if (field == "availability") { setAvailability(value.toUInt()); } else if (field == "state") { setState(value.toUInt()); - } else if (field == "trust") { - setTrust(value.value()); - } else if (field == "encryption") { - setEncryption(value.value()); } else { Element::update(field, value); } } -void Models::Contact::addPresence(const QString& p_name, const QMap& data) { +void Models::Contact::addPresence(const QString& p_name, const QMap& data) +{ QMap::iterator itr = presences.find(p_name); if (itr == presences.end()) { @@ -138,12 +135,14 @@ void Models::Contact::addPresence(const QString& p_name, const QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) + for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { pr->update(itr.key(), itr.value()); + } } } -void Models::Contact::removePresence(const QString& name) { +void Models::Contact::removePresence(const QString& name) +{ QMap::iterator itr = presences.find(name); if (itr == presences.end()) { @@ -156,15 +155,8 @@ void Models::Contact::removePresence(const QString& name) { } } -Models::Presence * Models::Contact::getPresence(const QString& name) { - QMap::iterator itr = presences.find(name); - if (itr == presences.end()) - return nullptr; - else - return itr.value(); -} - -void Models::Contact::refresh() { +void Models::Contact::refresh() +{ QDateTime lastActivity; Presence* presence = 0; for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { @@ -186,43 +178,36 @@ void Models::Contact::refresh() { } } -void Models::Contact::_removeChild(int index) { +void Models::Contact::_removeChild(int index) +{ Item* child = childItems[index]; disconnect(child, &Item::childChanged, this, &Contact::refresh); Item::_removeChild(index); refresh(); } -void Models::Contact::_appendChild(Models::Item* child) { +void Models::Contact::_appendChild(Models::Item* child) +{ Item::_appendChild(child); connect(child, &Item::childChanged, this, &Contact::refresh); refresh(); } -Shared::SubscriptionState Models::Contact::getState() const { +Shared::SubscriptionState Models::Contact::getState() const +{ return state; } -void Models::Contact::setState(Shared::SubscriptionState p_state) { +void Models::Contact::setState(Shared::SubscriptionState p_state) +{ if (state != p_state) { state = p_state; changed(2); } } -Shared::TrustSummary Models::Contact::getTrust() const { - return trust; -} - -void Models::Contact::setTrust(const Shared::TrustSummary& p_trust) { - if (trust != p_trust) { - trust = p_trust; - changed(8); - } -} - - -QIcon Models::Contact::getStatusIcon(bool big) const { +QIcon Models::Contact::getStatusIcon(bool big) const +{ if (getMessagesCount() > 0) { return Shared::icon("mail-message", big); } else if (state == Shared::SubscriptionState::both || state == Shared::SubscriptionState::to) { @@ -232,7 +217,8 @@ QIcon Models::Contact::getStatusIcon(bool big) const { } } -void Models::Contact::toOfflineState() { +void Models::Contact::toOfflineState() +{ std::deque::size_type size = childItems.size(); if (size > 0) { emit childIsAboutToBeRemoved(this, 0, size - 1); @@ -249,31 +235,14 @@ void Models::Contact::toOfflineState() { } } -QString Models::Contact::getDisplayedName() const { +QString Models::Contact::getDisplayedName() const +{ return getContactName(); } -void Models::Contact::handleRecconnect() { - if (getMessagesCount() > 0) +void Models::Contact::handleRecconnect() +{ + if (getMessagesCount() > 0) { feed->requestLatestMessages(); -} - -bool Models::Contact::hasKeys(Shared::EncryptionProtocol protocol) const { - return trust.hasKeys(protocol); -} - -void Models::Contact::setEncryption(Shared::EncryptionProtocol p_enc) { - if (encryption != p_enc) { - encryption = p_enc; - changed(9); } } - -void Models::Contact::setEncryption(unsigned int p_enc) { - setEncryption(Shared::Global::fromInt(p_enc)); -} - -Shared::EncryptionProtocol Models::Contact::getEncryption() const { - return encryption; -} - diff --git a/ui/models/contact.h b/ui/models/contact.h index d03e936..a8b80a3 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -19,18 +19,16 @@ #ifndef MODELS_CONTACT_H #define MODELS_CONTACT_H -#include -#include - -#include - #include "element.h" #include "presence.h" -#include -#include -#include -#include -#include +#include "shared/enums.h" +#include "shared/message.h" +#include "shared/icons.h" +#include "shared/global.h" + +#include +#include +#include namespace Models { @@ -43,7 +41,6 @@ public: Shared::Availability getAvailability() const; Shared::SubscriptionState getState() const; - Shared::EncryptionProtocol getEncryption() const; QIcon getStatusIcon(bool big = false) const; @@ -54,13 +51,10 @@ public: void addPresence(const QString& name, const QMap& data); void removePresence(const QString& name); - Presence* getPresence(const QString& name); QString getContactName() const; QString getStatus() const; QString getDisplayedName() const override; - Shared::TrustSummary getTrust() const; - bool hasKeys(Shared::EncryptionProtocol protocol) const; void handleRecconnect(); //this is a special method Models::Roster calls when reconnect happens @@ -78,18 +72,12 @@ protected: void setState(Shared::SubscriptionState p_state); void setState(unsigned int p_state); void setStatus(const QString& p_state); - void setTrust(const Shared::TrustSummary& p_trust); - void setEncryption(Shared::EncryptionProtocol p_enc); - void setEncryption(unsigned int p_enc); private: Shared::Availability availability; Shared::SubscriptionState state; - Shared::EncryptionProtocol encryption; - Shared::TrustSummary trust; QMap presences; QString status; - }; } diff --git a/ui/models/element.cpp b/ui/models/element.cpp index e89c5aa..4e741a4 100644 --- a/ui/models/element.cpp +++ b/ui/models/element.cpp @@ -75,11 +75,6 @@ void Models::Element::update(const QString& field, const QVariant& value) } } -QString Models::Element::getId() const -{ - return jid; -} - QString Models::Element::getAvatarPath() const { return avatarPath; @@ -139,11 +134,6 @@ unsigned int Models::Element::getMessagesCount() const return feed->unreadMessagesCount(); } -bool Models::Element::markMessageAsRead(const QString& id) const -{ - return feed->markMessageAsRead(id); -} - void Models::Element::addMessage(const Shared::Message& data) { feed->addMessage(data); @@ -181,7 +171,6 @@ void Models::Element::fileError(const QString& messageId, const QString& error, void Models::Element::onFeedUnreadMessagesCountChanged() { - emit unreadMessagesCountChanged(); if (type == contact) { changed(4); } else if (type == room) { diff --git a/ui/models/element.h b/ui/models/element.h index 2ce5a21..94d67cb 100644 --- a/ui/models/element.h +++ b/ui/models/element.h @@ -38,12 +38,10 @@ public: QString getAvatarPath() const; virtual void update(const QString& field, const QVariant& value); - virtual QString getId() const override; void addMessage(const Shared::Message& data); void changeMessage(const QString& id, const QMap& data); unsigned int getMessagesCount() const; - bool markMessageAsRead(const QString& id) const; void responseArchive(const std::list list, bool last); bool isRoom() const; void fileProgress(const QString& messageId, qreal value, bool up); @@ -54,7 +52,6 @@ signals: void requestArchive(const QString& before); void fileDownloadRequest(const QString& url); void unnoticedMessage(const QString& account, const Shared::Message& msg); - void unreadMessagesCountChanged(); void localPathInvalid(const QString& path); protected: diff --git a/ui/models/info/CMakeLists.txt b/ui/models/info/CMakeLists.txt deleted file mode 100644 index 57a078c..0000000 --- a/ui/models/info/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -target_sources(squawk PRIVATE - emails.cpp - emails.h - phones.cpp - phones.h - ) - -if (WITH_OMEMO) - add_subdirectory(omemo) -endif() diff --git a/ui/models/info/omemo/CMakeLists.txt b/ui/models/info/omemo/CMakeLists.txt deleted file mode 100644 index 166ca76..0000000 --- a/ui/models/info/omemo/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -target_sources(squawk PRIVATE - keys.cpp - keys.h -) diff --git a/ui/models/info/omemo/keys.cpp b/ui/models/info/omemo/keys.cpp deleted file mode 100644 index 9ef8c73..0000000 --- a/ui/models/info/omemo/keys.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "keys.h" - -#include "shared/defines.h" - -const QHash Models::Keys::roles = { - {Label, "label"}, - {FingerPrint, "fingerPrint"}, - {TrustLevel, "trustLevel"} -}; - -Models::Keys::Keys(QObject* parent): - QAbstractListModel(parent), - keys(), - modified() {} - -Models::Keys::~Keys() { - for (Shared::KeyInfo* key : keys) - delete key; - - for (std::pair& pair: modified) - delete pair.second; -} - -std::list Models::Keys::modifiedKeys() const { - std::list response; - for (const std::pair& pair: modified) - response.push_back(*(pair.second)); - - - return response; -} - -std::list Models::Keys::finalKeys() const { - std::list result; - finalKeys(result); - return result; -} - -void Models::Keys::finalKeys(std::list& out) const { - for (int i = 0; i < rowCount(); ++i) - out.push_back(key(i)); -} - -const Shared::KeyInfo & Models::Keys::key(unsigned int i) const { - std::map::const_iterator itr = modified.find(i); - if (itr != modified.end()) - return*(itr->second); - else - return *(keys[i]); -} - -void Models::Keys::addKey(const Shared::KeyInfo& info) { - beginInsertRows(QModelIndex(), keys.size(), keys.size()); - keys.push_back(new Shared::KeyInfo(info)); - endInsertRows(); -} - -QVariant Models::Keys::data(const QModelIndex& index, int role) const { - int i = index.row(); - const Shared::KeyInfo* info; - bool dirty; - std::map::const_iterator itr = modified.find(i); - if (itr != modified.end()) { - info = itr->second; - dirty = true; - } else { - dirty = false; - info = keys[i]; - } - QVariant answer; - - switch (role) { - case Qt::DisplayRole: - case Label: - answer = info->label; - break; - case FingerPrint: - answer = info->fingerPrint; - break; - case TrustLevel: - answer = static_cast(info->trustLevel); - break; - case LastInteraction: - answer = info->lastInteraction; - break; - case Dirty: - answer = dirty; - break; - } - - return answer; -} - -int Models::Keys::rowCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); - return keys.size(); -} - -QHash Models::Keys::roleNames() const {return roles;} - -QModelIndex Models::Keys::index(int row, int column, const QModelIndex& parent) const { - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - return createIndex(row, column, keys[row]); -} - -void Models::Keys::revertKey(int row) { - std::map::const_iterator itr = modified.find(row); - if (itr != modified.end()) { - modified.erase(itr); - QModelIndex index = createIndex(row, 0, keys[row]); - dataChanged(index, index); - } -} - -void Models::Keys::setTrustLevel(int row, Shared::TrustLevel level) { - std::map::const_iterator itr = modified.find(row); - Shared::KeyInfo* info; - if (itr == modified.end()) { - if (row < rowCount()) { - info = new Shared::KeyInfo(*(keys[row])); - modified.insert(std::make_pair(row, info)); - } else { - return; - } - } else { - info = itr->second; - } - - info->trustLevel = level; - - QModelIndex index = createIndex(row, 0, info); - dataChanged(index, index, {Keys::Dirty}); -} - -void Models::Keys::clear() { - beginResetModel(); - keys.clear(); - modified.clear(); - endResetModel(); -} - - diff --git a/ui/models/info/omemo/keys.h b/ui/models/info/omemo/keys.h deleted file mode 100644 index 1bc79de..0000000 --- a/ui/models/info/omemo/keys.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include - -namespace Models { - -class Keys : public QAbstractListModel { -public: - Keys(QObject *parent = nullptr); - ~Keys(); - - void addKey(const Shared::KeyInfo& info); - void clear(); - - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - const Shared::KeyInfo& key(unsigned int i) const; - - QHash roleNames() const override; - QModelIndex index(int row, int column, const QModelIndex & parent) const override; - - std::list modifiedKeys() const; - std::list finalKeys() const; - void finalKeys(std::list& out) const; - - enum Roles { - Label = Qt::UserRole + 1, - FingerPrint, - TrustLevel, - LastInteraction, - Dirty - }; - -public slots: - void revertKey(int index); - void setTrustLevel(int index, Shared::TrustLevel level); - -private: - std::deque keys; - std::map modified; - -private: - static const QHash roles; -}; - -} diff --git a/ui/models/item.cpp b/ui/models/item.cpp index dda28d6..4a88dd2 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -181,11 +181,6 @@ QString Models::Item::getName() const return name; } -QString Models::Item::getId() const -{ - return name; -} - QVariant Models::Item::data(int column) const { if (column != 0) { diff --git a/ui/models/item.h b/ui/models/item.h index d3fbb02..4661479 100644 --- a/ui/models/item.h +++ b/ui/models/item.h @@ -65,7 +65,6 @@ class Item : public QObject{ virtual void appendChild(Item *child); virtual void removeChild(int index); virtual QString getDisplayedName() const; - virtual QString getId() const; QString getName() const; void setName(const QString& name); diff --git a/ui/models/participant.cpp b/ui/models/participant.cpp index 7201acb..dc42c07 100644 --- a/ui/models/participant.cpp +++ b/ui/models/participant.cpp @@ -26,46 +26,52 @@ Models::Participant::Participant(const QMap& data, Models::It role(Shared::Role::unspecified) { QMap::const_iterator itr = data.find("affiliation"); - if (itr != data.end()) + if (itr != data.end()) { setAffiliation(itr.value().toUInt()); - + } + itr = data.find("role"); - if (itr != data.end()) + if (itr != data.end()) { setRole(itr.value().toUInt()); - + } + itr = data.find("avatarState"); - if (itr != data.end()) + if (itr != data.end()) { setAvatarState(itr.value().toUInt()); - + } itr = data.find("avatarPath"); - if (itr != data.end()) + if (itr != data.end()) { setAvatarPath(itr.value().toString()); - + } } Models::Participant::~Participant() -{} - -int Models::Participant::columnCount() const { - return 9; +{ } -QVariant Models::Participant::data(int column) const { +int Models::Participant::columnCount() const +{ + return 8; +} + +QVariant Models::Participant::data(int column) const +{ switch (column) { - case 5: + case 4: return QVariant::fromValue(affiliation); - case 6: + case 5: return QVariant::fromValue(role); - case 7: + case 6: return QVariant::fromValue(getAvatarState()); - case 8: + case 7: return getAvatarPath(); default: return AbstractParticipant::data(column); } } -void Models::Participant::update(const QString& key, const QVariant& value) { +void Models::Participant::update(const QString& key, const QVariant& value) +{ if (key == "affiliation") { setAffiliation(value.toUInt()); } else if (key == "role") { @@ -79,58 +85,67 @@ void Models::Participant::update(const QString& key, const QVariant& value) { } } -Shared::Affiliation Models::Participant::getAffiliation() const { +Shared::Affiliation Models::Participant::getAffiliation() const +{ return affiliation; } -void Models::Participant::setAffiliation(Shared::Affiliation p_aff) { +void Models::Participant::setAffiliation(Shared::Affiliation p_aff) +{ if (p_aff != affiliation) { affiliation = p_aff; + changed(4); + } +} + +void Models::Participant::setAffiliation(unsigned int aff) +{ + setAffiliation(Shared::Global::fromInt(aff)); +} + +Shared::Role Models::Participant::getRole() const +{ + return role; +} + +void Models::Participant::setRole(Shared::Role p_role) +{ + if (p_role != role) { + role = p_role; changed(5); } } -void Models::Participant::setAffiliation(unsigned int aff) { - setAffiliation(Shared::Global::fromInt(aff)); -} - -Shared::Role Models::Participant::getRole() const { - return role; -} - -void Models::Participant::setRole(Shared::Role p_role) { - if (p_role != role) { - role = p_role; - changed(6); - } -} - -void Models::Participant::setRole(unsigned int p_role) { +void Models::Participant::setRole(unsigned int p_role) +{ setRole(Shared::Global::fromInt(p_role)); } -QString Models::Participant::getAvatarPath() const { +QString Models::Participant::getAvatarPath() const +{ return avatarPath; } -Shared::Avatar Models::Participant::getAvatarState() const { +Shared::Avatar Models::Participant::getAvatarState() const +{ return avatarState; } -void Models::Participant::setAvatarPath(const QString& path) { +void Models::Participant::setAvatarPath(const QString& path) +{ if (avatarPath != path) { avatarPath = path; - changed(8); - } -} - -void Models::Participant::setAvatarState(Shared::Avatar p_state) { - if (avatarState != p_state) { - avatarState = p_state; changed(7); } } -void Models::Participant::setAvatarState(unsigned int p_state) { - setAvatarState(Shared::Global::fromInt(p_state)); +void Models::Participant::setAvatarState(Shared::Avatar p_state) +{ + if (avatarState != p_state) { + avatarState = p_state; + changed(6); + } } + +void Models::Participant::setAvatarState(unsigned int p_state) +{setAvatarState(Shared::Global::fromInt(p_state));} diff --git a/ui/models/presence.cpp b/ui/models/presence.cpp index a867243..8ba7c47 100644 --- a/ui/models/presence.cpp +++ b/ui/models/presence.cpp @@ -21,8 +21,14 @@ Models::Presence::Presence(const QMap& data, Item* parentItem): AbstractParticipant(Item::presence, data, parentItem) -{} +{ +} Models::Presence::~Presence() -{} +{ +} +int Models::Presence::columnCount() const +{ + return 4; +} diff --git a/ui/models/presence.h b/ui/models/presence.h index 4df662a..fb1a31c 100644 --- a/ui/models/presence.h +++ b/ui/models/presence.h @@ -34,6 +34,8 @@ class Presence : public Models::AbstractParticipant public: explicit Presence(const QMap &data, Item *parentItem = 0); ~Presence(); + + int columnCount() const override; }; } diff --git a/ui/models/reference.cpp b/ui/models/reference.cpp index fe3bf41..1aaea15 100644 --- a/ui/models/reference.cpp +++ b/ui/models/reference.cpp @@ -75,11 +75,6 @@ QString Models::Reference::getDisplayedName() const return original->getDisplayedName(); } -QString Models::Reference::getId() const -{ - return original->getId(); -} - Models::Item * Models::Reference::dereference() { return original; diff --git a/ui/models/reference.h b/ui/models/reference.h index bc717bb..8ec5352 100644 --- a/ui/models/reference.h +++ b/ui/models/reference.h @@ -38,8 +38,6 @@ public: QString getDisplayedName() const override; void appendChild(Models::Item * child) override; void removeChild(int index) override; - QString getId() const override; - Item* dereference(); const Item* dereferenceConst() const; diff --git a/ui/models/room.cpp b/ui/models/room.cpp index 4aaa07e..a6a36d0 100644 --- a/ui/models/room.cpp +++ b/ui/models/room.cpp @@ -264,16 +264,6 @@ void Models::Room::removeParticipant(const QString& p_name) } } -Models::Participant * Models::Room::getParticipant(const QString& p_name) -{ - std::map::const_iterator itr = participants.find(p_name); - if (itr == participants.end()) { - return nullptr; - } else { - return itr->second; - } -} - void Models::Room::handleParticipantUpdate(std::map::const_iterator itr, const QMap& data) { Participant* part = itr->second; diff --git a/ui/models/room.h b/ui/models/room.h index b3e889c..a51a537 100644 --- a/ui/models/room.h +++ b/ui/models/room.h @@ -58,7 +58,6 @@ public: void addParticipant(const QString& name, const QMap& data); void changeParticipant(const QString& name, const QMap& data); void removeParticipant(const QString& name); - Participant* getParticipant(const QString& name); void toOfflineState() override; QString getDisplayedName() const override; @@ -76,6 +75,7 @@ private: private: bool autoJoin; bool joined; + QString jid; QString nick; QString subject; std::map participants; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index fc6382a..2d5f99f 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -21,8 +21,6 @@ #include #include -#include "shared/defines.h" - Models::Roster::Roster(QObject* parent): QAbstractItemModel(parent), accountsModel(new Accounts()), @@ -41,36 +39,39 @@ Models::Roster::Roster(QObject* parent): connect(root, &Item::childMoved, this, &Roster::onChildMoved); } -Models::Roster::~Roster() { +Models::Roster::~Roster() +{ delete accountsModel; delete root; } -void Models::Roster::addAccount(const QMap& data) { +void Models::Roster::addAccount(const QMap& data) +{ Account* acc = new Account(data); connect(acc, &Account::reconnected, this, &Roster::onAccountReconnected); root->appendChild(acc); accounts.insert(std::make_pair(acc->getName(), acc)); accountsModel->addAccount(acc); - - emit addedElement({acc->getId()}); } -QVariant Models::Roster::data (const QModelIndex& index, int role) const { - if (!index.isValid()) +QVariant Models::Roster::data (const QModelIndex& index, int role) const +{ + if (!index.isValid()) { return QVariant(); + } QVariant result; Item *item = static_cast(index.internalPointer()); - if (item->type == Item::reference) + if (item->type == Item::reference) { item = static_cast(item)->dereference(); - + } switch (role) { - case Qt::DisplayRole: { - if (index.column() != 0) + case Qt::DisplayRole: + { + if (index.column() != 0) { break; - + } switch (item->type) { case Item::group: { Group* gr = static_cast(item); @@ -78,9 +79,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { str += gr->getName(); unsigned int amount = gr->getUnreadMessages(); - if (amount > 0) + if (amount > 0) { str += QString(" (") + tr("New messages") + ")"; - + } result = str; } @@ -101,8 +102,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { } else if (col == 1) { QString path = acc->getAvatarPath(); - if (path.size() > 0) + if (path.size() > 0) { result = QIcon(path); + } } } break; @@ -112,15 +114,16 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { if (col == 0) { result = contact->getStatusIcon(false); } else if (col == 1) { - if (contact->getAvatarState() != Shared::Avatar::empty) + if (contact->getAvatarState() != Shared::Avatar::empty) { result = QIcon(contact->getAvatarPath()); + } } } break; case Item::presence: { - if (index.column() != 0) + if (index.column() != 0) { break; - + } Presence* presence = static_cast(item); result = presence->getStatusIcon(false); } @@ -133,8 +136,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { } else if (col == 1) { QString path = room->getAvatarPath(); - if (path.size() > 0) + if (path.size() > 0) { result = QIcon(path); + } } } break; @@ -145,11 +149,14 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { result = p->getStatusIcon(false); } else if (col == 1) { QString path = p->getAvatarPath(); - if (path.size() > 0) + if (path.size() > 0) { result = QIcon(path); + } } - if (index.column() != 0) + if (index.column() != 0) { break; + } + } break; default: @@ -185,9 +192,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { Contact* contact = static_cast(item); QString str(""); int mc = contact->getMessagesCount(); - if (mc > 0) + if (mc > 0) { str += QString(tr("New messages: ")) + std::to_string(mc).c_str() + "\n"; - + } str += tr("Jabber ID: ") + contact->getJid() + "\n"; Shared::SubscriptionState ss = contact->getState(); if (ss == Shared::SubscriptionState::both || ss == Shared::SubscriptionState::to) { @@ -195,8 +202,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { str += tr("Availability: ") + Shared::Global::getName(av); if (av != Shared::Availability::offline) { QString s = contact->getStatus(); - if (s.size() > 0) + if (s.size() > 0) { str += "\n" + tr("Status: ") + s; + } } str += "\n" + tr("Subscription: ") + Shared::Global::getName(ss); } else { @@ -212,10 +220,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { Shared::Availability av = contact->getAvailability(); str += tr("Availability: ") + Shared::Global::getName(av); QString s = contact->getStatus(); - if (s.size() > 0) + if (s.size() > 0) { str += "\n" + tr("Status: ") + s; - - str += "\n" + tr("Client: ") + contact->getClientNode(); + } result = str; } @@ -226,12 +233,12 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { Shared::Availability av = p->getAvailability(); str += tr("Availability: ") + Shared::Global::getName(av) + "\n"; QString s = p->getStatus(); - if (s.size() > 0) + if (s.size() > 0) { str += tr("Status: ") + s + "\n"; + } str += tr("Affiliation: ") + Shared::Global::getName(p->getAffiliation()) + "\n"; - str += tr("Role: ") + Shared::Global::getName(p->getRole()) + "\n"; - str += tr("Client: ") + p->getClientNode(); + str += tr("Role: ") + Shared::Global::getName(p->getRole()); result = str; } @@ -240,9 +247,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { Group* gr = static_cast(item); unsigned int count = gr->getUnreadMessages(); QString str(""); - if (count > 0) + if (count > 0) { str += tr("New messages: ") + std::to_string(count).c_str() + "\n"; - + } str += tr("Online contacts: ") + std::to_string(gr->getOnlineContacts()).c_str() + "\n"; str += tr("Total contacts: ") + std::to_string(gr->childCount()).c_str(); result = str; @@ -252,14 +259,15 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { Room* rm = static_cast(item); unsigned int count = rm->getMessagesCount(); QString str(""); - if (count > 0) + if (count > 0) { str += tr("New messages: ") + std::to_string(count).c_str() + "\n"; + } str += tr("Jabber ID: ") + rm->getJid() + "\n"; str += tr("Subscription: ") + rm->getStatusText(); - if (rm->getJoined()) + if (rm->getJoined()) { str += QString("\n") + tr("Members: ") + std::to_string(rm->childCount()).c_str(); - + } result = str; } break; @@ -268,17 +276,6 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { break; } break; - case Qt::ForegroundRole: - switch (item->type) { - case Item::account: { - Account* acc = static_cast(item); - if (!acc->getActive()) - result = qApp->palette().brush(QPalette::Disabled, QPalette::Text); - } - break; - default: - break; - } default: break; } @@ -286,14 +283,17 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const { return result; } -int Models::Roster::columnCount (const QModelIndex& parent) const { - if (parent.isValid()) +int Models::Roster::columnCount (const QModelIndex& parent) const +{ + if (parent.isValid()) { return static_cast(parent.internalPointer())->columnCount(); - else + } else { return root->columnCount(); + } } -void Models::Roster::updateAccount(const QString& account, const QString& field, const QVariant& value) { +void Models::Roster::updateAccount(const QString& account, const QString& field, const QVariant& value) +{ std::map::iterator itr = accounts.find(account); if (itr != accounts.end()) { Account* acc = itr->second; @@ -301,63 +301,75 @@ void Models::Roster::updateAccount(const QString& account, const QString& field, } } -Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const { - if (!index.isValid()) + +Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const +{ + if (!index.isValid()) { return Qt::ItemFlags(); + } return QAbstractItemModel::flags(index); } -int Models::Roster::rowCount (const QModelIndex& parent) const{ + +int Models::Roster::rowCount (const QModelIndex& parent) const +{ Item *parentItem; - if (!parent.isValid()) + if (!parent.isValid()) { parentItem = root; - else + } else { parentItem = static_cast(parent.internalPointer()); + } return parentItem->childCount(); } -QVariant Models::Roster::headerData(int section, Qt::Orientation orientation, int role) const { - SHARED_UNUSED(section); - SHARED_UNUSED(orientation); - SHARED_UNUSED(role); - +QVariant Models::Roster::headerData(int section, Qt::Orientation orientation, int role) const +{ return QVariant(); } -QModelIndex Models::Roster::parent (const QModelIndex& child) const { - if (!child.isValid()) +QModelIndex Models::Roster::parent (const QModelIndex& child) const +{ + if (!child.isValid()) { return QModelIndex(); + } Item *childItem = static_cast(child.internalPointer()); - if (childItem == root) + if (childItem == root) { return QModelIndex(); + } Item *parentItem = childItem->parentItem(); - if (parentItem == root) + + if (parentItem == root) { return createIndex(0, 0, parentItem); + } return createIndex(parentItem->row(), 0, parentItem); } -QModelIndex Models::Roster::index (int row, int column, const QModelIndex& parent) const { - if (!hasIndex(row, column, parent)) +QModelIndex Models::Roster::index (int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) { return QModelIndex(); + } Item *parentItem; - if (!parent.isValid()) + if (!parent.isValid()) { parentItem = root; - else + } else { parentItem = static_cast(parent.internalPointer()); + } Item *childItem = parentItem->child(row); - if (childItem) + if (childItem) { return createIndex(row, column, childItem); - else + } else { return QModelIndex(); + } } Models::Roster::ElId::ElId(const QString& p_account, const QString& p_name): @@ -365,22 +377,27 @@ Models::Roster::ElId::ElId(const QString& p_account, const QString& p_name): name(p_name) {} -bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const { - if (account == other.account) +bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const +{ + if (account == other.account) { return name < other.name; - else + } else { return account < other.account; + } } -bool Models::Roster::ElId::operator!=(const Models::Roster::ElId& other) const { +bool Models::Roster::ElId::operator!=(const Models::Roster::ElId& other) const +{ return !(operator == (other)); } -bool Models::Roster::ElId::operator==(const Models::Roster::ElId& other) const { +bool Models::Roster::ElId::operator==(const Models::Roster::ElId& other) const +{ return (account == other.account) && (name == other.name); } -void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles) { +void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles) +{ if (tl.column() == 0) { emit dataChanged(tl, br, roles); } else if (tl.column() == 2) { @@ -390,7 +407,8 @@ void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelInd } } -void Models::Roster::addGroup(const QString& account, const QString& name) { +void Models::Roster::addGroup(const QString& account, const QString& name) +{ ElId id(account, name); std::map::const_iterator gItr = groups.find(id); if (gItr != groups.end()) { @@ -403,14 +421,13 @@ void Models::Roster::addGroup(const QString& account, const QString& name) { Group* group = new Group({{"name", name}}); groups.insert(std::make_pair(id, group)); acc->appendChild(group); - - emit addedElement({acc->getId(), group->getId()}); } else { qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping"; } } -void Models::Roster::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) { +void Models::Roster::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) +{ Item* parent; Account* acc; Contact* contact; @@ -434,14 +451,12 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest); connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage); connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid); - connect(contact, &Contact::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages); contacts.insert(std::make_pair(id, contact)); } else { contact = itr->second; } } - std::list path = {acc->getId()}; if (group == "") { if (acc->getContact(jid) != -1) { qDebug() << "An attempt to add a contact" << jid << "to the ungrouped contact set of account" << account << "for the second time, skipping"; @@ -458,7 +473,6 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons } parent = itr->second; - path.push_back(parent->getId()); if (parent->getContact(jid) != -1) { qDebug() << "An attempt to add a contact" << jid << "to the group" << group << "for the second time, skipping"; @@ -475,17 +489,15 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons } } - path.push_back(contact->getId()); - if (ref == 0) + if (ref == 0) { ref = new Reference(contact); - + } parent->appendChild(ref); - - emit addedElement(path); } -void Models::Roster::removeGroup(const QString& account, const QString& name) { +void Models::Roster::removeGroup(const QString& account, const QString& name) +{ ElId id(account, name); std::map::const_iterator gItr = groups.find(id); if (gItr == groups.end()) { @@ -504,10 +516,11 @@ void Models::Roster::removeGroup(const QString& account, const QString& name) { item->removeChild(0); Contact* cont = static_cast(ref->dereference()); - if (cont->referencesCount() == 1) + if (cont->referencesCount() == 1) { toInsert.push_back(ref); - else + } else { delete ref; + } } if (toInsert.size() > 0) { @@ -521,24 +534,26 @@ void Models::Roster::removeGroup(const QString& account, const QString& name) { groups.erase(gItr); } -void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap& data) { - Element* el = getElement(ElId(account, jid)); - if (el != nullptr) { +void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap& data) +{ + Element* el = getElement({account, jid}); + if (el != NULL) { for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { el->update(itr.key(), itr.value()); } } } -void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) { - Element* el = getElement(ElId(account, jid)); - if (el != nullptr) +void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) +{ + Element* el = getElement({account, jid}); + if (el != NULL) { el->changeMessage(id, data); - else - qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found"; + } } -void Models::Roster::removeContact(const QString& account, const QString& jid) { +void Models::Roster::removeContact(const QString& account, const QString& jid) +{ ElId id(account, jid); std::map::iterator itr = contacts.find(id); @@ -550,18 +565,21 @@ void Models::Roster::removeContact(const QString& account, const QString& jid) { std::set toRemove; for (std::pair pair : groups) { - if (pair.second->childCount() == 0) + if (pair.second->childCount() == 0) { toRemove.insert(pair.first); + } } - for (const ElId& elId : toRemove) + for (const ElId& elId : toRemove) { removeGroup(elId.account, elId.name); + } } else { qDebug() << "An attempt to remove contact " << jid << " from account " << account <<" which doesn't exist there, skipping"; } } -void Models::Roster::removeContact(const QString& account, const QString& jid, const QString& group) { +void Models::Roster::removeContact(const QString& account, const QString& jid, const QString& group) +{ ElId contactId(account, jid); ElId groupId(account, group); @@ -594,18 +612,20 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c } else { delete ref; } - if (gr->childCount() == 0) + if (gr->childCount() == 0) { removeGroup(account, group); + } } -void Models::Roster::onChildChanged(Models::Item* item, int row, int col) { - SHARED_UNUSED(col); +void Models::Roster::onChildChanged(Models::Item* item, int row, int col) +{ QModelIndex index = createIndex(row, 0, item); QModelIndex index2 = createIndex(row, 1, item); emit dataChanged(index, index2); } -void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last) { +void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last) +{ int row = 0; if (parent != root) { row = parent->row(); @@ -615,48 +635,54 @@ void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, } } -void Models::Roster::onChildIsAboutToBeMoved(Models::Item* source, int first, int last, Models::Item* destination, int newIndex) { +void Models::Roster::onChildIsAboutToBeMoved(Models::Item* source, int first, int last, Models::Item* destination, int newIndex) +{ int oldRow = 0; - if (source != root) + if (source != root) { oldRow = source->row(); - + } int newRow = 0; - if (destination != root) + if (destination != root) { newRow = destination->row(); - + } beginMoveRows(createIndex(oldRow, 0, source), first, last, createIndex(newRow, 0, destination), newIndex); } -void Models::Roster::onChildIsAboutToBeRemoved(Models::Item* parent, int first, int last) { +void Models::Roster::onChildIsAboutToBeRemoved(Models::Item* parent, int first, int last) +{ int row = 0; - if (parent != root) + if (parent != root) { row = parent->row(); - + } beginRemoveRows(createIndex(row, 0, parent), first, last); } -void Models::Roster::onChildInserted() { +void Models::Roster::onChildInserted() +{ endInsertRows(); } -void Models::Roster::onChildMoved() { +void Models::Roster::onChildMoved() +{ endMoveRows(); } -void Models::Roster::onChildRemoved() { +void Models::Roster::onChildRemoved() +{ endRemoveRows(); } -void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) { +void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ ElId contactId(account, jid); std::map::iterator itr = contacts.find(contactId); - if (itr != contacts.end()) + if (itr != contacts.end()) { itr->second->addPresence(name, data); - else - qDebug() << "Received a presence" << jid + "/" + name << "don't know what to do with it"; + } } -void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name) { +void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name) +{ ElId contactId(account, jid); std::map::iterator itr = contacts.find(contactId); if (itr != contacts.end()) { @@ -664,13 +690,16 @@ void Models::Roster::removePresence(const QString& account, const QString& jid, } } -void Models::Roster::addMessage(const QString& account, const Shared::Message& data) { - Element* el = getElement(ElId(account, data.getPenPalJid())); - if (el != nullptr) +void Models::Roster::addMessage(const QString& account, const Shared::Message& data) +{ + Element* el = getElement({account, data.getPenPalJid()}); + if (el != NULL) { el->addMessage(data); + } } -void Models::Roster::removeAccount(const QString& account) { +void Models::Roster::removeAccount(const QString& account) +{ std::map::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { qDebug() << "An attempt to remove non existing account " << account << ", skipping"; @@ -720,23 +749,26 @@ void Models::Roster::removeAccount(const QString& account) { acc->deleteLater(); } -QString Models::Roster::getContactName(const QString& account, const QString& jid) const { +QString Models::Roster::getContactName(const QString& account, const QString& jid) +{ ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); QString name = ""; if (cItr == contacts.end()) { std::map::const_iterator rItr = rooms.find(id); - if (rItr == rooms.end()) + if (rItr == rooms.end()) { qDebug() << "An attempt to get a name of non existing contact/room " << account << ":" << jid << ", skipping"; - else + } else { name = rItr->second->getRoomName(); + } } else { name = cItr->second->getContactName(); } return name; } -void Models::Roster::addRoom(const QString& account, const QString jid, const QMap& data) { +void Models::Roster::addRoom(const QString& account, const QString jid, const QMap& data) +{ Account* acc; { std::map::iterator itr = accounts.find(account); @@ -755,18 +787,16 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM } Room* room = new Room(acc, jid, data); - connect(room, &Room::requestArchive, this, &Roster::onElementRequestArchive); - connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest); - connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage); - connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid); - connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages); + connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive); + connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest); + connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage); + connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid); rooms.insert(std::make_pair(id, room)); acc->appendChild(room); - - emit addedElement({acc->getId(), room->getId()}); } -void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap& data) { +void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap& data) +{ ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -779,7 +809,8 @@ void Models::Roster::changeRoom(const QString& account, const QString jid, const } } -void Models::Roster::removeRoom(const QString& account, const QString jid) { +void Models::Roster::removeRoom(const QString& account, const QString jid) +{ Account* acc; { std::map::iterator itr = accounts.find(account); @@ -803,7 +834,8 @@ void Models::Roster::removeRoom(const QString& account, const QString jid) { rooms.erase(itr); } -void Models::Roster::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) { +void Models::Roster::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -814,7 +846,8 @@ void Models::Roster::addRoomParticipant(const QString& account, const QString& j } } -void Models::Roster::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) { +void Models::Roster::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -825,7 +858,8 @@ void Models::Roster::changeRoomParticipant(const QString& account, const QString } } -void Models::Roster::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) { +void Models::Roster::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) +{ ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -836,17 +870,20 @@ void Models::Roster::removeRoomParticipant(const QString& account, const QString } } -std::deque Models::Roster::groupList(const QString& account) const { +std::deque Models::Roster::groupList(const QString& account) const +{ std::deque answer; for (std::pair pair : groups) { - if (pair.first.account == account) + if (pair.first.account == account) { answer.push_back(pair.first.name); + } } return answer; } -bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const { +bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const +{ ElId grId({account, group}); std::map::const_iterator gItr = groups.find(grId); if (gItr == groups.end()) { @@ -856,52 +893,33 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou } } -QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const { +QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) +{ ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); QString path = ""; if (cItr == contacts.end()) { std::map::const_iterator rItr = rooms.find(id); - if (rItr == rooms.end()) + if (rItr == rooms.end()) { qDebug() << "An attempt to get an icon path of non existing contact" << account << ":" << jid << ", returning empty value"; - else + } else { path = rItr->second->getParticipantIconPath(resource); + } } else { - if (cItr->second->getAvatarState() != Shared::Avatar::empty) + if (cItr->second->getAvatarState() != Shared::Avatar::empty) { path = cItr->second->getAvatarPath(); + } } return path; } -Models::Account * Models::Roster::getAccount(const QString& name) { - return const_cast(getAccountConst(name));} - -const Models::Account * Models::Roster::getAccountConst(const QString& name) const { - return accounts.at(name);} - -const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const { - std::map::const_iterator cItr = contacts.find(id); - - if (cItr != contacts.end()) { - return cItr->second; - } else { - std::map::const_iterator rItr = rooms.find(id); - if (rItr != rooms.end()) - return rItr->second; - } - - return nullptr; +Models::Account * Models::Roster::getAccount(const QString& name) +{ + return accounts.find(name)->second; } -bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId) { - const Element* el = getElementConst(elementId); - if (el != nullptr) - return el->markMessageAsRead(messageId); - else - return false; -} - -QModelIndex Models::Roster::getAccountIndex(const QString& name) const { +QModelIndex Models::Roster::getAccountIndex(const QString& name) +{ std::map::const_iterator itr = accounts.find(name); if (itr == accounts.end()) { return QModelIndex(); @@ -910,12 +928,13 @@ QModelIndex Models::Roster::getAccountIndex(const QString& name) const { } } -QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name) const { +QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name) +{ std::map::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { return QModelIndex(); } else { - std::map::const_iterator gItr = groups.find(ElId(account, name)); + std::map::const_iterator gItr = groups.find({account, name}); if (gItr == groups.end()) { return QModelIndex(); } else { @@ -925,173 +944,76 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& } } -QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource) const { - std::map::const_iterator itr = accounts.find(account); - if (itr == accounts.end()) { - return QModelIndex(); - } else { - Account* acc = itr->second; - QModelIndex accIndex = index(acc->row(), 0, QModelIndex()); - std::map::const_iterator cItr = contacts.find(ElId(account, jid)); - if (cItr != contacts.end()) { - QModelIndex contactIndex = index(acc->getContact(jid), 0, accIndex); - if (resource.size() == 0) { - return contactIndex; - } else { - Presence* pres = cItr->second->getPresence(resource); - if (pres != nullptr) - return index(pres->row(), 0, contactIndex); - else - return contactIndex; - } - } else { - std::map::const_iterator rItr = rooms.find(ElId(account, jid)); - if (rItr != rooms.end()) { - QModelIndex roomIndex = index(rItr->second->row(), 0, accIndex); - if (resource.size() == 0) { - return roomIndex; - } else { - Participant* part = rItr->second->getParticipant(resource); - if (part != nullptr) - return index(part->row(), 0, roomIndex); - else - return roomIndex; - } - } else { - return QModelIndex(); - } - } - } -} - -void Models::Roster::onElementRequestArchive(const QString& before) { +void Models::Roster::onElementRequestArchive(const QString& before) +{ Element* el = static_cast(sender()); emit requestArchive(el->getAccountName(), el->getJid(), before); } -void Models::Roster::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) { +void Models::Roster::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) +{ ElId id(account, jid); Element* el = getElement(id); - if (el != nullptr) + if (el != NULL) { el->responseArchive(list, last); + } } -void Models::Roster::fileProgress(const std::list& msgs, qreal value, bool up) { +void Models::Roster::fileProgress(const std::list& msgs, qreal value, bool up) +{ for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) + Element* el = getElement({info.account, info.jid}); + if (el != NULL) { el->fileProgress(info.messageId, value, up); + } } } -void Models::Roster::fileComplete(const std::list& msgs, bool up) { +void Models::Roster::fileComplete(const std::list& msgs, bool up) +{ for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) + Element* el = getElement({info.account, info.jid}); + if (el != NULL) { el->fileComplete(info.messageId, up); + } } } -void Models::Roster::fileError(const std::list& msgs, const QString& err, bool up) { +void Models::Roster::fileError(const std::list& msgs, const QString& err, bool up) +{ for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) + Element* el = getElement({info.account, info.jid}); + if (el != NULL) { el->fileError(info.messageId, err, up); + } } } -Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) { - return const_cast(getElementConst(id)); +Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) +{ + std::map::iterator cItr = contacts.find(id); + + if (cItr != contacts.end()) { + return cItr->second; + } else { + std::map::iterator rItr = rooms.find(id); + if (rItr != rooms.end()) { + return rItr->second; + } + } + + return NULL; } -Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const { - const Models::Element* el = getElementConst(id); - if (el == nullptr) - return Item::root; - - - return el->type; -} - - -void Models::Roster::onAccountReconnected() { +void Models::Roster::onAccountReconnected() +{ Account* acc = static_cast(sender()); QString accName = acc->getName(); for (const std::pair& pair : contacts) { - if (pair.first.account == accName) + if (pair.first.account == accName) { pair.second->handleRecconnect(); - } -} - -void Models::Roster::recalculateUnreadMessages() { - int count(0); - for (const std::pair& pair : contacts) - count += pair.second->getMessagesCount(); - - for (const std::pair& pair : rooms) - count += pair.second->getMessagesCount(); - - emit unreadMessagesCountChanged(count); -} - -std::list Models::Roster::getItemPath(const QModelIndex& index) const { - std::list result; - if (index.isValid() && index.model() == this) { - Item* item = static_cast(index.internalPointer()); - while (item->type != Item::root) { - result.push_front(item->getId()); - item = item->parentItem(); } } - - return result; } -QModelIndex Models::Roster::getIndexByPath(const std::list& path) const { - if (path.empty()) - return QModelIndex(); - - QModelIndex current; - for (const QString& hop : path) { - int rows = rowCount(current); - bool found = false; - for (int i = 0; i < rows; ++i) { - QModelIndex el = index(i, 0, current); - Item* item = static_cast(el.internalPointer()); - if (item->getId() == hop) { - found = true; - current = el; - break; - } - } - if (!found) - break; - - } - return current; //this way I will return the last matching model index, may be it's logically incorrect - - -// std::list::const_iterator pathItr = path.begin(); -// QString accName = *pathItr; -// QModelIndex accIndex = getAccountIndex(accName); -// if (path.size() == 1) -// return accIndex; -// -// if (!accIndex.isValid()) -// return QModelIndex(); -// -// ++pathItr; -// ElId id{accName, *pathItr}; -// QModelIndex contactIndex = getContactIndex(id.account, id.name); -// if (!contactIndex.isValid()) -// contactIndex = getGroupIndex(id.account, id.name); -// -// if (path.size() == 2) -// return contactIndex; -// -// if (!contactIndex.isValid()) -// return QModelIndex(); -// -// ++pathItr; -} diff --git a/ui/models/roster.h b/ui/models/roster.h index 59fef44..08d5afc 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "shared/message.h" @@ -47,7 +46,6 @@ public: Roster(QObject* parent = 0); ~Roster(); -public slots: void addAccount(const QMap &data); void updateAccount(const QString& account, const QString& field, const QVariant& value); void removeAccount(const QString& account); @@ -67,12 +65,7 @@ public slots: void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); - -public: - QString getContactName(const QString& account, const QString& jid) const; - Item::Type getContactType(const Models::Roster::ElId& id) const; - const Element* getElementConst(const ElId& id) const; - Element* getElement(const ElId& id); + QString getContactName(const QString& account, const QString& jid); QVariant data ( const QModelIndex& index, int role ) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; @@ -84,16 +77,10 @@ public: std::deque groupList(const QString& account) const; bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const; - QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const; + QString getContactIconPath(const QString& account, const QString& jid, const QString& resource); Account* getAccount(const QString& name); - const Account* getAccountConst(const QString& name) const; - QModelIndex getAccountIndex(const QString& name) const; - QModelIndex getGroupIndex(const QString& account, const QString& name) const; - QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "") const; - QModelIndex getIndexByPath(const std::list& path) const; - std::list getItemPath(const QModelIndex& index) const; - - bool markMessageAsRead(const ElId& elementId, const QString& messageId); + QModelIndex getAccountIndex(const QString& name); + QModelIndex getGroupIndex(const QString& account, const QString& name); void responseArchive(const QString& account, const QString& jid, const std::list& list, bool last); void fileProgress(const std::list& msgs, qreal value, bool up); @@ -105,10 +92,11 @@ public: signals: void requestArchive(const QString& account, const QString& jid, const QString& before); void fileDownloadRequest(const QString& url); - void unreadMessagesCountChanged(int count); void unnoticedMessage(const QString& account, const Shared::Message& msg); void localPathInvalid(const QString& path); - void addedElement(const std::list& path); //emits only on addition of Account, Contact, Room or Group. Presence and Participant are ignored + +private: + Element* getElement(const ElId& id); private slots: void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles); @@ -121,8 +109,7 @@ private slots: void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex); void onChildMoved(); void onElementRequestArchive(const QString& before); - void recalculateUnreadMessages(); - + private: Item* root; std::map accounts; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 64ea636..6a0a676 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -21,27 +21,25 @@ #include #include -Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : +Squawk::Squawk(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), - accounts(nullptr), - preferences(nullptr), - about(nullptr), - rosterModel(p_rosterModel), + accounts(0), + rosterModel(), + conversations(), contextMenu(new QMenu()), - infoWidgets(), - currentConversation(nullptr), + dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), + vCards(), + requestedAccountsForPasswords(), + prompt(0), + currentConversation(0), restoreSelection(), needToRestore(false) { m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu); - if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) - m_ui->roster->setColumnWidth(1, 52); - else - m_ui->roster->setColumnWidth(1, 26); - + m_ui->roster->setColumnWidth(1, 30); m_ui->roster->setIconSize(QSize(20, 20)); m_ui->roster->header()->setStretchLastSection(false); m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch); @@ -53,51 +51,51 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : m_ui->comboBox->setCurrentIndex(static_cast(Shared::Availability::offline)); connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts); - connect(m_ui->actionPreferences, &QAction::triggered, this, &Squawk::onPreferences); connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); - connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::quit); + connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::close); connect(m_ui->comboBox, qOverload(&QComboBox::activated), this, &Squawk::onComboboxActivated); //connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked); connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); - connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::itemCollapsed); - connect(m_ui->roster, &QTreeView::expanded, this, &Squawk::itemExpanded); + connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); + connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage); - connect(rosterModel.accountsModel, &Models::Accounts::changed, this, &Squawk::onAccountsChanged); + connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); + connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive); + connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest); + connect(&rosterModel, &Models::Roster::localPathInvalid, this, &Squawk::localPathInvalid); connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide); - connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); //m_ui->mainToolBar->addWidget(m_ui->comboBox); - if (testAttribute(Qt::WA_TranslucentBackground)) + if (testAttribute(Qt::WA_TranslucentBackground)) { m_ui->roster->viewport()->setAutoFillBackground(false); - + } QSettings settings; settings.beginGroup("ui"); settings.beginGroup("window"); - if (settings.contains("geometry")) + if (settings.contains("geometry")) { restoreGeometry(settings.value("geometry").toByteArray()); - - if (settings.contains("state")) + } + if (settings.contains("state")) { restoreState(settings.value("state").toByteArray()); - + } settings.endGroup(); - if (settings.contains("splitter")) + if (settings.contains("splitter")) { m_ui->splitter->restoreState(settings.value("splitter").toByteArray()); - + } settings.endGroup(); - - onAccountsChanged(); } Squawk::~Squawk() { delete contextMenu; } -void Squawk::onAccounts() { - if (accounts == nullptr) { +void Squawk::onAccounts() +{ + if (accounts == 0) { accounts = new Accounts(rosterModel.accountsModel); accounts->setAttribute(Qt::WA_DeleteOnClose); connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed); @@ -115,24 +113,8 @@ void Squawk::onAccounts() { } } -void Squawk::onPreferences() { - if (preferences == nullptr) { - preferences = new Settings(); - preferences->setAttribute(Qt::WA_DeleteOnClose); - connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed); - connect(preferences, &Settings::changeDownloadsPath, this, &Squawk::changeDownloadsPath); - connect(preferences, &Settings::changeTray, this, &Squawk::changeTray); - - preferences->show(); - } else { - preferences->show(); - preferences->raise(); - preferences->activateWindow(); - } -} - -void Squawk::onAccountsChanged() { - unsigned int size = rosterModel.accountsModel->activeSize(); +void Squawk::onAccountsSizeChanged(unsigned int size) +{ if (size > 0) { m_ui->actionAddContact->setEnabled(true); m_ui->actionAddConference->setEnabled(true); @@ -142,7 +124,8 @@ void Squawk::onAccountsChanged() { } } -void Squawk::onNewContact() { +void Squawk::onNewContact() +{ NewContact* nc = new NewContact(rosterModel.accountsModel, this); connect(nc, &NewContact::accepted, this, &Squawk::onNewContactAccepted); @@ -151,7 +134,8 @@ void Squawk::onNewContact() { nc->exec(); } -void Squawk::onNewConference() { +void Squawk::onNewConference() +{ JoinConference* jc = new JoinConference(rosterModel.accountsModel, this); connect(jc, &JoinConference::accepted, this, &Squawk::onJoinConferenceAccepted); @@ -160,7 +144,8 @@ void Squawk::onNewConference() { jc->exec(); } -void Squawk::onNewContactAccepted() { +void Squawk::onNewContactAccepted() +{ NewContact* nc = static_cast(sender()); NewContact::Data value = nc->value(); @@ -169,7 +154,8 @@ void Squawk::onNewContactAccepted() { nc->deleteLater(); } -void Squawk::onJoinConferenceAccepted() { +void Squawk::onJoinConferenceAccepted() +{ JoinConference* jc = static_cast(sender()); JoinConference::Data value = jc->value(); @@ -178,91 +164,360 @@ void Squawk::onJoinConferenceAccepted() { jc->deleteLater(); } -void Squawk::closeEvent(QCloseEvent* event) { - if (accounts != nullptr) +void Squawk::closeEvent(QCloseEvent* event) +{ + if (accounts != 0) { accounts->close(); - - if (preferences != nullptr) - preferences->close(); - - if (about != nullptr) - about->close(); - - - for (const std::pair& pair : infoWidgets) { - disconnect(pair.second, &UI::Info::destroyed, this, &Squawk::onInfoClosed); - pair.second->close(); } - infoWidgets.clear(); - writeSettings(); - emit closing();; - + + for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { + disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed); + itr->second->close(); + } + conversations.clear(); + + for (std::map::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) { + disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed); + itr->second->close(); + } + vCards.clear(); + QMainWindow::closeEvent(event); } -void Squawk::onAccountsClosed() { - accounts = nullptr;} -void Squawk::onPreferencesClosed() { - preferences = nullptr;} - -void Squawk::onAboutSquawkClosed() { - about = nullptr;} - -void Squawk::onComboboxActivated(int index) { - Shared::Availability av = Shared::Global::fromInt(index); - emit changeState(av); +void Squawk::onAccountsClosed(QObject* parent) +{ + accounts = 0; } -void Squawk::expand(const QModelIndex& index) { - m_ui->roster->expand(index);} +void Squawk::newAccount(const QMap& account) +{ + rosterModel.addAccount(account); +} -void Squawk::stateChanged(Shared::Availability state) { - m_ui->comboBox->setCurrentIndex(static_cast(state));} +void Squawk::onComboboxActivated(int index) +{ + Shared::Availability av = Shared::Global::fromInt(index); + if (av != Shared::Availability::offline) { + int size = rosterModel.accountsModel->rowCount(QModelIndex()); + if (size > 0) { + emit changeState(av); + for (int i = 0; i < size; ++i) { + Models::Account* acc = rosterModel.accountsModel->getAccount(i); + if (acc->getState() == Shared::ConnectionState::disconnected) { + emit connectAccount(acc->getName()); + } + } + } else { + m_ui->comboBox->setCurrentIndex(static_cast(Shared::Availability::offline)); + } + } else { + emit changeState(av); + int size = rosterModel.accountsModel->rowCount(QModelIndex()); + for (int i = 0; i != size; ++i) { + Models::Account* acc = rosterModel.accountsModel->getAccount(i); + if (acc->getState() != Shared::ConnectionState::disconnected) { + emit disconnectAccount(acc->getName()); + } + } + } +} -void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) { +void Squawk::changeAccount(const QString& account, const QMap& data) +{ + for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { + QString attr = itr.key(); + rosterModel.updateAccount(account, attr, *itr); + } +} + +void Squawk::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) +{ + rosterModel.addContact(account, jid, group, data); + + QSettings settings; + settings.beginGroup("ui"); + settings.beginGroup("roster"); + settings.beginGroup(account); + if (settings.value("expanded", false).toBool()) { + QModelIndex ind = rosterModel.getAccountIndex(account); + m_ui->roster->expand(ind); + } + settings.endGroup(); + settings.endGroup(); + settings.endGroup(); +} + +void Squawk::addGroup(const QString& account, const QString& name) +{ + rosterModel.addGroup(account, name); + + QSettings settings; + settings.beginGroup("ui"); + settings.beginGroup("roster"); + settings.beginGroup(account); + if (settings.value("expanded", false).toBool()) { + QModelIndex ind = rosterModel.getAccountIndex(account); + m_ui->roster->expand(ind); + if (settings.value(name + "/expanded", false).toBool()) { + m_ui->roster->expand(rosterModel.getGroupIndex(account, name)); + } + } + settings.endGroup(); + settings.endGroup(); + settings.endGroup(); +} + +void Squawk::removeGroup(const QString& account, const QString& name) +{ + rosterModel.removeGroup(account, name); +} + +void Squawk::changeContact(const QString& account, const QString& jid, const QMap& data) +{ + rosterModel.changeContact(account, jid, data); +} + +void Squawk::removeContact(const QString& account, const QString& jid) +{ + rosterModel.removeContact(account, jid); +} + +void Squawk::removeContact(const QString& account, const QString& jid, const QString& group) +{ + rosterModel.removeContact(account, jid, group); +} + +void Squawk::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + rosterModel.addPresence(account, jid, name, data); +} + +void Squawk::removePresence(const QString& account, const QString& jid, const QString& name) +{ + rosterModel.removePresence(account, jid, name); +} + +void Squawk::stateChanged(Shared::Availability state) +{ + m_ui->comboBox->setCurrentIndex(static_cast(state)); +} + +void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) +{ if (item.isValid()) { Models::Item* node = static_cast(item.internalPointer()); - if (node->type == Models::Item::reference) + if (node->type == Models::Item::reference) { node = static_cast(node)->dereference(); - - Models::Contact* contact = nullptr; - Models::Room* room = nullptr; + } + Models::Contact* contact = 0; + Models::Room* room = 0; + QString res; + Models::Roster::ElId* id = 0; switch (node->type) { case Models::Item::contact: contact = static_cast(node); - emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid())); + id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); break; case Models::Item::presence: contact = static_cast(node->parentItem()); - emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()), node->getName()); + id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); + res = node->getName(); break; case Models::Item::room: room = static_cast(node); - emit openConversation(Models::Roster::ElId(room->getAccountName(), room->getJid())); + id = new Models::Roster::ElId(room->getAccountName(), room->getJid()); break; default: m_ui->roster->expand(item); break; } + + if (id != 0) { + Conversations::const_iterator itr = conversations.find(*id); + Models::Account* acc = rosterModel.getAccount(id->account); + Conversation* conv = 0; + bool created = false; + if (itr != conversations.end()) { + conv = itr->second; + } else if (contact != 0) { + created = true; + conv = new Chat(acc, contact); + } else if (room != 0) { + created = true; + conv = new Room(acc, room); + + if (!room->getJoined()) { + emit setRoomJoined(id->account, id->name, true); + } + } + + if (conv != 0) { + if (created) { + conv->setAttribute(Qt::WA_DeleteOnClose); + subscribeConversation(conv); + conversations.insert(std::make_pair(*id, conv)); + } + + conv->show(); + conv->raise(); + conv->activateWindow(); + + if (res.size() > 0) { + conv->setPalResource(res); + } + } + + delete id; + } } } -void Squawk::closeCurrentConversation() { - if (currentConversation != nullptr) { +void Squawk::onConversationClosed(QObject* parent) +{ + Conversation* conv = static_cast(sender()); + Models::Roster::ElId id(conv->getAccount(), conv->getJid()); + Conversations::const_iterator itr = conversations.find(id); + if (itr != conversations.end()) { + conversations.erase(itr); + } + if (conv->isMuc) { + Room* room = static_cast(conv); + if (!room->autoJoined()) { + emit setRoomJoined(id.account, id.name, false); + } + } +} + +void Squawk::fileProgress(const std::list msgs, qreal value, bool up) +{ + rosterModel.fileProgress(msgs, value, up); +} + +void Squawk::fileDownloadComplete(const std::list msgs, const QString& path) +{ + rosterModel.fileComplete(msgs, false); +} + +void Squawk::fileError(const std::list msgs, const QString& error, bool up) +{ + rosterModel.fileError(msgs, error, up); +} + +void Squawk::fileUploadComplete(const std::list msgs, const QString& path) +{ + rosterModel.fileComplete(msgs, true); +} + +void Squawk::accountMessage(const QString& account, const Shared::Message& data) +{ + rosterModel.addMessage(account, data); +} + +void Squawk::onUnnoticedMessage(const QString& account, const Shared::Message& msg) +{ + notify(account, msg); //Telegram does this way - notifies even if the app is visible + QApplication::alert(this); +} + +void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) +{ + rosterModel.changeMessage(account, jid, id, data); +} + +void Squawk::notify(const QString& account, const Shared::Message& msg) +{ + QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid())); + QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); + QVariantList args; + args << QString(QCoreApplication::applicationName()); + args << QVariant(QVariant::UInt); //TODO some normal id + if (path.size() > 0) { + args << path; + } else { + args << QString("mail-message"); //TODO should here better be unknown user icon? + } + if (msg.getType() == Shared::Message::groupChat) { + args << msg.getFromResource() + " from " + name; + } else { + args << name; + } + + QString body(msg.getBody()); + QString oob(msg.getOutOfBandUrl()); + if (body == oob) { + body = tr("Attached file"); + } + + args << body; + args << QStringList(); + args << QVariantMap(); + args << 3000; + dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args); +} + +void Squawk::onConversationMessage(const Shared::Message& msg) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + + rosterModel.addMessage(acc, msg); + emit sendMessage(acc, msg); +} + +void Squawk::onConversationResend(const QString& id) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + QString jid = conv->getJid(); + + emit resendMessage(acc, jid, id); +} + +void Squawk::onRequestArchive(const QString& account, const QString& jid, const QString& before) +{ + emit requestArchive(account, jid, 20, before); //TODO amount as a settings value +} + +void Squawk::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) +{ + rosterModel.responseArchive(account, jid, list, last); +} + +void Squawk::removeAccount(const QString& account) +{ + Conversations::const_iterator itr = conversations.begin(); + while (itr != conversations.end()) { + if (itr->first.account == account) { + Conversations::const_iterator lItr = itr; + ++itr; + Conversation* conv = lItr->second; + disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); + conv->close(); + conversations.erase(lItr); + } else { + ++itr; + } + } + + if (currentConversation != 0 && currentConversation->getAccount() == account) { currentConversation->deleteLater(); - currentConversation = nullptr; + currentConversation = 0; m_ui->filler->show(); } + + rosterModel.removeAccount(account); } -void Squawk::onRosterContextMenu(const QPoint& point) { +void Squawk::onRosterContextMenu(const QPoint& point) +{ QModelIndex index = m_ui->roster->indexAt(point); if (index.isValid()) { Models::Item* item = static_cast(index.internalPointer()); - if (item->type == Models::Item::reference) + if (item->type == Models::Item::reference) { item = static_cast(item)->dereference(); - + } contextMenu->clear(); bool hasMenu = false; bool active = item->getAccountConnectionState() == Shared::ConnectionState::connected; @@ -272,31 +527,40 @@ void Squawk::onRosterContextMenu(const QPoint& point) { hasMenu = true; QString name = acc->getName(); - if (acc->getActive()) { - QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Deactivate")); - connect(con, &QAction::triggered, std::bind(&Squawk::disconnectAccount, this, name)); + if (acc->getState() != Shared::ConnectionState::disconnected) { + QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Disconnect")); + con->setEnabled(active); + connect(con, &QAction::triggered, [this, name]() { + emit disconnectAccount(name); + }); } else { - QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Activate")); - connect(con, &QAction::triggered, std::bind(&Squawk::connectAccount, this, name)); + QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Connect")); + connect(con, &QAction::triggered, [this, name]() { + emit connectAccount(name); + }); } - QAction* info = contextMenu->addAction(Shared::icon("user-properties"), tr("Info")); - info->setEnabled(active); - connect(info, &QAction::triggered, std::bind(&Squawk::onActivateInfo, this, name, acc->getBareJid())); + QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); + card->setEnabled(active); + connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); - connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccountRequest, this, name)); + remove->setEnabled(active); + connect(remove, &QAction::triggered, [this, name]() { + emit removeAccount(name); + }); + } break; case Models::Item::contact: { Models::Contact* cnt = static_cast(item); - Models::Roster::ElId id(cnt->getAccountName(), cnt->getJid()); - QString cntName = cnt->getName(); hasMenu = true; QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog")); dialog->setEnabled(active); - connect(dialog, &QAction::triggered, std::bind(&Squawk::onRosterItemDoubleClicked, this, index)); + connect(dialog, &QAction::triggered, [this, index]() { + onRosterItemDoubleClicked(index); + }); Shared::SubscriptionState state = cnt->getState(); switch (state) { @@ -304,7 +568,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) { case Shared::SubscriptionState::to: { QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); unsub->setEnabled(active); - connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false)); + connect(unsub, &QAction::triggered, [this, cnt]() { + emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), ""); + }); } break; case Shared::SubscriptionState::from: @@ -312,67 +578,75 @@ void Squawk::onRosterContextMenu(const QPoint& point) { case Shared::SubscriptionState::none: { QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); sub->setEnabled(active); - connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true)); + connect(sub, &QAction::triggered, [this, cnt]() { + emit subscribeContact(cnt->getAccountName(), cnt->getJid(), ""); + }); } } + QString accName = cnt->getAccountName(); + QString cntJID = cnt->getJid(); + QString cntName = cnt->getName(); QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename")); rename->setEnabled(active); - connect(rename, &QAction::triggered, [this, cntName, id]() { + connect(rename, &QAction::triggered, [this, cntName, accName, cntJID]() { QInputDialog* dialog = new QInputDialog(this); - connect(dialog, &QDialog::accepted, [this, dialog, cntName, id]() { + connect(dialog, &QDialog::accepted, [this, dialog, cntName, accName, cntJID]() { QString newName = dialog->textValue(); - if (newName != cntName) - emit renameContactRequest(id.account, id.name, newName); - + if (newName != cntName) { + emit renameContactRequest(accName, cntJID, newName); + } dialog->deleteLater(); }); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); dialog->setInputMode(QInputDialog::TextInput); - dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(id.name)); - dialog->setWindowTitle(tr("Renaming %1").arg(id.name)); + dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(cntJID)); + dialog->setWindowTitle(tr("Renaming %1").arg(cntJID)); dialog->setTextValue(cntName); dialog->exec(); }); QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups")); - std::deque groupList = rosterModel.groupList(id.account); + std::deque groupList = rosterModel.groupList(accName); for (QString groupName : groupList) { QAction* gr = groupsMenu->addAction(groupName); gr->setCheckable(true); - gr->setChecked(rosterModel.groupHasContact(id.account, groupName, id.name)); + gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID)); gr->setEnabled(active); - connect(gr, &QAction::toggled, [this, groupName, id](bool checked) { - if (checked) - emit addContactToGroupRequest(id.account, id.name, groupName); - else - emit removeContactFromGroupRequest(id.account, id.name, groupName); + connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) { + if (checked) { + emit addContactToGroupRequest(accName, cntJID, groupName); + } else { + emit removeContactFromGroupRequest(accName, cntJID, groupName); + } }); } QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group")); newGroup->setEnabled(active); - connect(newGroup, &QAction::triggered, [this, id]() { + connect(newGroup, &QAction::triggered, [this, accName, cntJID]() { QInputDialog* dialog = new QInputDialog(this); - connect(dialog, &QDialog::accepted, [this, dialog, id]() { - emit addContactToGroupRequest(id.account, id.name, dialog->textValue()); + connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() { + emit addContactToGroupRequest(accName, cntJID, dialog->textValue()); dialog->deleteLater(); }); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(tr("New group name")); - dialog->setWindowTitle(tr("Add %1 to a new group").arg(id.name)); + dialog->setWindowTitle(tr("Add %1 to a new group").arg(cntJID)); dialog->exec(); }); - QAction* info = contextMenu->addAction(Shared::icon("user-properties"), tr("Info")); - info->setEnabled(active); - connect(info, &QAction::triggered, std::bind(&Squawk::onActivateInfo, this, id.account, id.name)); + QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); + card->setEnabled(active); + connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); - connect(remove, &QAction::triggered, std::bind(&Squawk::removeContactRequest, this, id.account, id.name)); + connect(remove, &QAction::triggered, [this, cnt]() { + emit removeContactRequest(cnt->getAccountName(), cnt->getJid()); + }); } break; @@ -391,88 +665,270 @@ void Squawk::onRosterContextMenu(const QPoint& point) { if (room->getAutoJoin()) { QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); unsub->setEnabled(active); - connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false)); + connect(unsub, &QAction::triggered, [this, id]() { + emit setRoomAutoJoin(id.account, id.name, false); + if (conversations.find(id) == conversations.end() + && (currentConversation == 0 || currentConversation->getId() != id) + ) { //to leave the room if it's not opened in a conversation window + emit setRoomJoined(id.account, id.name, false); + } + }); } else { - QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); - sub->setEnabled(active); - connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true)); + QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); + unsub->setEnabled(active); + connect(unsub, &QAction::triggered, [this, id]() { + emit setRoomAutoJoin(id.account, id.name, true); + if (conversations.find(id) == conversations.end() + && (currentConversation == 0 || currentConversation->getId() != id) + ) { //to join the room if it's not already joined + emit setRoomJoined(id.account, id.name, true); + } + }); } QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); - connect(remove, &QAction::triggered, std::bind(&Squawk::removeRoomRequest, this, id.account, id.name)); + connect(remove, &QAction::triggered, [this, id]() { + emit removeRoomRequest(id.account, id.name); + }); } break; default: break; } - if (hasMenu) + if (hasMenu) { contextMenu->popup(m_ui->roster->viewport()->mapToGlobal(point)); + } } } -void Squawk::responseInfo(const Shared::Info& info) { - std::map::const_iterator itr = infoWidgets.find(info.getAddressRef()); - if (itr != infoWidgets.end()) { - itr->second->setData(info); +void Squawk::addRoom(const QString& account, const QString jid, const QMap& data) +{ + rosterModel.addRoom(account, jid, data); +} + +void Squawk::changeRoom(const QString& account, const QString jid, const QMap& data) +{ + rosterModel.changeRoom(account, jid, data); +} + +void Squawk::removeRoom(const QString& account, const QString jid) +{ + rosterModel.removeRoom(account, jid); +} + +void Squawk::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + rosterModel.addRoomParticipant(account, jid, name, data); +} + +void Squawk::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + rosterModel.changeRoomParticipant(account, jid, name, data); +} + +void Squawk::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) +{ + rosterModel.removeRoomParticipant(account, jid, name); +} + +void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) +{ + std::map::const_iterator itr = vCards.find(jid); + if (itr != vCards.end()) { + itr->second->setVCard(card); itr->second->hideProgress(); } } -void Squawk::onInfoClosed() { - UI::Info* info = static_cast(sender()); +void Squawk::onVCardClosed() +{ + VCard* vCard = static_cast(sender()); - std::map::const_iterator itr = infoWidgets.find(info->getJid()); - if (itr == infoWidgets.end()) { - qDebug() << "Info widget has been closed but can not be found among other opened vCards, application is most probably going to crash"; + std::map::const_iterator itr = vCards.find(vCard->getJid()); + if (itr == vCards.end()) { + qDebug() << "VCard has been closed but can not be found among other opened vCards, application is most probably going to crash"; return; } - infoWidgets.erase(itr); + vCards.erase(itr); } -void Squawk::onActivateInfo(const QString& account, const QString& jid) { - std::map::const_iterator itr = infoWidgets.find(jid); - UI::Info* info; - if (itr != infoWidgets.end()) { - info = itr->second; +void Squawk::onActivateVCard(const QString& account, const QString& jid, bool edition) +{ + std::map::const_iterator itr = vCards.find(jid); + VCard* card; + if (itr != vCards.end()) { + card = itr->second; } else { - info = new UI::Info(jid); - info->setWindowTitle(tr("Information about %1").arg(jid)); - info->setAttribute(Qt::WA_DeleteOnClose); - infoWidgets.insert(std::make_pair(jid, info)); + card = new VCard(jid, edition); + if (edition) { + card->setWindowTitle(tr("%1 account card").arg(account)); + } else { + card->setWindowTitle(tr("%1 contact card").arg(jid)); + } + card->setAttribute(Qt::WA_DeleteOnClose); + vCards.insert(std::make_pair(jid, card)); - connect(info, &UI::Info::destroyed, this, &Squawk::onInfoClosed); - connect(info, &UI::Info::saveInfo, std::bind(&Squawk::onInfoSave, this, std::placeholders::_1, account)); + connect(card, &VCard::destroyed, this, &Squawk::onVCardClosed); + connect(card, &VCard::saveVCard, std::bind( &Squawk::onVCardSave, this, std::placeholders::_1, account)); } - info->show(); - info->raise(); - info->activateWindow(); - info->showProgress(); + card->show(); + card->raise(); + card->activateWindow(); + card->showProgress(tr("Downloading vCard")); - emit requestInfo(account, jid); + emit requestVCard(account, jid); } -void Squawk::onInfoSave(const Shared::Info& info, const QString& account) { - UI::Info* widget = static_cast(sender()); - emit updateInfo(account, info); +void Squawk::onVCardSave(const Shared::VCard& card, const QString& account) +{ + VCard* widget = static_cast(sender()); + emit uploadVCard(account, card); widget->deleteLater(); } -void Squawk::writeSettings() { +void Squawk::readSettings() +{ QSettings settings; settings.beginGroup("ui"); - settings.beginGroup("window"); - settings.setValue("geometry", saveGeometry()); - settings.setValue("state", saveState()); - settings.endGroup(); - settings.setValue("splitter", m_ui->splitter->saveState()); + if (settings.contains("availability")) { + int avail = settings.value("availability").toInt(); + m_ui->comboBox->setCurrentIndex(avail); + emit stateChanged(Shared::Global::fromInt(avail)); + + int size = settings.beginReadArray("connectedAccounts"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + emit connectAccount(settings.value("name").toString()); //TODO this is actually not needed, stateChanged event already connects everything you have + } // need to fix that + settings.endArray(); + } settings.endGroup(); } -void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { +void Squawk::writeSettings() +{ + QSettings settings; + settings.beginGroup("ui"); + settings.beginGroup("window"); + settings.setValue("geometry", saveGeometry()); + settings.setValue("state", saveState()); + settings.endGroup(); + + settings.setValue("splitter", m_ui->splitter->saveState()); + + settings.setValue("availability", m_ui->comboBox->currentIndex()); + settings.beginWriteArray("connectedAccounts"); + int size = rosterModel.accountsModel->rowCount(QModelIndex()); + for (int i = 0; i < size; ++i) { + Models::Account* acc = rosterModel.accountsModel->getAccount(i); + if (acc->getState() != Shared::ConnectionState::disconnected) { + settings.setArrayIndex(i); + settings.setValue("name", acc->getName()); + } + } + settings.endArray(); + + settings.remove("roster"); + settings.beginGroup("roster"); + for (int i = 0; i < size; ++i) { + QModelIndex acc = rosterModel.index(i, 0, QModelIndex()); + Models::Account* account = rosterModel.accountsModel->getAccount(i); + QString accName = account->getName(); + settings.beginGroup(accName); + + settings.setValue("expanded", m_ui->roster->isExpanded(acc)); + std::deque groups = rosterModel.groupList(accName); + for (const QString& groupName : groups) { + settings.beginGroup(groupName); + QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName); + settings.setValue("expanded", m_ui->roster->isExpanded(gIndex)); + settings.endGroup(); + } + + settings.endGroup(); + } + settings.endGroup(); + settings.endGroup(); +} + +void Squawk::onItemCollepsed(const QModelIndex& index) +{ + QSettings settings; + Models::Item* item = static_cast(index.internalPointer()); + switch (item->type) { + case Models::Item::account: + settings.setValue("ui/roster/" + item->getName() + "/expanded", false); + break; + case Models::Item::group: { + QModelIndex accInd = rosterModel.parent(index); + Models::Account* account = rosterModel.accountsModel->getAccount(accInd.row()); + settings.setValue("ui/roster/" + account->getName() + "/" + item->getName() + "/expanded", false); + } + break; + default: + break; + } +} + +void Squawk::requestPassword(const QString& account) +{ + requestedAccountsForPasswords.push_back(account); + checkNextAccountForPassword(); +} + +void Squawk::checkNextAccountForPassword() +{ + if (prompt == 0 && requestedAccountsForPasswords.size() > 0) { + prompt = new QInputDialog(this); + QString accName = requestedAccountsForPasswords.front(); + connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted); + connect(prompt, &QDialog::rejected, this, &Squawk::onPasswordPromptRejected); + prompt->setInputMode(QInputDialog::TextInput); + prompt->setTextEchoMode(QLineEdit::Password); + prompt->setLabelText(tr("Input the password for account %1").arg(accName)); + prompt->setWindowTitle(tr("Password for account %1").arg(accName)); + prompt->setTextValue(""); + prompt->exec(); + } +} + +void Squawk::onPasswordPromptAccepted() +{ + emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue()); + onPasswordPromptDone(); +} + +void Squawk::onPasswordPromptDone() +{ + prompt->deleteLater(); + prompt = 0; + requestedAccountsForPasswords.pop_front(); + checkNextAccountForPassword(); +} + +void Squawk::onPasswordPromptRejected() +{ + //for now it's the same on reject and on accept, but one day I'm gonna make + //"Asking for the password again on the authentication failure" feature + //and here I'll be able to break the circle of password requests + emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue()); + onPasswordPromptDone(); +} + +void Squawk::subscribeConversation(Conversation* conv) +{ + connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); + connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage); + connect(conv, &Conversation::resendMessage, this, &Squawk::onConversationResend); + connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify); +} + +void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +{ if (restoreSelection.isValid() && restoreSelection == current) { restoreSelection = QModelIndex(); return; @@ -480,13 +936,13 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn if (current.isValid()) { Models::Item* node = static_cast(current.internalPointer()); - if (node->type == Models::Item::reference) + if (node->type == Models::Item::reference) { node = static_cast(node)->dereference(); - - Models::Contact* contact = nullptr; - Models::Room* room = nullptr; + } + Models::Contact* contact = 0; + Models::Room* room = 0; QString res; - Models::Roster::ElId* id = nullptr; + Models::Roster::ElId* id = 0; bool hasContext = true; switch (node->type) { case Models::Item::contact: @@ -515,20 +971,20 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) { - if (id != nullptr) + if (id != 0) { delete id; - + } needToRestore = true; restoreSelection = previous; return; } - if (id != nullptr) { - if (currentConversation != nullptr) { + if (id != 0) { + if (currentConversation != 0) { if (currentConversation->getId() == *id) { - if (contact != nullptr) + if (contact != 0) { currentConversation->setPalResource(res); - + } return; } else { currentConversation->deleteLater(); @@ -538,58 +994,48 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } Models::Account* acc = rosterModel.getAccount(id->account); - if (contact != nullptr) + if (contact != 0) { currentConversation = new Chat(acc, contact); - else if (room != nullptr) + } else if (room != 0) { currentConversation = new Room(acc, room); - - if (!testAttribute(Qt::WA_TranslucentBackground)) + + if (!room->getJoined()) { + emit setRoomJoined(id->account, id->name, true); + } + } + if (!testAttribute(Qt::WA_TranslucentBackground)) { currentConversation->setFeedFrames(true, false, true, true); - + } - emit openedConversation(); + subscribeConversation(currentConversation); - if (res.size() > 0) + if (res.size() > 0) { currentConversation->setPalResource(res); + } m_ui->splitter->insertWidget(1, currentConversation); delete id; } else { - closeCurrentConversation(); + if (currentConversation != 0) { + currentConversation->deleteLater(); + currentConversation = 0; + m_ui->filler->show(); + } } } else { - closeCurrentConversation(); + if (currentConversation != 0) { + currentConversation->deleteLater(); + currentConversation = 0; + m_ui->filler->show(); + } } } -void Squawk::onContextAboutToHide() { +void Squawk::onContextAboutToHide() +{ if (needToRestore) { needToRestore = false; m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect); } } - -void Squawk::onAboutSquawkCalled() { - if (about == nullptr) { - about = new About(); - about->setAttribute(Qt::WA_DeleteOnClose); - connect(about, &Settings::destroyed, this, &Squawk::onAboutSquawkClosed); - } else { - about->raise(); - about->activateWindow(); - } - about->show(); -} - -Models::Roster::ElId Squawk::currentConversationId() const { - if (currentConversation == nullptr) - return Models::Roster::ElId(); - else - return Models::Roster::ElId(currentConversation->getAccount(), currentConversation->getJid()); -} - -void Squawk::select(QModelIndex index) { - m_ui->roster->scrollTo(index, QAbstractItemView::EnsureVisible); - m_ui->roster->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); -} diff --git a/ui/squawk.h b/ui/squawk.h index 70eae8e..28389fa 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include #include @@ -31,109 +31,139 @@ #include #include -#include "widgets/accounts/accounts.h" +#include "widgets/accounts.h" #include "widgets/chat.h" #include "widgets/room.h" #include "widgets/newcontact.h" #include "widgets/joinconference.h" #include "models/roster.h" -#include "widgets/info/info.h" -#include "widgets/settings/settings.h" -#include "widgets/about.h" +#include "widgets/vcard/vcard.h" #include "shared/shared.h" -#include "shared/global.h" -#include "shared/info.h" namespace Ui { class Squawk; } -class Application; - -class Squawk : public QMainWindow { +class Squawk : public QMainWindow +{ Q_OBJECT - friend class Application; + public: - explicit Squawk(Models::Roster& rosterModel, QWidget *parent = nullptr); + explicit Squawk(QWidget *parent = nullptr); ~Squawk() override; + void writeSettings(); + signals: - void closing(); - void quit(); void newAccountRequest(const QMap&); + void modifyAccountRequest(const QString&, const QMap&); void removeAccountRequest(const QString&); void connectAccount(const QString&); void disconnectAccount(const QString&); void changeState(Shared::Availability state); + void sendMessage(const QString& account, const Shared::Message& data); + void resendMessage(const QString& account, const QString& jid, const QString& id); + void requestArchive(const QString& account, const QString& jid, int count, const QString& before); + void subscribeContact(const QString& account, const QString& jid, const QString& reason); + void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); void removeContactRequest(const QString& account, const QString& jid); void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups); void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName); void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName); void renameContactRequest(const QString& account, const QString& jid, const QString& newName); + void setRoomJoined(const QString& account, const QString& jid, bool joined); + void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin); void removeRoomRequest(const QString& account, const QString& jid); - void requestInfo(const QString& account, const QString& jid); - void updateInfo(const QString& account, const Shared::Info& info); - void changeDownloadsPath(const QString& path); - void changeTray(bool enabled, bool hide); - - void notify(const QString& account, const Shared::Message& msg); - void changeSubscription(const Models::Roster::ElId& id, bool subscribe); - void openedConversation(); - void openConversation(const Models::Roster::ElId& id, const QString& resource = ""); - - void modifyAccountRequest(const QString&, const QMap&); - void itemExpanded (const QModelIndex& index); - void itemCollapsed (const QModelIndex& index); + void fileDownloadRequest(const QString& url); + void requestVCard(const QString& account, const QString& jid); + void uploadVCard(const QString& account, const Shared::VCard& card); + void responsePassword(const QString& account, const QString& password); + void localPathInvalid(const QString& path); -public: - Models::Roster::ElId currentConversationId() const; - void closeCurrentConversation(); - public slots: - void writeSettings(); + void readSettings(); + void newAccount(const QMap& account); + void changeAccount(const QString& account, const QMap& data); + void removeAccount(const QString& account); + void addGroup(const QString& account, const QString& name); + void removeGroup(const QString& account, const QString& name); + void addContact(const QString& account, const QString& jid, const QString& group, const QMap& data); + void removeContact(const QString& account, const QString& jid, const QString& group); + void removeContact(const QString& account, const QString& jid); + void changeContact(const QString& account, const QString& jid, const QMap& data); + void addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data); + void removePresence(const QString& account, const QString& jid, const QString& name); void stateChanged(Shared::Availability state); - void responseInfo(const Shared::Info& info); - void select(QModelIndex index); + void accountMessage(const QString& account, const Shared::Message& data); + void responseArchive(const QString& account, const QString& jid, const std::list& list, bool last); + void addRoom(const QString& account, const QString jid, const QMap& data); + void changeRoom(const QString& account, const QString jid, const QMap& data); + void removeRoom(const QString& account, const QString jid); + void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); + void fileError(const std::list msgs, const QString& error, bool up); + void fileProgress(const std::list msgs, qreal value, bool up); + void fileDownloadComplete(const std::list msgs, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& path); + void responseVCard(const QString& jid, const Shared::VCard& card); + void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); + void requestPassword(const QString& account); private: + typedef std::map Conversations; QScopedPointer m_ui; Accounts* accounts; - Settings* preferences; - About* about; - Models::Roster& rosterModel; + Models::Roster rosterModel; + Conversations conversations; QMenu* contextMenu; - std::map infoWidgets; + QDBusInterface dbus; + std::map vCards; + std::deque requestedAccountsForPasswords; + QInputDialog* prompt; Conversation* currentConversation; QModelIndex restoreSelection; bool needToRestore; protected: void closeEvent(QCloseEvent * event) override; - void expand(const QModelIndex& index); + +protected slots: + void notify(const QString& account, const Shared::Message& msg); private slots: void onAccounts(); - void onPreferences(); void onNewContact(); void onNewConference(); void onNewContactAccepted(); void onJoinConferenceAccepted(); - void onAccountsChanged(); - void onAccountsClosed(); - void onPreferencesClosed(); - void onInfoClosed(); - void onInfoSave(const Shared::Info& info, const QString& account); - void onActivateInfo(const QString& account, const QString& jid); + void onAccountsSizeChanged(unsigned int size); + void onAccountsClosed(QObject* parent = 0); + void onConversationClosed(QObject* parent = 0); + void onVCardClosed(); + void onVCardSave(const Shared::VCard& card, const QString& account); + void onActivateVCard(const QString& account, const QString& jid, bool edition = false); void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); + void onConversationMessage(const Shared::Message& msg); + void onConversationResend(const QString& id); + void onRequestArchive(const QString& account, const QString& jid, const QString& before); void onRosterContextMenu(const QPoint& point); + void onItemCollepsed(const QModelIndex& index); + void onPasswordPromptAccepted(); + void onPasswordPromptRejected(); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); - void onAboutSquawkCalled(); - void onAboutSquawkClosed(); + + void onUnnoticedMessage(const QString& account, const Shared::Message& msg); + +private: + void checkNextAccountForPassword(); + void onPasswordPromptDone(); + void subscribeConversation(Conversation* conv); }; #endif // SQUAWK_H diff --git a/ui/squawk.ui b/ui/squawk.ui index a8b0730..f6cb300 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -73,9 +73,6 @@ 0 - - 0 - @@ -99,31 +96,16 @@ true - - true - false false - - 10 - - - false - - - - 0 - 30 - - false @@ -133,9 +115,6 @@ -1 - - true - @@ -183,7 +162,7 @@ 0 0 718 - 32 + 27 @@ -191,7 +170,6 @@ Settings - @@ -201,20 +179,13 @@ - - - Help - - - - - - :/images/fallback/dark/big/group.svg:/images/fallback/dark/big/group.svg + + .. Accounts @@ -222,8 +193,8 @@ - - :/images/fallback/dark/big/edit-none.svg:/images/fallback/dark/big/edit-none.svg + + .. Quit @@ -234,8 +205,8 @@ false - - :/images/fallback/dark/big/add.svg:/images/fallback/dark/big/add.svg + + .. Add contact @@ -246,30 +217,14 @@ false - - :/images/fallback/dark/big/group-new.svg:/images/fallback/dark/big/group-new.svg + + .. Add conference - - - - .. - - - Preferences - - - - - About Squawk - - - - - +
diff --git a/ui/utils/CMakeLists.txt b/ui/utils/CMakeLists.txt index 7e68f25..b46d30d 100644 --- a/ui/utils/CMakeLists.txt +++ b/ui/utils/CMakeLists.txt @@ -15,6 +15,4 @@ target_sources(squawk PRIVATE resizer.h shadowoverlay.cpp shadowoverlay.h - expandinglist.cpp - expandinglist.h ) diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp index d65b957..ef15bd2 100644 --- a/ui/utils/badge.cpp +++ b/ui/utils/badge.cpp @@ -18,8 +18,6 @@ #include "badge.h" -#include "shared/utils.h" - Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWidget* parent): QFrame(parent), id(p_id), @@ -28,75 +26,35 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid closeButton(new QPushButton()), layout(new QHBoxLayout(this)) { - createMandatoryComponents(); - - image->setPixmap(icon.pixmap(25, 25)); - - layout->addWidget(image); - layout->addWidget(text); - layout->addWidget(closeButton); -} - -Badge::Badge(QWidget* parent): - QFrame(parent), - id(Shared::generateUUID()), - image(nullptr), - text(nullptr), - closeButton(new QPushButton()), - layout(new QHBoxLayout(this)) -{ - createMandatoryComponents(); - - layout->addWidget(closeButton); -} - -void Badge::setIcon(const QIcon& icon) { - if (image == nullptr) { - image = new QLabel(); - image->setPixmap(icon.pixmap(25, 25)); - layout->insertWidget(0, image); - } else { - image->setPixmap(icon.pixmap(25, 25)); - } -} - -void Badge::setText(const QString& p_text) { - if (text == nullptr) { - text = new QLabel(p_text); - int index = 0; - if (image != nullptr) { - index = 1; - } - layout->insertWidget(index, text); - } else { - text->setText(p_text); - } -} - -Badge::~Badge() { - if (image != nullptr) - delete image; - - if (text != nullptr) - delete text; - - delete closeButton; -} - -void Badge::createMandatoryComponents() { setBackgroundRole(QPalette::Base); //setAutoFillBackground(true); setFrameStyle(QFrame::StyledPanel); setFrameShadow(QFrame::Raised); - - QIcon tabCloseIcon = QIcon::fromTheme("tab-close"); - if (tabCloseIcon.isNull()) - tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off); - - closeButton->setIcon(tabCloseIcon); - + + image->setPixmap(icon.pixmap(25, 25)); + closeButton->setIcon(QIcon::fromTheme("tab-close")); closeButton->setMaximumHeight(25); closeButton->setMaximumWidth(25); + + layout->addWidget(image); + layout->addWidget(text); + layout->addWidget(closeButton); + layout->setContentsMargins(2, 2, 2, 2); - connect(closeButton, &QPushButton::clicked, this, &Badge::closeClicked); + + connect(closeButton, &QPushButton::clicked, this, &Badge::close); +} + +Badge::~Badge() +{ +} + +bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const +{ + return a->id < b->id; +} + +bool Badge::Comparator::operator()(const Badge& a, const Badge& b) const +{ + return a.id < b.id; } diff --git a/ui/utils/badge.h b/ui/utils/badge.h index 074eda0..93a7f00 100644 --- a/ui/utils/badge.h +++ b/ui/utils/badge.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef BADGE_H +#define BADGE_H #include #include @@ -24,29 +25,32 @@ #include #include -class Badge : public QFrame { +/** + * @todo write docs + */ +class Badge : public QFrame +{ Q_OBJECT public: Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr); - Badge(QWidget* parent = nullptr); ~Badge(); const QString id; - -public: - void setText(const QString& text); - void setIcon(const QIcon& icon); signals: - void closeClicked(); + void close(); private: QLabel* image; QLabel* text; QPushButton* closeButton; QHBoxLayout* layout; - -private: - void createMandatoryComponents(); - + +public: + struct Comparator { + bool operator()(const Badge& a, const Badge& b) const; + bool operator()(const Badge* a, const Badge* b) const; + }; }; + +#endif // BADGE_H diff --git a/ui/utils/comboboxdelegate.cpp b/ui/utils/comboboxdelegate.cpp index cbaa1e3..4c96c79 100644 --- a/ui/utils/comboboxdelegate.cpp +++ b/ui/utils/comboboxdelegate.cpp @@ -18,30 +18,35 @@ #include "QTimer" #include "comboboxdelegate.h" -#include "shared/defines.h" ComboboxDelegate::ComboboxDelegate(QObject *parent): QStyledItemDelegate(parent), entries(), ff(new FocusFilter()) -{} +{ +} -ComboboxDelegate::~ComboboxDelegate() { + +ComboboxDelegate::~ComboboxDelegate() +{ delete ff; } -QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { - SHARED_UNUSED(option); - SHARED_UNUSED(index); + +QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ QComboBox *cb = new QComboBox(parent); - for (const std::pair& pair : entries) + + for (const std::pair& pair : entries) { cb->addItem(pair.second, pair.first); + } return cb; } -void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { +void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ QComboBox *cb = static_cast(editor); int currentIndex = index.data(Qt::EditRole).toInt(); if (currentIndex >= 0) { @@ -51,16 +56,19 @@ void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) } -void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { +void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ QComboBox *cb = static_cast(editor); model->setData(index, cb->currentIndex(), Qt::EditRole); } -void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) { +void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) +{ entries.emplace_back(title, icon); } -bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt) { +bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt) +{ if (evt->type() == QEvent::FocusIn) { QComboBox* cb = static_cast(src); cb->removeEventFilter(this); diff --git a/ui/utils/expandinglist.cpp b/ui/utils/expandinglist.cpp deleted file mode 100644 index 5cc9bad..0000000 --- a/ui/utils/expandinglist.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "expandinglist.h" - -#include - -ExpandingList::ExpandingList(QWidget* parent) : - QListView(parent), - scrollBarExtent(qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent)) -{ - setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); -} - -bool ExpandingList::hasHeightForWidth() const { -#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) - return - QAbstractItemView::sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents && - scrollBarExtent > 0; -#else - return false; -#endif -} - -int ExpandingList::heightForWidth(int width) const { - QAbstractItemModel* md = model(); - - if (md == nullptr) - return 0; - if (md->rowCount() == 0) - return 0; - - const int rowCount = md->rowCount(); - int height = 0; - for (int i = 0; i < rowCount; i++) - height += QListView::sizeHintForRow(i); - - height += frameWidth(); - int minWidth = sizeHintForColumn(0) + frameWidth() + 1; - - if (width <= minWidth) - height += scrollBarExtent + 1; - - return height; -} diff --git a/ui/utils/expandinglist.h b/ui/utils/expandinglist.h deleted file mode 100644 index 9084757..0000000 --- a/ui/utils/expandinglist.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -class ExpandingList : public QListView { - Q_OBJECT -public: - ExpandingList(QWidget* parent = nullptr); - - bool hasHeightForWidth() const override; - int heightForWidth(int width) const override; - // QSize viewportSizeHint() const override; - // QSize minimumSizeHint() const override; - -private: - int scrollBarExtent; -}; diff --git a/ui/utils/flowlayout.cpp b/ui/utils/flowlayout.cpp index 34f978a..ad7715e 100644 --- a/ui/utils/flowlayout.cpp +++ b/ui/utils/flowlayout.cpp @@ -33,78 +33,96 @@ FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing): setContentsMargins(margin, margin, margin, margin); } -FlowLayout::~FlowLayout() { +FlowLayout::~FlowLayout() +{ QLayoutItem *item; - while ((item = takeAt(0))) + while ((item = takeAt(0))) { delete item; + } } -void FlowLayout::addItem(QLayoutItem *item) { +void FlowLayout::addItem(QLayoutItem *item) +{ itemList.append(item); } -int FlowLayout::horizontalSpacing() const { - if (m_hSpace >= 0) +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { return m_hSpace; - else + } else { return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } } -int FlowLayout::verticalSpacing() const { - if (m_vSpace >= 0) +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { return m_vSpace; - else + } else { return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } } -int FlowLayout::count() const { +int FlowLayout::count() const +{ return itemList.size(); } -QLayoutItem *FlowLayout::itemAt(int index) const { +QLayoutItem *FlowLayout::itemAt(int index) const +{ return itemList.value(index); } -QLayoutItem *FlowLayout::takeAt(int index) { - if (index >= 0 && index < itemList.size()) +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) { return itemList.takeAt(index); - + } return nullptr; } -Qt::Orientations FlowLayout::expandingDirections() const { +Qt::Orientations FlowLayout::expandingDirections() const +{ return Qt::Orientations(); } -bool FlowLayout::hasHeightForWidth() const { +bool FlowLayout::hasHeightForWidth() const +{ return true; } -int FlowLayout::heightForWidth(int width) const { +int FlowLayout::heightForWidth(int width) const +{ int height = doLayout(QRect(0, 0, width, 0), true); return height; } -void FlowLayout::setGeometry(const QRect &rect) { +void FlowLayout::setGeometry(const QRect &rect) +{ QLayout::setGeometry(rect); doLayout(rect, false); } -QSize FlowLayout::sizeHint() const { +QSize FlowLayout::sizeHint() const +{ return minimumSize(); } -QSize FlowLayout::minimumSize() const { +QSize FlowLayout::minimumSize() const +{ QSize size; - for (const QLayoutItem *item : std::as_const(itemList)) + for (const QLayoutItem *item : qAsConst(itemList)) { size = size.expandedTo(item->minimumSize()); + } const QMargins margins = contentsMargins(); size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return size; } -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); @@ -112,16 +130,16 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { int y = effectiveRect.y(); int lineHeight = 0; - for (QLayoutItem *item : std::as_const(itemList)) { + for (QLayoutItem *item : qAsConst(itemList)) { const QWidget *wid = item->widget(); int spaceX = horizontalSpacing(); - if (spaceX == -1) + if (spaceX == -1) { spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - + } int spaceY = verticalSpacing(); - if (spaceY == -1) + if (spaceY == -1) { spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - + } int nextX = x + item->sizeHint().width() + spaceX; if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { x = effectiveRect.x(); @@ -130,8 +148,9 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { lineHeight = 0; } - if (!testOnly) + if (!testOnly) { item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } x = nextX; lineHeight = qMax(lineHeight, item->sizeHint().height()); @@ -139,7 +158,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { return y + lineHeight - rect.y() + bottom; } -int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const { +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ QObject *parent = this->parent(); if (!parent) { return -1; diff --git a/ui/utils/flowlayout.h b/ui/utils/flowlayout.h index 24830e7..0e52c87 100644 --- a/ui/utils/flowlayout.h +++ b/ui/utils/flowlayout.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H #include #include @@ -25,7 +26,8 @@ /** * @todo write docs */ -class FlowLayout : public QLayout { +class FlowLayout : public QLayout +{ Q_OBJECT public: explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); @@ -53,3 +55,5 @@ private: int m_hSpace; int m_vSpace; }; + +#endif // FLOWLAYOUT_H diff --git a/ui/utils/progress.cpp b/ui/utils/progress.cpp index 676376b..73e4d02 100644 --- a/ui/utils/progress.cpp +++ b/ui/utils/progress.cpp @@ -49,7 +49,7 @@ Progress::Progress(quint16 p_size, QWidget* parent): QGridLayout* layout = new QGridLayout(); setLayout(layout); - layout->setContentsMargins(0, 0, 0, 0); + layout->setMargin(0); layout->setVerticalSpacing(0); layout->setHorizontalSpacing(0); diff --git a/ui/utils/resizer.cpp b/ui/utils/resizer.cpp index ad62e6d..8691400 100644 --- a/ui/utils/resizer.cpp +++ b/ui/utils/resizer.cpp @@ -18,13 +18,14 @@ #include "resizer.h" -#include "shared/defines.h" - Resizer::Resizer(QWidget* parent): -QObject(parent) {} +QObject(parent) +{ + +} -bool Resizer::eventFilter(QObject* obj, QEvent* event) { - SHARED_UNUSED(obj); +bool Resizer::eventFilter(QObject* obj, QEvent* event) +{ if (event->type() == QEvent::Resize) { QResizeEvent* ev = static_cast(event); emit resized(ev->oldSize(), ev->size()); diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index e8d846b..c7e47e0 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -1,35 +1,24 @@ -set(SOURCE_FILES - chat.cpp - conversation.cpp - joinconference.cpp - newcontact.cpp - room.cpp - about.cpp -) - -set(UI_FILES - conversation.ui - joinconference.ui - newcontact.ui - about.ui -) - -set(HEADER_FILES - chat.h - conversation.h - joinconference.h - newcontact.h - room.h - about.h -) - target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${UI_FILES} - ${HEADER_FILES} -) + account.cpp + account.h + account.ui + accounts.cpp + accounts.h + accounts.ui + chat.cpp + chat.h + conversation.cpp + conversation.h + conversation.ui + joinconference.cpp + joinconference.h + joinconference.ui + newcontact.cpp + newcontact.h + newcontact.ui + room.cpp + room.h + ) -add_subdirectory(info) +add_subdirectory(vcard) add_subdirectory(messageline) -add_subdirectory(settings) -add_subdirectory(accounts) diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp deleted file mode 100644 index ca1bfa2..0000000 --- a/ui/widgets/about.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "about.h" -#include "ui_about.h" -#include -#include - -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 2, 0) -static const std::string _QXMPP_PATCH_(std::to_string(QXMPP_VERSION & 0xff)); -static const std::string _QXMPP_MINOR_(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); -static const std::string _QXMPP_MAJOR_(std::to_string(QXMPP_VERSION >> 16)); -static const QString SQUAWK_INNER_QXMPP_VERSION_STRING = QString::fromStdString(_QXMPP_MAJOR_ + "." + _QXMPP_MINOR_ + "." + _QXMPP_PATCH_); -#else -static const QString SQUAWK_INNER_QXMPP_VERSION_STRING = QXmppVersion(); -#endif - -About::About(QWidget* parent): - QWidget(parent), - m_ui(new Ui::About), - license(nullptr) -{ - m_ui->setupUi(this); - m_ui->versionValue->setText(QApplication::applicationVersion()); - m_ui->qtVersionValue->setText(qVersion()); - m_ui->qtBuiltAgainstVersion->setText(tr("(built against %1)").arg(QT_VERSION_STR)); - - m_ui->qxmppVersionValue->setText(QXmppVersion()); - m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(SQUAWK_INNER_QXMPP_VERSION_STRING)); - - setWindowFlag(Qt::Tool); - - connect(m_ui->licenceLink, &QLabel::linkActivated, this, &About::onLicenseActivated); -} - -About::~About() { - if (license != nullptr) { - license->deleteLater(); - } -}; - -void About::onLicenseActivated() { - if (license == nullptr) { - QFile file; - bool found = false; - QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - for (const QString& path : shares) { - file.setFileName(path + "/LICENSE.md"); - - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - found = true; - break; - } - } - if (!found) { - qDebug() << "couldn't read license file, bailing"; - return; - } - - license = new QWidget(); - license->setWindowTitle(tr("License")); - QVBoxLayout* layout = new QVBoxLayout(license); - QLabel* text = new QLabel(license); - QScrollArea* area = new QScrollArea(license); - text->setTextFormat(Qt::MarkdownText); - text->setWordWrap(true); - text->setOpenExternalLinks(true); - text->setMargin(5); - area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - - layout->addWidget(area); - license->setAttribute(Qt::WA_DeleteOnClose); - connect(license, &QWidget::destroyed, this, &About::onLicenseClosed); - - QTextStream in(&file); - QString line; - QString licenseText(""); - while (!in.atEnd()) { - line = in.readLine(); - licenseText.append(line + "\n"); - } - text->setText(licenseText); - file.close(); - - area->setWidget(text); - - } else { - license->raise(); - license->activateWindow(); - } - - license->show(); -} - -void About::onLicenseClosed() { - license = nullptr; -} diff --git a/ui/widgets/about.h b/ui/widgets/about.h deleted file mode 100644 index 35df9f4..0000000 --- a/ui/widgets/about.h +++ /dev/null @@ -1,43 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Ui{ -class About; -} - -class About : public QWidget { - Q_OBJECT -public: - About(QWidget* parent = nullptr); - ~About(); - -protected slots: - void onLicenseActivated(); - void onLicenseClosed(); - -private: - QScopedPointer m_ui; - QWidget* license; -}; diff --git a/ui/widgets/about.ui b/ui/widgets/about.ui deleted file mode 100644 index e7b9ce4..0000000 --- a/ui/widgets/about.ui +++ /dev/null @@ -1,680 +0,0 @@ - - - About - - - - 0 - 0 - 375 - 290 - - - - About Squawk - - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - - 12 - - - - Squawk - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - - - - - - 50 - 50 - - - - - - - :/images/logo.svg - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 0 - - - false - - - - About - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - XMPP (jabber) messenger - - - - - - - (c) 2019 - 2022, Yury Gubich - - - - - - - <a href="https://git.macaw.me/blue/squawk">Project site</a> - - - Qt::RichText - - - true - - - - - - - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - - - Qt::RichText - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - Components - - - - - 0 - 0 - 355 - 181 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 2 - - - 0 - - - - - - true - - - - Version - - - - - - - - true - - - - 0.0.0 - - - - - - - - true - - - - (built against 0.0.0) - - - - - - - - 75 - true - - - - Qt - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <a href="https://www.qt.io/">www.qt.io</a> - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 2 - - - 0 - - - - - - true - - - - Version - - - - - - - - true - - - - 0.0.0 - - - - - - - - true - - - - (built against 0.0.0) - - - - - - - - 75 - true - - - - QXmpp - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <a href="https://github.com/qxmpp-project/qxmpp">github.com/qxmpp-project/qxmpp</a> - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Report Bugs - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Please report any bug you find! -To report bugs you can use: - - - - - - - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - - - true - - - - - - - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - - - true - - - - - - - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - Thanks To - - - - - 0 - 0 - 355 - 181 - - - - - 10 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Vae - - - - - - - - true - - - - Major refactoring, bug fixes, constructive criticism - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Shunf4 - - - - - - - - true - - - - Major refactoring, bug fixes, build adaptations for Windows and MacOS - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Bruno F. Fontes - - - - - - - - true - - - - Brazilian Portuguese translation - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - - - - Version - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - 0.0.0 - - - - - - - - - - diff --git a/ui/widgets/accounts/account.cpp b/ui/widgets/account.cpp similarity index 87% rename from ui/widgets/accounts/account.cpp rename to ui/widgets/account.cpp index 1e2c4c7..ba3af6b 100644 --- a/ui/widgets/accounts/account.cpp +++ b/ui/widgets/account.cpp @@ -26,7 +26,6 @@ Account::Account(): m_ui->setupUi(this); connect(m_ui->passwordType, qOverload(&QComboBox::currentIndexChanged), this, &Account::onComboboxChange); - QStandardItemModel *model = static_cast(m_ui->passwordType->model()); for (int i = static_cast(Shared::AccountPasswordLowest); i < static_cast(Shared::AccountPasswordHighest) + 1; ++i) { Shared::AccountPassword ap = static_cast(i); @@ -35,14 +34,18 @@ Account::Account(): m_ui->passwordType->setCurrentIndex(static_cast(Shared::AccountPassword::plain)); if (!Shared::Global::supported("KWallet")) { + QStandardItemModel *model = static_cast(m_ui->passwordType->model()); QStandardItem *item = model->item(static_cast(Shared::AccountPassword::kwallet)); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } } -Account::~Account() {} +Account::~Account() +{ +} -QMap Account::value() const { +QMap Account::value() const +{ QMap map; map["login"] = m_ui->login->text(); map["password"] = m_ui->password->text(); @@ -50,16 +53,17 @@ QMap Account::value() const { map["name"] = m_ui->name->text(); map["resource"] = m_ui->resource->text(); map["passwordType"] = m_ui->passwordType->currentIndex(); - map["active"] = m_ui->active->isChecked(); return map; } -void Account::lockId() { +void Account::lockId() +{ m_ui->name->setReadOnly(true);; } -void Account::setData(const QMap& data) { +void Account::setData(const QMap& data) +{ m_ui->login->setText(data.value("login").toString()); m_ui->password->setText(data.value("password").toString()); m_ui->server->setText(data.value("server").toString()); @@ -68,7 +72,8 @@ void Account::setData(const QMap& data) { m_ui->passwordType->setCurrentIndex(data.value("passwordType").toInt()); } -void Account::onComboboxChange(int index) { +void Account::onComboboxChange(int index) +{ QString description = Shared::Global::getDescription(Shared::Global::fromInt(index)); m_ui->comment->setText(description); } diff --git a/ui/widgets/accounts/account.h b/ui/widgets/account.h similarity index 100% rename from ui/widgets/accounts/account.h rename to ui/widgets/account.h diff --git a/ui/widgets/accounts/account.ui b/ui/widgets/account.ui similarity index 91% rename from ui/widgets/accounts/account.ui rename to ui/widgets/account.ui index b7f9f26..a1879bc 100644 --- a/ui/widgets/accounts/account.ui +++ b/ui/widgets/account.ui @@ -7,7 +7,7 @@ 0 0 438 - 345 + 342 @@ -34,7 +34,7 @@ 6 - + Your account login @@ -44,14 +44,14 @@ - + Server - + A server address of your account. Like 404.city or macaw.me @@ -61,21 +61,21 @@ - + Login - + Password - + Password of your account @@ -97,14 +97,14 @@ - + Name - + Just a name how would you call this account, doesn't affect anything @@ -114,14 +114,14 @@ - + Resource - + A resource name like "Home" or "Work" @@ -131,17 +131,17 @@ - + Password storage - + - + @@ -157,23 +157,6 @@ - - - - Active - - - - - - - enable - - - true - - - diff --git a/ui/widgets/accounts/accounts.cpp b/ui/widgets/accounts.cpp similarity index 94% rename from ui/widgets/accounts/accounts.cpp rename to ui/widgets/accounts.cpp index 82a8ca0..7f4a135 100644 --- a/ui/widgets/accounts/accounts.cpp +++ b/ui/widgets/accounts.cpp @@ -83,8 +83,7 @@ void Accounts::onEditButton() {"server", mAcc->getServer()}, {"name", mAcc->getName()}, {"resource", mAcc->getResource()}, - {"passwordType", QVariant::fromValue(mAcc->getPasswordType())}, - {"active", mAcc->getActive()} + {"passwordType", QVariant::fromValue(mAcc->getPasswordType())} }); acc->lockId(); connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted); @@ -119,17 +118,17 @@ void Accounts::updateConnectButton() bool allConnected = true; for (int i = 0; i < selectionSize && allConnected; ++i) { const Models::Account* mAcc = model->getAccount(sm->selectedRows().at(i).row()); - allConnected = allConnected && mAcc->getActive(); + allConnected = mAcc->getState() == Shared::ConnectionState::connected; } if (allConnected) { toDisconnect = true; - m_ui->connectButton->setText(tr("Deactivate")); + m_ui->connectButton->setText(tr("Disconnect")); } else { toDisconnect = false; - m_ui->connectButton->setText(tr("Activate")); + m_ui->connectButton->setText(tr("Connect")); } } else { - m_ui->connectButton->setText(tr("Activate")); + m_ui->connectButton->setText(tr("Connect")); toDisconnect = false; m_ui->connectButton->setEnabled(false); } diff --git a/ui/widgets/accounts/accounts.h b/ui/widgets/accounts.h similarity index 98% rename from ui/widgets/accounts/accounts.h rename to ui/widgets/accounts.h index 6d5eb95..9fd0b57 100644 --- a/ui/widgets/accounts/accounts.h +++ b/ui/widgets/accounts.h @@ -24,7 +24,7 @@ #include #include "account.h" -#include "ui/models/accounts.h" +#include "../models/accounts.h" namespace Ui { diff --git a/ui/widgets/accounts/accounts.ui b/ui/widgets/accounts.ui similarity index 100% rename from ui/widgets/accounts/accounts.ui rename to ui/widgets/accounts.ui diff --git a/ui/widgets/accounts/CMakeLists.txt b/ui/widgets/accounts/CMakeLists.txt deleted file mode 100644 index 970985d..0000000 --- a/ui/widgets/accounts/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -target_sources(squawk PRIVATE - account.cpp - account.h - account.ui - accounts.cpp - accounts.h - accounts.ui - credentialsprompt.cpp - credentialsprompt.h - credentialsprompt.ui - ) diff --git a/ui/widgets/accounts/credentialsprompt.cpp b/ui/widgets/accounts/credentialsprompt.cpp deleted file mode 100644 index 3d1bafa..0000000 --- a/ui/widgets/accounts/credentialsprompt.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "credentialsprompt.h" -#include "ui_credentialsprompt.h" - -CredentialsPrompt::CredentialsPrompt(QWidget* parent): - QDialog(parent), - m_ui(new Ui::CredentialsPrompt), - title(), - message() -{ - m_ui->setupUi(this); - - title = windowTitle(); - message = m_ui->message->text(); -} - -CredentialsPrompt::~CredentialsPrompt() -{ -} - -void CredentialsPrompt::setAccount(const QString& account) -{ - m_ui->message->setText(message.arg(account)); - setWindowTitle(title.arg(account)); -} - -QString CredentialsPrompt::getLogin() const -{ - return m_ui->login->text(); -} - -QString CredentialsPrompt::getPassword() const -{ - return m_ui->password->text(); -} - -void CredentialsPrompt::setLogin(const QString& login) -{ - m_ui->login->setText(login); -} - -void CredentialsPrompt::setPassword(const QString& password) -{ - m_ui->password->setText(password); -} diff --git a/ui/widgets/accounts/credentialsprompt.h b/ui/widgets/accounts/credentialsprompt.h deleted file mode 100644 index ce9a791..0000000 --- a/ui/widgets/accounts/credentialsprompt.h +++ /dev/null @@ -1,52 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef CREDENTIALSPROMPT_H -#define CREDENTIALSPROMPT_H - -#include -#include - -namespace Ui -{ -class CredentialsPrompt; -} - -/** - * @todo write docs - */ -class CredentialsPrompt : public QDialog -{ - Q_OBJECT - -public: - CredentialsPrompt(QWidget* parent = nullptr); - ~CredentialsPrompt(); - - void setAccount(const QString& account); - void setLogin(const QString& login); - void setPassword(const QString& password); - - QString getLogin() const; - QString getPassword() const; - -private: - QScopedPointer m_ui; - QString title; - QString message; -}; - -#endif // CREDENTIALSPROMPT_H diff --git a/ui/widgets/accounts/credentialsprompt.ui b/ui/widgets/accounts/credentialsprompt.ui deleted file mode 100644 index 2ad4d8d..0000000 --- a/ui/widgets/accounts/credentialsprompt.ui +++ /dev/null @@ -1,144 +0,0 @@ - - - CredentialsPrompt - - - - 0 - 0 - 318 - 229 - - - - Authentication error: %1 - - - - - - true - - - - 0 - 0 - - - - Couldn't authenticate account %1: login or password is icorrect. -Would you like to check them and try again? - - - Qt::AlignCenter - - - true - - - - - - - - - Login - - - - - - - Your account login (without @server.domain) - - - - - - - Password - - - - - - - Your password - - - Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData - - - - - - QLineEdit::Password - - - false - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - CredentialsPrompt - accept() - - - 20 - 20 - - - 20 - 20 - - - - - buttonBox - rejected() - CredentialsPrompt - reject() - - - 20 - 20 - - - 20 - 20 - - - - - diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index b624a5d..052d83d 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -17,9 +17,6 @@ */ #include "chat.h" -#include "ui_conversation.h" - -#include "shared/defines.h" Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): Conversation(false, acc, p_contact, p_contact->getJid(), "", parent), @@ -31,16 +28,14 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): setAvatar(p_contact->getAvatarPath()); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); -#ifdef WITH_OMEMO - updateEncryptionState(); -#endif } Chat::~Chat() -{} +{ +} -void Chat::onContactChanged(Models::Item* item, int row, int col) { - SHARED_UNUSED(row); +void Chat::onContactChanged(Models::Item* item, int row, int col) +{ if (item == contact) { switch (col) { case 0: @@ -55,63 +50,35 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) { case 7: setAvatar(contact->getAvatarPath()); break; -#ifdef WITH_OMEMO - case 8: //[fallthrough] - case 9: - updateEncryptionState(); - break; -#endif } } } -void Chat::updateState() { +void Chat::updateState() +{ Shared::Availability av = contact->getAvailability(); statusIcon->setPixmap(Shared::availabilityIcon(av, true).pixmap(40)); statusIcon->setToolTip(Shared::Global::getName(av)); } -void Chat::updateEncryptionState() { - if (contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { - m_ui->encryptionButton->setVisible(true); - m_ui->encryptionButton->setEnabled(true); - if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) - m_ui->encryptionButton->setIcon(Shared::icon("lock")); - else - m_ui->encryptionButton->setIcon(Shared::icon("unlock")); - } else { - m_ui->encryptionButton->setVisible(false); - } -} - - -Shared::Message Chat::createMessage() const { +Shared::Message Chat::createMessage() const +{ Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::chat); msg.setFrom(account->getFullJid()); msg.setToJid(palJid); msg.setToResource(activePalResource); -#ifdef WITH_OMEMO - if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) - msg.setEncryption(Shared::EncryptionProtocol::omemo2); -#endif return msg; } -void Chat::onMessage(const Shared::Message& data){ +void Chat::onMessage(const Shared::Message& data) +{ Conversation::onMessage(data); if (!data.getOutgoing()) { const QString& res = data.getPenPalResource(); - if (res.size() > 0) + if (res.size() > 0) { setPalResource(res); + } } } - -void Chat::onEncryptionButtonClicked() { - m_ui->encryptionButton->setEnabled(false); - if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) - emit setEncryption(Shared::EncryptionProtocol::none); - else - emit setEncryption(Shared::EncryptionProtocol::omemo2); -} diff --git a/ui/widgets/chat.h b/ui/widgets/chat.h index 912299e..78e6bec 100644 --- a/ui/widgets/chat.h +++ b/ui/widgets/chat.h @@ -24,11 +24,12 @@ #include "shared/icons.h" #include "shared/global.h" -namespace Ui { +namespace Ui +{ class Chat; } - -class Chat : public Conversation { +class Chat : public Conversation +{ Q_OBJECT public: Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent = 0); @@ -36,7 +37,6 @@ public: protected slots: void onContactChanged(Models::Item* item, int row, int col); - void onEncryptionButtonClicked() override; protected: Shared::Message createMessage() const override; @@ -44,7 +44,6 @@ protected: private: void updateState(); - void updateEncryptionState(); private: Models::Contact* contact; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index cedcf21..d003551 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -20,27 +20,13 @@ #include "ui_conversation.h" #include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include - #include -#include - -#include "shared/icons.h" -#include "shared/utils.h" -#include "shared/pathcheck.h" -#include "shared/defines.h" - -constexpr QSize avatarSize(50, 50); +#include +#include Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent): QWidget(parent), @@ -61,73 +47,65 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, delegate(new MessageDelegate(this)), manualSliderChange(false), tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1), - pasteImageAction(new QAction(tr("Paste Image"), this)), shadow(10, 1, Qt::black, this), - contextMenu(new QMenu()), - currentAction(CurrentAction::none), - currentMessageId() + contextMenu(new QMenu()) { - createUI(); - createFeed(); - subscribeEvents(); - initializeOverlay(); -} - -Conversation::~Conversation() { - delete contextMenu; + m_ui->setupUi(this); + + shadow.setFrames(true, false, true, false); - element->feed->decrementObservers(); - delete m_ui; -} - -void Conversation::createFeed() { feed->setItemDelegate(delegate); feed->setFrameShape(QFrame::NoFrame); feed->setContextMenuPolicy(Qt::CustomContextMenu); - feed->setModel(element->feed); - element->feed->incrementObservers(); + delegate->initializeFonts(feed->getFont()); + feed->setModel(el->feed); + el->feed->incrementObservers(); m_ui->widget->layout()->addWidget(feed); - - connect(element->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage); + + connect(el->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage); connect(feed, &FeedView::resized, this, &Conversation::positionShadow); connect(feed, &FeedView::customContextMenuRequested, this, &Conversation::onFeedContext); -} - -void Conversation::createUI() { - m_ui->setupUi(this); - statusIcon = m_ui->statusIcon; - statusLabel = m_ui->statusLabel; - + + connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged); + filesLayout = new FlowLayout(m_ui->filesPanel, 0); m_ui->filesPanel->setLayout(filesLayout); - - m_ui->currentActionBadge->setVisible(false); - m_ui->encryptionButton->setVisible(false); - - shadow.setFrames(true, false, true, false); -} - -void Conversation::subscribeEvents() { - connect(account, &Models::Account::childChanged, this, &Conversation::onAccountChanged); - connect(&ker, &KeyEnterReceiver::enterPressed, this, qOverload<>(&Conversation::initiateMessageSending)); - connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); - connect(m_ui->sendButton, &QPushButton::clicked, this, qOverload<>(&Conversation::initiateMessageSending)); + + statusIcon = m_ui->statusIcon; + statusLabel = m_ui->statusLabel; + + connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); + connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); - connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); - connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, + connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); + connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &Conversation::onTextEditDocSizeChanged); - connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked); - + m_ui->messageEditor->installEventFilter(&ker); - - connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); - connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); - - connect(m_ui->currentActionBadge, &Badge::closeClicked, this, &Conversation::clear); + + + //line->setAutoFillBackground(false); + //if (testAttribute(Qt::WA_TranslucentBackground)) { + //m_ui->scrollArea->setAutoFillBackground(false); + //} else { + //m_ui->scrollArea->setBackgroundRole(QPalette::Base); + //} + + //line->setMyAvatarPath(acc->getAvatarPath()); + //line->setMyName(acc->getName()); + + initializeOverlay(); } -void Conversation::onAccountChanged(Models::Item* item, int row, int col) { - SHARED_UNUSED(row); +Conversation::~Conversation() +{ + delete contextMenu; + + element->feed->decrementObservers(); +} + +void Conversation::onAccountChanged(Models::Item* item, int row, int col) +{ if (item == account) { if (col == 2 && account->getState() == Shared::ConnectionState::connected) { //to request the history when we're back online after reconnect //if (!requestingHistory) { @@ -140,7 +118,8 @@ void Conversation::onAccountChanged(Models::Item* item, int row, int col) { } } -void Conversation::initializeOverlay() { +void Conversation::initializeOverlay() +{ QGridLayout* gr = static_cast(layout()); QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message")); gr->addWidget(overlay, 0, 0, 2, 1); @@ -163,22 +142,26 @@ void Conversation::initializeOverlay() { overlay->hide(); } -void Conversation::setName(const QString& name) { +void Conversation::setName(const QString& name) +{ m_ui->nameLabel->setText(name); setWindowTitle(name); } -QString Conversation::getAccount() const { +QString Conversation::getAccount() const +{ return account->getName(); } -QString Conversation::getJid() const { +QString Conversation::getJid() const +{ return palJid; } KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {} -bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) { +bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) +{ QEvent::Type type = event->type(); if (type == QEvent::KeyPress) { QKeyEvent* key = static_cast(event); @@ -200,74 +183,43 @@ bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) { } } } - if (k == Qt::Key_V && key->modifiers() & Qt::CTRL) { - if (Conversation::checkClipboardImage()) { - emit imagePasted(); - return true; - } - } } return QObject::eventFilter(obj, event); } -bool Conversation::checkClipboardImage() { - return !QApplication::clipboard()->image().isNull(); -} - -QString Conversation::getPalResource() const { +QString Conversation::getPalResource() const +{ return activePalResource; } -void Conversation::setPalResource(const QString& res) { +void Conversation::setPalResource(const QString& res) +{ activePalResource = res; } -void Conversation::initiateMessageSending() { +void Conversation::onEnterPressed() +{ QString body(m_ui->messageEditor->toPlainText()); if (body.size() > 0) { + m_ui->messageEditor->clear(); Shared::Message msg = createMessage(); msg.setBody(body); - initiateMessageSending(msg); + emit sendMessage(msg); } if (filesToAttach.size() > 0) { - for (const Badge* badge : filesToAttach) { + for (Badge* badge : filesToAttach) { Shared::Message msg = createMessage(); msg.setAttachPath(badge->id); element->feed->registerUpload(msg.getId()); - initiateMessageSending(msg); + emit sendMessage(msg); } - } - clear(); -} - -void Conversation::initiateMessageSending(const Shared::Message& msg) { - if (currentAction == CurrentAction::edit) { - emit replaceMessage(currentMessageId, msg); - currentAction = CurrentAction::none; - } else { - emit sendMessage(msg); + clearAttachedFiles(); } } -void Conversation::onImagePasted() { - QImage image = QApplication::clipboard()->image(); - if (image.isNull()) - return; - - QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/squawk_img_attach_XXXXXX.png"), QApplication::instance()); - tempFile->open(); - image.save(tempFile, "PNG"); - tempFile->close(); - qDebug() << "image on paste temp file: " << tempFile->fileName(); - addAttachedFile(tempFile->fileName()); - - // The file, if successfully uploaded, will be copied to Download folder. - // On application closing, this temporary file will be automatically removed by Qt. - // See Core::NetworkAccess::onUploadFinished. -} - -void Conversation::onAttach() { +void Conversation::onAttach() +{ QFileDialog* d = new QFileDialog(this, tr("Chose a file to send")); d->setFileMode(QFileDialog::ExistingFile); @@ -277,116 +229,103 @@ void Conversation::onAttach() { d->show(); } -void Conversation::onFileSelected() { +void Conversation::onFileSelected() +{ QFileDialog* d = static_cast(sender()); - for (const QString& path : d->selectedFiles()) + + for (const QString& path : d->selectedFiles()) { addAttachedFile(path); + } d->deleteLater(); } -void Conversation::setStatus(const QString& status) { +void Conversation::setStatus(const QString& status) +{ statusLabel->setText(Shared::processMessageBody(status)); } -Models::Roster::ElId Conversation::getId() const { +Models::Roster::ElId Conversation::getId() const +{ return {getAccount(), getJid()}; } -void Conversation::addAttachedFile(const QString& path) { - std::vector::const_iterator itr = std::find_if( - filesToAttach.begin(), - filesToAttach.end(), - [&path] (const Badge* badge) {return badge->id == path;} - ); - if (itr != filesToAttach.end()) - return; - +void Conversation::addAttachedFile(const QString& path) +{ QMimeDatabase db; QMimeType type = db.mimeTypeForFile(path); QFileInfo info(path); - - QIcon fileIcon = QIcon::fromTheme(type.iconName()); - if (fileIcon.isNull()) - fileIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); - - Badge* badge = new Badge(path, info.fileName(), fileIcon); - filesToAttach.push_back(badge); - connect(badge, &Badge::closeClicked, this, &Conversation::onBadgeClose); - filesLayout->addWidget(badge); - if (filesLayout->count() == 1) - filesLayout->setContentsMargins(3, 3, 3, 3); + Badge* badge = new Badge(path, info.fileName(), QIcon::fromTheme(type.iconName())); + + connect(badge, &Badge::close, this, &Conversation::onBadgeClose); + try { + filesToAttach.push_back(badge); + filesLayout->addWidget(badge); + if (filesLayout->count() == 1) { + filesLayout->setContentsMargins(3, 3, 3, 3); + } + } catch (const W::Order::Duplicates& e) { + delete badge; + } catch (...) { + throw; + } } -void Conversation::removeAttachedFile(const QString& id) { - std::vector::const_iterator itr = std::find_if( - filesToAttach.begin(), - filesToAttach.end(), - [&id] (const Badge* badge) {return badge->id == id;} - ); +void Conversation::removeAttachedFile(Badge* badge) +{ + W::Order::const_iterator itr = filesToAttach.find(badge); if (itr != filesToAttach.end()) { - Badge* badge = *itr; - filesToAttach.erase(itr); - if (filesLayout->count() == 1) + filesToAttach.erase(badge); + if (filesLayout->count() == 1) { filesLayout->setContentsMargins(0, 0, 0, 0); - + } badge->deleteLater(); } } -void Conversation::onBadgeClose() { +void Conversation::onBadgeClose() +{ Badge* badge = static_cast(sender()); - removeAttachedFile(badge->id); + removeAttachedFile(badge); } -void Conversation::clearAttachedFiles() { - for (Badge* badge : filesToAttach) +void Conversation::clearAttachedFiles() +{ + for (Badge* badge : filesToAttach) { badge->deleteLater(); - + } filesToAttach.clear(); filesLayout->setContentsMargins(0, 0, 0, 0); } -void Conversation::clear() { - currentMessageId.clear(); - currentAction = CurrentAction::none; - m_ui->currentActionBadge->setVisible(false); +void Conversation::onClearButton() +{ clearAttachedFiles(); m_ui->messageEditor->clear(); } -void Conversation::onEncryptionButtonClicked() {} - -void Conversation::setAvatar(const QString& path) { - QPixmap pixmap; - if (path.size() == 0) - pixmap = Shared::icon("user", true).pixmap(avatarSize); - else - pixmap = QPixmap(path).scaled(avatarSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - - QPixmap result(avatarSize); - result.fill(Qt::transparent); - QPainter painter(&result); - painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::SmoothPixmapTransform); - QPainterPath maskPath; - maskPath.addEllipse(0, 0, avatarSize.width(), avatarSize.height()); - painter.setClipPath(maskPath); - painter.drawPixmap(0, 0, pixmap); - - m_ui->avatar->setPixmap(result); +void Conversation::setAvatar(const QString& path) +{ + if (path.size() == 0) { + m_ui->avatar->setPixmap(Shared::icon("user", true).pixmap(QSize(50, 50))); + } else { + m_ui->avatar->setPixmap(path); + } } -void Conversation::onTextEditDocSizeChanged(const QSizeF& size) { +void Conversation::onTextEditDocSizeChanged(const QSizeF& size) +{ m_ui->messageEditor->setMaximumHeight(int(size.height())); } -void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) { +void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) +{ shadow.setFrames(top, right, bottom, left); } -void Conversation::dragEnterEvent(QDragEnterEvent* event) { +void Conversation::dragEnterEvent(QDragEnterEvent* event) +{ bool accept = false; if (event->mimeData()->hasUrls()) { QList list = event->mimeData()->urls(); @@ -406,12 +345,13 @@ void Conversation::dragEnterEvent(QDragEnterEvent* event) { } } -void Conversation::dragLeaveEvent(QDragLeaveEvent* event) { - SHARED_UNUSED(event); +void Conversation::dragLeaveEvent(QDragLeaveEvent* event) +{ overlay->hide(); } -void Conversation::dropEvent(QDropEvent* event) { +void Conversation::dropEvent(QDropEvent* event) +{ bool accept = false; if (event->mimeData()->hasUrls()) { QList list = event->mimeData()->urls(); @@ -425,13 +365,14 @@ void Conversation::dropEvent(QDropEvent* event) { } } } - if (accept) + if (accept) { event->acceptProposedAction(); - + } overlay->hide(); } -Shared::Message Conversation::createMessage() const { +Shared::Message Conversation::createMessage() const +{ Shared::Message msg; msg.setOutgoing(true); msg.generateRandomId(); @@ -440,20 +381,23 @@ Shared::Message Conversation::createMessage() const { return msg; } -void Conversation::onFeedMessage(const Shared::Message& msg) { +void Conversation::onFeedMessage(const Shared::Message& msg) +{ this->onMessage(msg); } -void Conversation::onMessage(const Shared::Message& msg) { +void Conversation::onMessage(const Shared::Message& msg) +{ if (!msg.getForwarded()) { QApplication::alert(this); - if (window()->windowState().testFlag(Qt::WindowMinimized)) + if (window()->windowState().testFlag(Qt::WindowMinimized)) { emit notifyableMessage(getAccount(), msg); - + } } } -void Conversation::positionShadow() { +void Conversation::positionShadow() +{ int w = width(); int h = feed->height(); @@ -462,45 +406,26 @@ void Conversation::positionShadow() { shadow.raise(); } -void Conversation::onFeedContext(const QPoint& pos) { +void Conversation::onFeedContext(const QPoint& pos) +{ QModelIndex index = feed->indexAt(pos); if (index.isValid()) { Shared::Message* item = static_cast(index.internalPointer()); contextMenu->clear(); - QString id = item->getId(); bool showMenu = false; if (item->getState() == Shared::Message::State::error) { showMenu = true; + QString id = item->getId(); QAction* resend = contextMenu->addAction(Shared::icon("view-refresh"), tr("Try sending again")); connect(resend, &QAction::triggered, [this, id]() { element->feed->registerUpload(id); emit resendMessage(id); }); } - - QString selected = feed->getSelectedText(); - if (selected.size() > 0) { - showMenu = true; - QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected")); - connect(copy, &QAction::triggered, [selected] () { - QClipboard* cb = QApplication::clipboard(); - cb->setText(selected); - }); - } - - QString body = item->getBody(); - if (body.size() > 0) { - showMenu = true; - QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy message")); - connect(copy, &QAction::triggered, [body] () { - QClipboard* cb = QApplication::clipboard(); - cb->setText(body); - }); - } - QString path = Shared::resolvePath(item->getAttachPath()); - if (!path.isEmpty()) { + QString path = item->getAttachPath(); + if (path.size() > 0) { showMenu = true; QAction* open = contextMenu->addAction(Shared::icon("document-preview"), tr("Open")); connect(open, &QAction::triggered, [path]() { @@ -512,54 +437,9 @@ void Conversation::onFeedContext(const QPoint& pos) { Shared::Global::highlightInFileManager(path); }); } - - bool hasAttach = !item->getAttachPath().isEmpty() || !item->getOutOfBandUrl().isEmpty(); - //the only mandatory condition - is for the message to be outgoing, the rest is just a good intention on the server - if (item->getOutgoing() && !hasAttach && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { - showMenu = true; - QAction* edit = contextMenu->addAction(Shared::icon("edit-rename"), tr("Edit")); - connect(edit, &QAction::triggered, this, std::bind(&Conversation::onMessageEditRequested, this, id)); - } - if (showMenu) + if (showMenu) { contextMenu->popup(feed->viewport()->mapToGlobal(pos)); + } } } - -void Conversation::onMessageEditorContext(const QPoint& pos) { - pasteImageAction->setEnabled(Conversation::checkClipboardImage()); - - QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); - editorMenu->addSeparator(); - editorMenu->addAction(pasteImageAction); - - editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); -} - -void Conversation::onMessageEditRequested(const QString& id) { - clear(); - - try { - Shared::Message msg = element->feed->getMessage(id); - - currentMessageId = id; - m_ui->currentActionBadge->setVisible(true); - m_ui->currentActionBadge->setText(tr("Editing message...")); - currentAction = CurrentAction::edit; - m_ui->messageEditor->setText(msg.getBody()); - QString path = msg.getAttachPath(); - if (path.size() > 0) - addAttachedFile(path); - - } catch (const Models::MessageFeed::NotFound& e) { - qDebug() << "The message requested to be edited was not found" << e.getMessage().c_str(); - qDebug() << "Ignoring"; - } -} - -void Conversation::showEvent(QShowEvent* event) { - QWidget::showEvent(event); - - emit shown(); - m_ui->messageEditor->setFocus(); -} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index bece7a8..b0eb745 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -16,10 +16,10 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CONVERSATION_H +#define CONVERSATION_H #include -#include #include #include #include @@ -29,9 +29,10 @@ #include #include -#include - #include "shared/message.h" +#include "shared/order.h" +#include "shared/icons.h" +#include "shared/utils.h" #include "ui/models/account.h" #include "ui/models/roster.h" @@ -43,11 +44,13 @@ #include "ui/widgets/messageline/feedview.h" #include "ui/widgets/messageline/messagedelegate.h" -namespace Ui { +namespace Ui +{ class Conversation; } -class KeyEnterReceiver : public QObject { +class KeyEnterReceiver : public QObject +{ Q_OBJECT public: KeyEnterReceiver(QObject* parent = 0); @@ -57,10 +60,10 @@ protected: signals: void enterPressed(); - void imagePasted(); }; -class Conversation : public QWidget { +class Conversation : public QWidget +{ Q_OBJECT public: Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent = 0); @@ -74,90 +77,64 @@ public: void setPalResource(const QString& res); virtual void setAvatar(const QString& path); void setFeedFrames(bool top, bool right, bool bottom, bool left); - static bool checkClipboardImage(); signals: void sendMessage(const Shared::Message& message); - void replaceMessage(const QString& originalId, const Shared::Message& message); void resendMessage(const QString& id); void requestArchive(const QString& before); void shown(); void requestLocalFile(const QString& messageId, const QString& url); void downloadFile(const QString& messageId, const QString& url); void notifyableMessage(const QString& account, const Shared::Message& msg); - void setEncryption(Shared::EncryptionProtocol value); protected: virtual void setName(const QString& name); virtual Shared::Message createMessage() const; void setStatus(const QString& status); void addAttachedFile(const QString& path); - void removeAttachedFile(const QString& id); + void removeAttachedFile(Badge* badge); void clearAttachedFiles(); void dragEnterEvent(QDragEnterEvent* event) override; void dragLeaveEvent(QDragLeaveEvent* event) override; void dropEvent(QDropEvent* event) override; void initializeOverlay(); virtual void onMessage(const Shared::Message& msg); - virtual void showEvent(QShowEvent * event) override; - - void createFeed(); - void createUI(); - void subscribeEvents(); protected slots: - void initiateMessageSending(); - void initiateMessageSending(const Shared::Message& msg); - void onImagePasted(); + void onEnterPressed(); void onAttach(); void onFileSelected(); void onBadgeClose(); - void clear(); + void onClearButton(); void onTextEditDocSizeChanged(const QSizeF& size); void onAccountChanged(Models::Item* item, int row, int col); void onFeedMessage(const Shared::Message& msg); void positionShadow(); void onFeedContext(const QPoint &pos); - void onMessageEditorContext(const QPoint &pos); - void onMessageEditRequested(const QString& id); - virtual void onEncryptionButtonClicked(); public: const bool isMuc; protected: - enum class CurrentAction { - none, - edit - }; - Models::Account* account; Models::Element* element; QString palJid; QString activePalResource; - Ui::Conversation* m_ui; + QScopedPointer m_ui; KeyEnterReceiver ker; QString thread; QLabel* statusIcon; QLabel* statusLabel; FlowLayout* filesLayout; QWidget* overlay; - std::vector filesToAttach; + W::Order filesToAttach; FeedView* feed; MessageDelegate* delegate; bool manualSliderChange; bool tsb; //transient scroll bars - QAction* pasteImageAction; - ShadowOverlay shadow; QMenu* contextMenu; - CurrentAction currentAction; - QString currentMessageId; - -private: - static bool painterInitialized; - static QPainterPath* avatarMask; - static QPixmap* avatarPixmap; - static QPainter* avatarPainter; }; + +#endif // CONVERSATION_H diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index c73de45..bb38666 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -271,37 +271,14 @@ - - :/images/fallback/dark/big/unfavorite.svg:/images/fallback/dark/big/unfavorite.svg + + .. true - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - @@ -315,39 +292,14 @@ - - - - false - - - - - - - - - true - - - false - - - false - - - true - - - - - :/images/fallback/dark/big/mail-attachment.svg:/images/fallback/dark/big/mail-attachment.svg + + .. true @@ -360,8 +312,8 @@ - - :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg + + .. true @@ -380,8 +332,8 @@ - - :/images/fallback/dark/big/send.svg:/images/fallback/dark/big/send.svg + + .. true @@ -427,9 +379,6 @@ 30 - - Qt::CustomContextMenu - false @@ -451,7 +400,7 @@ background-color: transparent <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -470,16 +419,6 @@ p, li { white-space: pre-wrap; } - - - Badge - QFrame -
ui/utils/badge.h
- 1 -
-
- - - +
diff --git a/ui/widgets/info/CMakeLists.txt b/ui/widgets/info/CMakeLists.txt deleted file mode 100644 index 44edb66..0000000 --- a/ui/widgets/info/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -set(SOURCE_FILES - info.cpp - contactgeneral.cpp - contactcontacts.cpp - description.cpp -) - -set(UI_FILES - info.ui - contactgeneral.ui - contactcontacts.ui - description.ui -) - -set(HEADER_FILES - info.h - contactgeneral.h - contactcontacts.h - description.h -) - -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${UI_FILES} - ${HEADER_FILES} -) - -if (WITH_OMEMO) - add_subdirectory(omemo) -endif() diff --git a/ui/widgets/info/contactcontacts.cpp b/ui/widgets/info/contactcontacts.cpp deleted file mode 100644 index 10615ed..0000000 --- a/ui/widgets/info/contactcontacts.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "contactcontacts.h" -#include "ui_contactcontacts.h" - -UI::ContactContacts::ContactContacts(QWidget* parent): - QWidget(parent), - m_ui(new Ui::ContactContacts), - contextMenu(new QMenu()), - emails(), - phones(), - roleDelegate(new ComboboxDelegate()), - phoneTypeDelegate(new ComboboxDelegate()), - editable(false) -{ - m_ui->setupUi(this); - - m_ui->jabberID->setReadOnly(true); - - initializeDelegates(); - initializeViews(); -} - -UI::ContactContacts::~ContactContacts() { - contextMenu->deleteLater(); -} - -void UI::ContactContacts::setVCard(const QString& jid, const Shared::VCard& card, bool p_editable) { - editable = p_editable; - m_ui->jabberID->setText(jid); - m_ui->url->setText(card.getUrl()); - m_ui->url->setReadOnly(!p_editable); - - emails.setEditable(editable); - phones.setEditable(editable); - emails.setEmails(card.getEmails()); - phones.setPhones(card.getPhones()); -} - -void UI::ContactContacts::initializeDelegates() { - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); - - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); -} - -void UI::ContactContacts::initializeViews() { - m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->emailsView->setModel(&emails); - m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); - m_ui->emailsView->setColumnWidth(2, 25); - m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); - m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->phonesView->setModel(&phones); - m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); - m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); - m_ui->phonesView->setColumnWidth(3, 25); - m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); - m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &UI::ContactContacts::onContextMenu); - connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &UI::ContactContacts::onContextMenu); -} - -void UI::ContactContacts::onContextMenu(const QPoint& point) { - contextMenu->clear(); - bool hasMenu = false; - QAbstractItemView* snd = static_cast(sender()); - if (snd == m_ui->emailsView) { - hasMenu = true; - if (editable) { - QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); - connect(add, &QAction::triggered, this, &UI::ContactContacts::onAddEmail); - - QItemSelectionModel* sm = m_ui->emailsView->selectionModel(); - int selectionSize = sm->selectedRows().size(); - - if (selectionSize > 0) { - if (selectionSize == 1) { - int row = sm->selectedRows().at(0).row(); - if (emails.isPreferred(row)) { - QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); - } else { - QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); - } - } - - QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses")); - connect(del, &QAction::triggered, this, &UI::ContactContacts::onRemoveEmail); - } - } - - QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard")); - connect(cp, &QAction::triggered, this, &UI::ContactContacts::onCopyEmail); - } else if (snd == m_ui->phonesView) { - hasMenu = true; - if (editable) { - QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number")); - connect(add, &QAction::triggered, this, &UI::ContactContacts::onAddPhone); - - QItemSelectionModel* sm = m_ui->phonesView->selectionModel(); - int selectionSize = sm->selectedRows().size(); - - if (selectionSize > 0) { - if (selectionSize == 1) { - int row = sm->selectedRows().at(0).row(); - if (phones.isPreferred(row)) { - QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); - } else { - QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); - } - } - - QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers")); - connect(del, &QAction::triggered, this, &UI::ContactContacts::onRemovePhone); - } - } - - QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard")); - connect(cp, &QAction::triggered, this, &UI::ContactContacts::onCopyPhone); - } - - if (hasMenu) { - contextMenu->popup(snd->viewport()->mapToGlobal(point)); - } -} - -void UI::ContactContacts::onAddEmail() { - QModelIndex index = emails.addNewEmptyLine(); - m_ui->emailsView->setCurrentIndex(index); - m_ui->emailsView->edit(index); -} - -void UI::ContactContacts::onAddAddress() {} //TODO -void UI::ContactContacts::onAddPhone() { - QModelIndex index = phones.addNewEmptyLine(); - m_ui->phonesView->setCurrentIndex(index); - m_ui->phonesView->edit(index); -} -void UI::ContactContacts::onRemoveAddress() {} //TODO -void UI::ContactContacts::onRemoveEmail() { - QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); - - QList rows; - for (const QModelIndex& index : selection.indexes()) { - rows.append(index.row()); - } - - std::sort(rows.begin(), rows.end()); - - int prev = -1; - for (int i = rows.count() - 1; i >= 0; i -= 1) { - int current = rows[i]; - if (current != prev) { - emails.removeLines(current, 1); - prev = current; - } - } -} - -void UI::ContactContacts::onRemovePhone() { - QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); - - QList rows; - for (const QModelIndex& index : selection.indexes()) { - rows.append(index.row()); - } - - std::sort(rows.begin(), rows.end()); - - int prev = -1; - for (int i = rows.count() - 1; i >= 0; i -= 1) { - int current = rows[i]; - if (current != prev) { - phones.removeLines(current, 1); - prev = current; - } - } -} - -void UI::ContactContacts::onCopyEmail() { - QList selection(m_ui->emailsView->selectionModel()->selectedRows()); - - QList addrs; - for (const QModelIndex& index : selection) { - addrs.push_back(emails.getEmail(index.row())); - } - - QString list = addrs.join("\n"); - - QClipboard* cb = QApplication::clipboard(); - cb->setText(list); -} - -void UI::ContactContacts::onCopyPhone() { - QList selection(m_ui->phonesView->selectionModel()->selectedRows()); - - QList phs; - for (const QModelIndex& index : selection) { - phs.push_back(phones.getPhone(index.row())); - } - - QString list = phs.join("\n"); - - QClipboard* cb = QApplication::clipboard(); - cb->setText(list); -} - -QString UI::ContactContacts::title() const { - return m_ui->contactHeading->text(); -} - -void UI::ContactContacts::fillVCard(Shared::VCard& card) const { - card.setUrl(m_ui->url->text()); - - emails.getEmails(card.getEmails()); - phones.getPhones(card.getPhones()); -} - diff --git a/ui/widgets/info/contactcontacts.h b/ui/widgets/info/contactcontacts.h deleted file mode 100644 index d2c7e9f..0000000 --- a/ui/widgets/info/contactcontacts.h +++ /dev/null @@ -1,76 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef UI_WIDGETS_CONTACTCONTACTS_H -#define UI_WIDGETS_CONTACTCONTACTS_H - -#include -#include -#include -#include -#include -#include - -#include "shared/vcard.h" -#include "shared/icons.h" -#include "ui/models/info/emails.h" -#include "ui/models/info/phones.h" -#include "ui/utils/comboboxdelegate.h" - -namespace UI { -namespace Ui -{ -class ContactContacts; -} - -class ContactContacts : public QWidget { - Q_OBJECT -public: - ContactContacts(QWidget* parent = nullptr); - ~ContactContacts(); - - void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); - void fillVCard(Shared::VCard& card) const; - QString title() const; - -private slots: - void onContextMenu(const QPoint& point); - void onAddAddress(); - void onRemoveAddress(); - void onAddEmail(); - void onCopyEmail(); - void onRemoveEmail(); - void onAddPhone(); - void onCopyPhone(); - void onRemovePhone(); - -private: - void initializeDelegates(); - void initializeViews(); - -private: - QScopedPointer m_ui; - QMenu* contextMenu; - Models::EMails emails; - Models::Phones phones; - ComboboxDelegate* roleDelegate; - ComboboxDelegate* phoneTypeDelegate; - bool editable; -}; - -} - -#endif // UI_WIDGETS_CONTACTCONTACTS_H diff --git a/ui/widgets/info/contactcontacts.ui b/ui/widgets/info/contactcontacts.ui deleted file mode 100644 index 8104d50..0000000 --- a/ui/widgets/info/contactcontacts.ui +++ /dev/null @@ -1,282 +0,0 @@ - - - UI::ContactContacts - - - Contacts - - - - 0 - - - 0 - - - 6 - - - 0 - - - 0 - - - - - font: 600 24pt ; - - - Contact - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - true - - - - - 0 - 0 - 545 - 544 - - - - - - - Qt::Horizontal - - - - - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - font: 600 16pt; - - - Addresses - - - Qt::AlignCenter - - - - - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - font: 600 16pt; - - - E-Mail addresses - - - Qt::AlignCenter - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Jabber ID - - - jabberID - - - - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Web site - - - url - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - font: 600 16pt; - - - Phone numbers - - - Qt::AlignCenter - - - - - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - - - - - - diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp deleted file mode 100644 index e469fcb..0000000 --- a/ui/widgets/info/contactgeneral.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "contactgeneral.h" -#include "ui_contactgeneral.h" - -#include - -const std::set UI::ContactGeneral::supportedTypes = {"image/jpeg", "image/png"}; -constexpr int maxAvatarSize = 160; - -UI::ContactGeneral::ContactGeneral(QWidget* parent): - QWidget(parent), - m_ui(new Ui::ContactGeneral), - avatarMenu(nullptr), - avatarButtonMargins(), - currentAvatarType(Shared::Avatar::empty), - currentAvatarPath(""), - currentJid(""), - avatarDiablog(nullptr) -{ - m_ui->setupUi(this); - - initializeActions(); - initializeAvatar(); -} - -UI::ContactGeneral::~ContactGeneral() { - if (avatarMenu != nullptr) - avatarMenu->deleteLater(); - - if (avatarDiablog != nullptr) - deleteAvatarDialog(); -} - -QString UI::ContactGeneral::title() const { - return m_ui->generalHeading->text();} - -void UI::ContactGeneral::setEditable(bool edit) { - m_ui->fullName->setReadOnly(!edit); - m_ui->firstName->setReadOnly(!edit); - m_ui->middleName->setReadOnly(!edit); - m_ui->lastName->setReadOnly(!edit); - m_ui->nickName->setReadOnly(!edit); - m_ui->birthday->setReadOnly(!edit); - m_ui->organizationName->setReadOnly(!edit); - m_ui->organizationDepartment->setReadOnly(!edit); - m_ui->organizationTitle->setReadOnly(!edit); - m_ui->organizationRole->setReadOnly(!edit); - - if (edit) { - avatarMenu = new QMenu(); - m_ui->avatarButton->setMenu(avatarMenu); - avatarMenu->addAction(m_ui->actionSetAvatar); - avatarMenu->addAction(m_ui->actionClearAvatar); - } else { - if (avatarMenu != nullptr) { - avatarMenu->deleteLater(); - avatarMenu = nullptr; - m_ui->avatarButton->setMenu(nullptr); - } - } - - m_ui->actionSetAvatar->setEnabled(edit); - m_ui->actionClearAvatar->setEnabled(false); //need to unlock it explicitly after the type of avatar is clear! -} - -void UI::ContactGeneral::deleteAvatarDialog() { - avatarDiablog->deleteLater(); - - disconnect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); - disconnect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); - - avatarDiablog = nullptr; -} - -void UI::ContactGeneral::initializeActions() { - QAction* setAvatar = m_ui->actionSetAvatar; - QAction* clearAvatar = m_ui->actionClearAvatar; - - connect(setAvatar, &QAction::triggered, this, &UI::ContactGeneral::onSetAvatar); - connect(clearAvatar, &QAction::triggered, this, &UI::ContactGeneral::onClearAvatar); - - setAvatar->setEnabled(false); - clearAvatar->setEnabled(false); -} - -void UI::ContactGeneral::initializeAvatar() { - QToolButton* avatarButton = m_ui->avatarButton; - avatarButtonMargins = avatarButton->size(); - - int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); - avatarButton->setIconSize(QSize(height, height)); -} - -void UI::ContactGeneral::onSetAvatar() { - avatarDiablog = new QFileDialog(this, tr("Chose your new avatar")); - avatarDiablog->setFileMode(QFileDialog::ExistingFile); - avatarDiablog->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); - - connect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); - connect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); - - avatarDiablog->show(); -} - -void UI::ContactGeneral::onClearAvatar() { - currentAvatarType = Shared::Avatar::empty; - currentAvatarPath = ""; - - updateAvatar(); -} - -void UI::ContactGeneral::updateAvatar() { - int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); - switch (currentAvatarType) { - case Shared::Avatar::empty: - m_ui->avatarButton->setIcon(Shared::icon("user", true)); - m_ui->avatarButton->setIconSize(QSize(height, height)); - m_ui->actionClearAvatar->setEnabled(false); - break; - case Shared::Avatar::autocreated: - case Shared::Avatar::valid: - QPixmap pixmap(currentAvatarPath); - qreal h = pixmap.height(); - qreal w = pixmap.width(); - qreal aspectRatio = w / h; - m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); - m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); - m_ui->actionClearAvatar->setEnabled(m_ui->actionSetAvatar->isEnabled()); //I assume that if set avatar is enabled then we can also clear - break; - } -} - -void UI::ContactGeneral::avatarSelected() { - QMimeDatabase db; - QString path = avatarDiablog->selectedFiles().front(); - QMimeType type = db.mimeTypeForFile(path); - deleteAvatarDialog(); - - if (supportedTypes.find(type.name()) == supportedTypes.end()) { - qDebug() << "Selected for avatar file is not supported, skipping"; - } else { - QImage src(path); - QImage dst; - if (src.width() > maxAvatarSize || src.height() > maxAvatarSize) { - dst = src.scaled(maxAvatarSize, maxAvatarSize, Qt::KeepAspectRatio); - } - QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + currentJid + ".temp." + type.preferredSuffix(); - QFile oldTemp(path); - if (oldTemp.exists()) { - if (!oldTemp.remove()) { - qDebug() << "Error removing old temp avatar" << path; - return; - } - } - bool success = dst.save(path); - if (success) { - currentAvatarPath = path; - currentAvatarType = Shared::Avatar::valid; - - updateAvatar(); - } else { - qDebug() << "couldn't save temp avatar" << path << ", skipping"; - } - } -} - -void UI::ContactGeneral::setVCard(const QString& jid, const Shared::VCard& card, bool editable) { - currentJid = jid; - setEditable(editable); - m_ui->fullName->setText(card.getFullName()); - m_ui->firstName->setText(card.getFirstName()); - m_ui->middleName->setText(card.getMiddleName()); - m_ui->lastName->setText(card.getLastName()); - m_ui->nickName->setText(card.getNickName()); - m_ui->birthday->setDate(card.getBirthday()); - m_ui->organizationName->setText(card.getOrgName()); - m_ui->organizationDepartment->setText(card.getOrgUnit()); - m_ui->organizationTitle->setText(card.getOrgTitle()); - m_ui->organizationRole->setText(card.getOrgRole()); - - currentAvatarType = card.getAvatarType(); - currentAvatarPath = card.getAvatarPath(); - - updateAvatar(); -} - -void UI::ContactGeneral::fillVCard(Shared::VCard& card) const { - card.setFullName(m_ui->fullName->text()); - card.setFirstName(m_ui->firstName->text()); - card.setMiddleName(m_ui->middleName->text()); - card.setLastName(m_ui->lastName->text()); - card.setNickName(m_ui->nickName->text()); - card.setBirthday(m_ui->birthday->date()); - card.setOrgName(m_ui->organizationName->text()); - card.setOrgUnit(m_ui->organizationDepartment->text()); - card.setOrgRole(m_ui->organizationRole->text()); - card.setOrgTitle(m_ui->organizationTitle->text()); - card.setAvatarPath(currentAvatarPath); - card.setAvatarType(currentAvatarType); -} diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h deleted file mode 100644 index 33b4c28..0000000 --- a/ui/widgets/info/contactgeneral.h +++ /dev/null @@ -1,77 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef UI_WIDGETS_CONTACTGENERAL_H -#define UI_WIDGETS_CONTACTGENERAL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shared/enums.h" -#include "shared/vcard.h" -#include "shared/icons.h" - -namespace UI { -namespace Ui -{ -class ContactGeneral; -} - -class ContactGeneral : public QWidget{ - Q_OBJECT -public: - ContactGeneral(QWidget* parent = nullptr); - ~ContactGeneral(); - - void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); - void fillVCard(Shared::VCard& card) const; - QString title() const; - -private: - void setEditable(bool edit); - void initializeActions(); - void initializeAvatar(); - void updateAvatar(); - -private slots: - void deleteAvatarDialog(); - void avatarSelected(); - void onSetAvatar(); - void onClearAvatar(); - -private: - QScopedPointer m_ui; - QMenu* avatarMenu; - QSize avatarButtonMargins; - Shared::Avatar currentAvatarType; - QString currentAvatarPath; - QString currentJid; - QFileDialog* avatarDiablog; - - static const std::set supportedTypes; -}; - -} - -#endif // UI_WIDGETS_CONTACTGENERAL_H diff --git a/ui/widgets/info/contactgeneral.ui b/ui/widgets/info/contactgeneral.ui deleted file mode 100644 index 5ec123e..0000000 --- a/ui/widgets/info/contactgeneral.ui +++ /dev/null @@ -1,466 +0,0 @@ - - - UI::ContactGeneral - - - - 0 - 0 - 340 - 625 - - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - - 0 - 0 - - - - font: 600 16pt; - - - Organization - - - Qt::AlignCenter - - - - - - - QLayout::SetDefaultConstraint - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Middle name - - - middleName - - - - - - - First name - - - firstName - - - - - - - Last name - - - lastName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Nick name - - - nickName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Birthday - - - birthday - - - - - - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - Organization name - - - organizationName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Unit / Department - - - organizationDepartment - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Role / Profession - - - organizationRole - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Job title - - - organizationTitle - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - - Qt::Horizontal - - - - - - - - - - - - Full name - - - fullName - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - - - - font: 600 24pt ; - - - General - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - font: 600 16pt; - - - QFrame::NoFrame - - - QFrame::Plain - - - Personal information - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - - :/images/fallback/dark/big/user.svg:/images/fallback/dark/big/user.svg - - - - 0 - 0 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonIconOnly - - - Qt::NoArrow - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - :/images/fallback/dark/big/edit-rename.svg:/images/fallback/dark/big/edit-rename.svg - - - Set avatar - - - - - - :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg - - - Clear avatar - - - - - fullName - firstName - middleName - lastName - nickName - birthday - avatarButton - organizationName - organizationDepartment - organizationRole - organizationTitle - - - - - - diff --git a/ui/widgets/info/description.cpp b/ui/widgets/info/description.cpp deleted file mode 100644 index b2981ca..0000000 --- a/ui/widgets/info/description.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "description.h" -#include "ui_description.h" - -UI::Description::Description(QWidget* parent): - QWidget(parent), - m_ui(new Ui::Description()) -{ - m_ui->setupUi(this); -} - -UI::Description::~Description() {} - -QString UI::Description::title() const { - return m_ui->descriptionHeading->text(); -} - -QString UI::Description::description() const { - return m_ui->description->toPlainText(); -} - -void UI::Description::setDescription(const QString& description) { - m_ui->description->setText(description); -} - -void UI::Description::setEditable(bool editable) { - m_ui->description->setReadOnly(!editable); -} - diff --git a/ui/widgets/info/description.h b/ui/widgets/info/description.h deleted file mode 100644 index dc823d0..0000000 --- a/ui/widgets/info/description.h +++ /dev/null @@ -1,46 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#ifndef UI_WIDGETS_DESCRIPTION_H -#define UI_WIDGETS_DESCRIPTION_H - -#include -#include - -namespace UI { -namespace Ui -{ -class Description; -} - -class Description : public QWidget { - Q_OBJECT -public: - Description(QWidget* parent = nullptr); - ~Description(); - - QString title() const; - QString description() const; - void setEditable(bool editable); - void setDescription(const QString& description); - -private: - QScopedPointer m_ui; -}; - -} - -#endif // UI_WIDGETS_DESCRIPTION_H diff --git a/ui/widgets/info/description.ui b/ui/widgets/info/description.ui deleted file mode 100644 index e69a8f6..0000000 --- a/ui/widgets/info/description.ui +++ /dev/null @@ -1,48 +0,0 @@ - - - UI::Description - - - Description - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - font: 600 24pt ; - - - Description - - - - - - - QFrame::StyledPanel - - - Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp deleted file mode 100644 index 26f63f1..0000000 --- a/ui/widgets/info/info.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "info.h" -#include "ui_info.h" - -UI::Info::Info(const QString& p_jid, QWidget* parent): - QWidget(parent), - jid(p_jid), - type(Shared::EntryType::none), - m_ui(new Ui::Info()), - contactGeneral(nullptr), - contactContacts(nullptr), -#ifdef WITH_OMEMO - omemo(nullptr), -#endif - description(nullptr), - overlay(new QWidget()), - progress(new Progress(100)), - progressLabel(new QLabel()) -{ - m_ui->setupUi(this); - m_ui->buttonBox->hide(); - m_ui->title->setText(tr("%1 info").arg(p_jid)); - m_ui->receivingTimeLabel->hide(); - - initializeOverlay(); - initializeButtonBox(); -} - -UI::Info::~Info() { - clear(); - overlay->deleteLater(); -} - -void UI::Info::setData(const Shared::Info& info) { - bool editable = false; - switch (info.getType()) { - case Shared::EntryType::ownAccount: - editable = true; - [[fallthrough]]; - case Shared::EntryType::contact: { - const Shared::VCard card = info.getVCardRef(); - QDateTime receivingTime = card.getReceivingTime(); - m_ui->receivingTimeLabel->show(); - m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); - initializeContactGeneral(jid, card, editable); - initializeContactContacts(jid, card, editable); - initializeDescription(card.getDescription(), editable); -#ifdef WITH_OMEMO - initializeOmemo(info.getActiveKeysRef()); -#endif - type = info.getType(); - } - break; - default: - clear(); - break; - } - - if (!editable) - m_ui->buttonBox->hide(); - else - m_ui->buttonBox->show(); - -} - -void UI::Info::initializeOverlay() { - QGridLayout* gr = static_cast(layout()); - gr->addWidget(overlay, 0, 0, 4, 1); - QVBoxLayout* nl = new QVBoxLayout(); - QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); - opacity->setOpacity(0.8); - overlay->setLayout(nl); - overlay->setBackgroundRole(QPalette::Base); - overlay->setAutoFillBackground(true); - overlay->setGraphicsEffect(opacity); - progressLabel->setAlignment(Qt::AlignCenter); - progressLabel->setFont(Shared::Global::getInstance()->titleFont); - progressLabel->setWordWrap(true); - nl->addStretch(); - nl->addWidget(progress); - nl->addWidget(progressLabel); - nl->addStretch(); - overlay->hide(); -} - -void UI::Info::initializeButtonBox() { - connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &UI::Info::onButtonBoxAccepted); - connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &UI::Info::close); - - m_ui->buttonBox->hide(); -} - -void UI::Info::onButtonBoxAccepted() { - if (type == Shared::EntryType::ownAccount) { - Shared::Info info(jid, Shared::EntryType::ownAccount); - Shared::VCard& card = info.getVCardRef(); - contactGeneral->fillVCard(card); - contactContacts->fillVCard(card); - card.setDescription(description->description()); -#ifdef WITH_OMEMO - omemo->fillData(info.getActiveKeysRef()); -#endif - emit saveInfo(info); - emit close(); - } -} - -void UI::Info::showProgress(const QString& line) { - if (line.size() > 0) - progressLabel->setText(line); - else - progressLabel->setText(tr("Requesting information about %1").arg(jid)); - - overlay->show(); - progress->start(); -} - -void UI::Info::hideProgress() { - overlay->hide(); - progress->stop(); -} - -void UI::Info::initializeContactGeneral(const QString& jid, const Shared::VCard& card, bool editable) { - if (contactGeneral == nullptr) { - contactGeneral = new ContactGeneral; - m_ui->tabWidget->addTab(contactGeneral, contactGeneral->title()); - } - contactGeneral->setVCard(jid, card, editable); -} - -void UI::Info::initializeContactContacts(const QString& jid, const Shared::VCard& card, bool editable) { - if (contactContacts == nullptr) { - contactContacts = new ContactContacts; - m_ui->tabWidget->addTab(contactContacts, contactContacts->title()); - } - contactContacts->setVCard(jid, card, editable); -} - -void UI::Info::initializeDescription(const QString& descr, bool editable) { - if (description == nullptr) { - description = new Description; - m_ui->tabWidget->addTab(description, description->title()); - } - - description->setDescription(descr); - description->setEditable(editable); -} - -QString UI::Info::getJid() const { - return jid; -} - -void UI::Info::clear() { - if (contactGeneral != nullptr) { - contactGeneral->deleteLater(); - contactGeneral = nullptr; - } - - if (contactContacts != nullptr) { - contactContacts->deleteLater(); - contactContacts = nullptr; - } - - if (description != nullptr) { - description->deleteLater(); - description = nullptr; - } - -#ifdef WITH_OMEMO - if (omemo != nullptr) { - omemo->deleteLater(); - omemo = nullptr; - } -#endif - - type = Shared::EntryType::none; -} - -#ifdef WITH_OMEMO -void UI::Info::initializeOmemo(const std::list& keys) { - if (omemo == nullptr) { - omemo = new Omemo(); - m_ui->tabWidget->addTab(omemo, omemo->title()); - } - omemo->setData(keys); -} - -#endif diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h deleted file mode 100644 index 10b7209..0000000 --- a/ui/widgets/info/info.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include -#include -#include -#include - -#include -#include - -#include "ui/utils/progress.h" - -#include "contactgeneral.h" -#include "contactcontacts.h" -#include "description.h" -#ifdef WITH_OMEMO -#include "omemo/omemo.h" -#endif - - -namespace UI { -namespace Ui { -class Info; -} - -class Info : public QWidget { - Q_OBJECT -public: - Info(const QString& jid, QWidget* parent = nullptr); - ~Info(); - - QString getJid() const; - void setData(const Shared::Info& info); - void showProgress(const QString& = ""); - void hideProgress(); - -signals: - void saveInfo(const Shared::Info& info); - -private slots: - void onButtonBoxAccepted(); - -private: - void initializeContactGeneral(const QString& jid, const Shared::VCard& card, bool editable); - void initializeContactContacts(const QString& jid, const Shared::VCard& card, bool editable); - void initializeDescription(const QString& description, bool editable); -#ifdef WITH_OMEMO - void initializeOmemo(const std::list& keys); -#endif - void initializeOverlay(); - void initializeButtonBox(); - void clear(); - -private: - QString jid; - Shared::EntryType type; - QScopedPointer m_ui; - ContactGeneral* contactGeneral; - ContactContacts* contactContacts; -#ifdef WITH_OMEMO - Omemo* omemo; -#endif - Description* description; - QWidget* overlay; - Progress* progress; - QLabel* progressLabel; - -}; - -} diff --git a/ui/widgets/info/info.ui b/ui/widgets/info/info.ui deleted file mode 100644 index ed2d42e..0000000 --- a/ui/widgets/info/info.ui +++ /dev/null @@ -1,113 +0,0 @@ - - - UI::Info - - - - 0 - 0 - 400 - 300 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - font: 16pt - - - Contact john@dow.org card - - - Qt::AlignCenter - - - - - - - font: italic 8pt; - - - Received 12.07.2007 at 17.35 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - Qt::TabFocus - - - QTabWidget::North - - - QTabWidget::Rounded - - - 1 - - - Qt::ElideNone - - - true - - - false - - - - - - - QDialogButtonBox::Close|QDialogButtonBox::Save - - - false - - - - - - - - - - diff --git a/ui/widgets/info/omemo/CMakeLists.txt b/ui/widgets/info/omemo/CMakeLists.txt deleted file mode 100644 index 4c76900..0000000 --- a/ui/widgets/info/omemo/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -set(SOURCE_FILES - omemo.cpp - keydelegate.cpp -) - -set(UI_FILES - omemo.ui -) - -set(HEADER_FILES - omemo.h - keydelegate.h -) - -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${UI_FILES} - ${HEADER_FILES} -) diff --git a/ui/widgets/info/omemo/keydelegate.cpp b/ui/widgets/info/omemo/keydelegate.cpp deleted file mode 100644 index 074ef1c..0000000 --- a/ui/widgets/info/omemo/keydelegate.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "keydelegate.h" -#include -#include - -#include "ui/models/info/omemo/keys.h" -#include - -constexpr uint8_t margin = 10; -constexpr uint8_t partSize = 8; -constexpr uint8_t maxSingleLineParts = 3; - -UI::KeyDelegate::KeyDelegate(QObject* parent): - QStyledItemDelegate(parent), - defaultFont(Shared::Global::getInstance()->defaultFont), - fingerPrintFont(Shared::Global::getInstance()->monospaceFont), - labelFont(Shared::Global::getInstance()->smallFont), - defaultMetrics(Shared::Global::getInstance()->defaultFontMetrics), - fingerPrintMetrics(Shared::Global::getInstance()->monospaceMetrics), - labelMetrics(Shared::Global::getInstance()->smallFontMetrics), - spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) -{} - -UI::KeyDelegate::~KeyDelegate() {} - - -void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, true); - - QRect r; - int maxRight = 0; - int leftOrigin = option.rect.left() + margin; - QColor q = painter->pen().color(); - q.setAlpha(180); - QRect rect = option.rect; - bool hover = option.state & QStyle::State_MouseOver; - if (hover) { - painter->save(); - painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); - painter->restore(); - } - - QVariant dirtyV = index.data(Models::Keys::Dirty); - if (dirtyV.isValid() && dirtyV.toBool()) { - painter->save(); - rect.setWidth(margin / 2); - painter->fillRect(rect, option.palette.brush(QPalette::Active, QPalette::Highlight)); - rect.setWidth(option.rect.width()); - painter->restore(); - } - - rect.adjust(margin, margin, -margin, -margin); - QVariant labelV = index.data(Models::Keys::Label); - if (labelV.isValid()) { - QString label = labelV.toString(); - if (label.size() > 0) { - painter->save(); - painter->setFont(labelFont); - painter->setPen(q); - painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label, &r); - rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); - maxRight = std::max(r.width(), maxRight); - painter->restore(); - } - } - - QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); - if (fingerPrintV.isValid()) { - painter->save(); - painter->setFont(fingerPrintFont); - QByteArray fingerPrint = fingerPrintV.toByteArray(); - std::vector parts = getParts(fingerPrint); - uint8_t partsLength = parts.size(); - uint8_t firstLine; - if (partsLength > maxSingleLineParts) - firstLine = partsLength / 2 + partsLength % 2; - else - firstLine = partsLength; - - for (uint8_t i = 0; i < partsLength; ++i) { - if (i == firstLine) { - maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); - rect.setLeft(leftOrigin); - rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); - } - painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r); - rect.adjust(r.width() + spaceWidth ,0, 0, 0); - } - - maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); - rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); - rect.setLeft(leftOrigin); - - painter->restore(); - } - - - QVariant lastV = index.data(Models::Keys::LastInteraction); - if (lastV.isValid()) { - QDateTime last = lastV.toDateTime(); - if (last.isValid()) { - painter->save(); - painter->setFont(labelFont); - painter->setPen(q); - painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString(), &r); - rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); - maxRight = std::max(r.width(), maxRight); - painter->restore(); - } - } - - QVariant levelV = index.data(Models::Keys::TrustLevel); - if (levelV.isValid()) { - Shared::TrustLevel level = static_cast(levelV.toUInt()); - QString levelName = Shared::Global::getName(level); - - if (maxRight > 0) - maxRight += margin; - rect.setLeft(leftOrigin + maxRight); - rect.setTop(option.rect.top() + maxRight); - rect.setBottom(option.rect.bottom() - maxRight); - painter->setFont(defaultFont); - painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, levelName, &r); - } - - - painter->restore(); -} - -QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - QSize size = QStyledItemDelegate::sizeHint(option, index); - - int mh = margin * 2; - int mw = margin * 2; - int width = 0; - - QVariant labelV = index.data(Models::Keys::Label); - if (labelV.isValid()) { - QString label = labelV.toString(); - if (label.size() > 0) { - mh += labelMetrics.lineSpacing(); - width = labelMetrics.horizontalAdvance(label); - } - } - - QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); - if (fingerPrintV.isValid()) { - QString hex = fingerPrintV.toByteArray().toHex(); - uint8_t parts = hex.size() / partSize; - - mh += fingerPrintMetrics.height(); - if (parts > maxSingleLineParts) - mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading(); - - uint8_t firstLine; - if (parts > maxSingleLineParts) - firstLine = parts / 2 + parts % 2; - else - firstLine = parts; - - width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth); - width += 1; //there is a mistake somewhere, this the cheapest way to compensate it - } - QVariant lastV = index.data(Models::Keys::LastInteraction); - if (lastV.isValid()) { - QDateTime last = lastV.toDateTime(); - if (last.isValid()) { - mh += labelMetrics.lineSpacing(); - QString dt = last.toString(); - width = std::max(labelMetrics.horizontalAdvance(dt), width); - } - } - - QVariant levelV = index.data(Models::Keys::TrustLevel); - if (levelV.isValid()) { - Shared::TrustLevel level = static_cast(levelV.toUInt()); - QString levelName = Shared::Global::getName(level); - if (width > 0) - width += margin; - - width += defaultMetrics.horizontalAdvance(levelName); - width += 1; //there is a mistake somewhere, this the cheapest way to compensate it - } - - mw += width; - if (size.width() < mw) - size.setWidth(mw); - - if (size.height() < mh) - size.setHeight(mh); - - return size; -} - -std::vector UI::KeyDelegate::getParts(const QByteArray& data) { - QString hex = data.toHex(); - uint8_t parts = hex.size() / partSize; - std::vector result(parts); - for (uint8_t i = 0; i < parts; ++i) { - uint16_t index = i * partSize; - uint8_t length = partSize; - if (index + length > hex.size()) - length = hex.size() - index; - QStringRef part(&hex, index, length); - result[i] = part.toString(); - } - - return result; -} - diff --git a/ui/widgets/info/omemo/keydelegate.h b/ui/widgets/info/omemo/keydelegate.h deleted file mode 100644 index 6d7f067..0000000 --- a/ui/widgets/info/omemo/keydelegate.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -#include - -namespace UI { - -class KeyDelegate : public QStyledItemDelegate { - Q_OBJECT -public: - KeyDelegate(QObject* parent = nullptr); - ~KeyDelegate(); - - QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - -private: - const QFont& defaultFont; - const QFont& fingerPrintFont; - const QFont& labelFont; - const QFontMetrics& defaultMetrics; - const QFontMetrics& fingerPrintMetrics; - const QFontMetrics& labelMetrics; - int spaceWidth; - -private: - static std::vector getParts(const QByteArray& data); -}; - -} diff --git a/ui/widgets/info/omemo/omemo.cpp b/ui/widgets/info/omemo/omemo.cpp deleted file mode 100644 index baa7aa0..0000000 --- a/ui/widgets/info/omemo/omemo.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "omemo.h" -#include "ui_omemo.h" - -#include -constexpr uint8_t fingerprintLength = 32; - -UI::Omemo::Omemo(QWidget* parent): - QWidget(parent), - m_ui(new Ui::Omemo()), - deviceKeyDelegate(), - keysDelegate(), - unusedKeysDelegate(), - deviceKeyModel(), - keysModel(), - unusedKeysModel(), - contextMenu(new QMenu()) -{ - m_ui->setupUi(this); - - m_ui->deviceKeyView->setItemDelegate(&deviceKeyDelegate); - m_ui->deviceKeyView->setModel(&deviceKeyModel); - m_ui->keysView->setItemDelegate(&keysDelegate); - m_ui->keysView->setModel(&keysModel); - m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate); - m_ui->unusedKeysView->setModel(&unusedKeysModel); - - unusedVisibility(false); - deviceKeyVisibility(false); - - m_ui->keysView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_ui->keysView, &QWidget::customContextMenuRequested, this, &Omemo::onActiveKeysContextMenu); -} - -UI::Omemo::~Omemo() { - contextMenu->deleteLater(); -} - -void UI::Omemo::generateMockData() { - std::random_device rd; - std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); - for (int i = 0; i < 5; ++i) { - QByteArray fp(fingerprintLength, 0); - for (int i = 0; i < fingerprintLength; ++i) - fp[i] = dist(rd); - - Shared::KeyInfo info; - info.id = i; - if (i % 3 == 0) - info.label = QString("test_") + std::to_string(i).c_str(); - info.fingerPrint = fp; - if (i % 2 == 0) - info.lastInteraction = QDateTime::currentDateTime(); - - keysModel.addKey(info); - } -} - -void UI::Omemo::setData(const std::list& keys) { - keysModel.clear(); - unusedKeysModel.clear(); - deviceKeyModel.clear(); - for (const Shared::KeyInfo& key : keys) { - if (key.currentDevice) - deviceKeyModel.addKey(key); - else - keysModel.addKey(key); - } - - unusedVisibility(unusedKeysModel.rowCount() > 0); - deviceKeyVisibility(deviceKeyModel.rowCount() > 0); -} - -void UI::Omemo::fillData(std::list& out) { - deviceKeyModel.finalKeys(out); - keysModel.finalKeys(out); - unusedKeysModel.finalKeys(out); -} - -const QString UI::Omemo::title() const { - return m_ui->OMEMOHeading->text(); -} - -void UI::Omemo::onActiveKeysContextMenu(const QPoint& pos) { - contextMenu->clear(); - QModelIndex index = m_ui->keysView->indexAt(pos); - if (index.isValid()) { - QVariant dirtyV = index.data(Models::Keys::Dirty); - if (dirtyV.isValid() && dirtyV.toBool()) { - QAction* rev = contextMenu->addAction(Shared::icon("clean"), tr("Revert changes")); - connect(rev, &QAction::triggered, std::bind(&Models::Keys::revertKey, &keysModel, index.row())); - } - - QVariant levelV = index.data(Models::Keys::TrustLevel); - if (levelV.isValid()) { - Shared::TrustLevel level = static_cast(levelV.toUInt()); - if (level == Shared::TrustLevel::undecided || - level == Shared::TrustLevel::automaticallyDistrusted || - level == Shared::TrustLevel::manuallyDistrusted - ) { - QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Trust")); - connect(rev, &QAction::triggered, - std::bind(&Models::Keys::setTrustLevel, &keysModel, - index.row(), Shared::TrustLevel::manuallyTrusted - ) - ); - } - - if (level == Shared::TrustLevel::undecided || - level == Shared::TrustLevel::automaticallyTrusted || - level == Shared::TrustLevel::manuallyTrusted || - level == Shared::TrustLevel::authenticated - ) { - QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Distrust")); - connect(rev, &QAction::triggered, - std::bind(&Models::Keys::setTrustLevel, &keysModel, - index.row(), Shared::TrustLevel::manuallyDistrusted - ) - ); - } - } - } - - contextMenu->popup(m_ui->keysView->viewport()->mapToGlobal(pos)); -} - -void UI::Omemo::deviceKeyVisibility(bool visible) { - m_ui->deviceKeyHeading->setVisible(visible); - m_ui->deviceKeyline->setVisible(visible); - m_ui->deviceKeyView->setVisible(visible); -} - -void UI::Omemo::unusedVisibility(bool visible) { - m_ui->unusedKeysView->setVisible(visible); - m_ui->unusedKeysHeading->setVisible(visible); - m_ui->line->setVisible(visible); -} diff --git a/ui/widgets/info/omemo/omemo.h b/ui/widgets/info/omemo/omemo.h deleted file mode 100644 index 64c2486..0000000 --- a/ui/widgets/info/omemo/omemo.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include -#include -#include - -#include "ui/models/info/omemo/keys.h" -#include "keydelegate.h" -#include "shared/icons.h" -#include "shared/keyinfo.h" - -namespace UI { -namespace Ui { -class Omemo; -} - -class Omemo : public QWidget { - Q_OBJECT -public: - Omemo(QWidget* parent = nullptr); - ~Omemo(); - - void setData(const std::list& keys); - void fillData(std::list& out); - const QString title() const; - -private slots: - void onActiveKeysContextMenu(const QPoint& pos); - -private: - void generateMockData(); - void unusedVisibility(bool visible); - void deviceKeyVisibility(bool visible); - -private: - QScopedPointer m_ui; - UI::KeyDelegate deviceKeyDelegate; - UI::KeyDelegate keysDelegate; - UI::KeyDelegate unusedKeysDelegate; - Models::Keys deviceKeyModel; - Models::Keys keysModel; - Models::Keys unusedKeysModel; - QMenu* contextMenu; -}; -} diff --git a/ui/widgets/info/omemo/omemo.ui b/ui/widgets/info/omemo/omemo.ui deleted file mode 100644 index 64361f9..0000000 --- a/ui/widgets/info/omemo/omemo.ui +++ /dev/null @@ -1,240 +0,0 @@ - - - UI::Omemo - - - - 0 - 0 - 473 - 657 - - - - - 0 - - - 0 - - - 6 - - - 0 - - - 0 - - - - - - 24 - 75 - true - - - - OMEMO - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - true - - - - - 0 - 0 - 473 - 592 - - - - - - - - 16 - 75 - true - - - - Unused keys - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - QAbstractScrollArea::AdjustToContents - - - false - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - - - - - - 0 - 0 - - - - QAbstractScrollArea::AdjustToContents - - - false - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - - - - - - 16 - 75 - true - - - - Device key - - - - - - - - 16 - 75 - true - - - - Active keys - - - - - - - - 0 - 0 - - - - QAbstractScrollArea::AdjustToContents - - - false - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - ExpandingList - QListView -
ui/utils/expandinglist.h
- 1 -
-
- - -
diff --git a/ui/widgets/joinconference.cpp b/ui/widgets/joinconference.cpp index 890e4e2..648de25 100644 --- a/ui/widgets/joinconference.cpp +++ b/ui/widgets/joinconference.cpp @@ -26,10 +26,10 @@ JoinConference::JoinConference(const Models::Accounts* accounts, QWidget* parent m_ui(new Ui::JoinConference()) { m_ui->setupUi ( this ); - std::deque names = accounts->getActiveNames(); + std::deque names = accounts->getNames(); - for (const QString& name : names) { - m_ui->account->addItem(name); + for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { + m_ui->account->addItem(*i); } m_ui->account->setCurrentIndex(0); @@ -40,11 +40,12 @@ JoinConference::JoinConference(const QString& acc, const Models::Accounts* accou m_ui(new Ui::JoinConference()) { m_ui->setupUi ( this ); - std::deque names = accounts->getActiveNames(); + std::deque names = accounts->getNames(); int index = 0; bool found = false; - for (const QString& name : names) { + for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { + const QString& name = *i; m_ui->account->addItem(name); if (!found) { if (name == acc) { diff --git a/ui/widgets/messageline/CMakeLists.txt b/ui/widgets/messageline/CMakeLists.txt index 1524509..7cace9d 100644 --- a/ui/widgets/messageline/CMakeLists.txt +++ b/ui/widgets/messageline/CMakeLists.txt @@ -1,17 +1,14 @@ -set(SOURCE_FILES - messagedelegate.cpp - preview.cpp - messagefeed.cpp - feedview.cpp -) - -set(HEADER_FILES - messagedelegate.h - preview.h - messagefeed.h - feedview.h -) - target_sources(squawk PRIVATE - ${SOURCE_FILES} -) + messagedelegate.cpp + messagedelegate.h + #messageline.cpp + #messageline.h + preview.cpp + preview.h + messagefeed.cpp + messagefeed.h + feedview.cpp + feedview.h + #message.cpp + #message.h + ) diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 29ec9c6..7bdfb9e 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include #include "messagedelegate.h" @@ -45,20 +43,13 @@ FeedView::FeedView(QWidget* parent): QAbstractItemView(parent), hints(), vo(0), - elementMargin(0), specialDelegate(false), specialModel(false), clearWidgetsMode(false), modelState(Models::MessageFeed::complete), progress(), - dividerFont(Shared::Global::getInstance()->titleFont), - dividerMetrics(Shared::Global::getInstance()->titleFontMetrics), - mousePressed(false), - dragging(false), - hovered(Shared::Hover::nothing), - dragStartPoint(), - dragEndPoint(), - selectedText() + dividerFont(), + dividerMetrics(dividerFont) { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -68,29 +59,46 @@ FeedView::FeedView(QWidget* parent): progress.setParent(viewport()); progress.resize(progressSize, progressSize); + + dividerFont = getFont(); + dividerFont.setBold(true); + float ndps = dividerFont.pointSizeF(); + if (ndps != -1) { + dividerFont.setPointSizeF(ndps * 1.2); + } else { + dividerFont.setPointSize(dividerFont.pointSize() + 2); + } } -FeedView::~FeedView() {} +FeedView::~FeedView() +{ +} -QModelIndex FeedView::indexAt(const QPoint& point) const { +QModelIndex FeedView::indexAt(const QPoint& point) const +{ int32_t vh = viewport()->height(); uint32_t y = vh - point.y() + vo; for (std::deque::size_type i = 0; i < hints.size(); ++i) { const Hint& hint = hints[i]; if (y <= hint.offset + hint.height) { - if (y > hint.offset) + if (y > hint.offset) { return model()->index(i, 0, rootIndex()); - else + } else { break; + } } } return QModelIndex(); } +void FeedView::scrollTo(const QModelIndex& index, QAbstractItemView::ScrollHint hint) +{ +} -QRect FeedView::visualRect(const QModelIndex& index) const { +QRect FeedView::visualRect(const QModelIndex& index) const +{ unsigned int row = index.row(); if (!index.isValid() || row >= hints.size()) { qDebug() << "visualRect for" << row; @@ -98,28 +106,48 @@ QRect FeedView::visualRect(const QModelIndex& index) const { } else { const Hint& hint = hints.at(row); const QWidget* vp = viewport(); - return QRect(hint.x, vp->height() - hint.height - hint.offset + vo, hint.width, hint.height); + return QRect(0, vp->height() - hint.height - hint.offset + vo, vp->width(), hint.height); } } -QString FeedView::getSelectedText() const{return selectedText;} +int FeedView::horizontalOffset() const +{ + return 0; +} -//TODO!!! -void FeedView::scrollTo(const QModelIndex& index, QAbstractItemView::ScrollHint hint) {} -int FeedView::horizontalOffset() const {return 0;} -bool FeedView::isIndexHidden(const QModelIndex& index) const{return false;} -QModelIndex FeedView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) {return QModelIndex();} -void FeedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) {} -int FeedView::verticalOffset() const {return vo;} -QRegion FeedView::visualRegionForSelection(const QItemSelection& selection) const {return QRegion();} +bool FeedView::isIndexHidden(const QModelIndex& index) const +{ + return false; +} -void FeedView::rowsInserted(const QModelIndex& parent, int start, int end){ +QModelIndex FeedView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + return QModelIndex(); +} + +void FeedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) +{ +} + +int FeedView::verticalOffset() const +{ + return vo; +} + +QRegion FeedView::visualRegionForSelection(const QItemSelection& selection) const +{ + return QRegion(); +} + +void FeedView::rowsInserted(const QModelIndex& parent, int start, int end) +{ QAbstractItemView::rowsInserted(parent, start, end); scheduleDelayedItemsLayout(); } -void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { +void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) +{ if (specialDelegate) { for (int role : roles) { if (geometryChangingRoles.count(role) != 0) { @@ -131,25 +159,15 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom QAbstractItemView::dataChanged(topLeft, bottomRight, roles); } -void FeedView::updateGeometries() { - //qDebug() << "updateGeometries"; - const QAbstractItemModel* m = model(); - if (m == nullptr) - return QAbstractItemView::updateGeometries(); - +void FeedView::updateGeometries() +{ + qDebug() << "updateGeometries"; QScrollBar* bar = verticalScrollBar(); + const QStyle* st = style(); - + const QAbstractItemModel* m = model(); QSize layoutBounds = maximumViewportSize(); - - - QStyleOptionViewItem option; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - initViewItemOption(&option); -#else - option = viewOptions(); -#endif - + QStyleOptionViewItem option = viewOptions(); option.rect.setHeight(maxMessageHeight); option.rect.setWidth(layoutBounds.width()); int frameAroundContents = 0; @@ -166,11 +184,13 @@ void FeedView::updateGeometries() { vo = 0; } else { int verticalMargin = 0; - if (st->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) + if (st->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { frameAroundContents = st->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; + } - if (verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) + if (verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) { verticalMargin = verticalScrollBarExtent + frameAroundContents; + } layoutBounds.rwidth() -= verticalMargin; @@ -178,45 +198,30 @@ void FeedView::updateGeometries() { option.rect.setWidth(layoutBounds.width()); hints.clear(); - uint32_t previousOffset = elementMargin; + uint32_t previousOffset = 0; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { QModelIndex index = m->index(i, 0, rootIndex()); QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); if (i > 0) { - if (currentDate.daysTo(lastDate) > 0) + if (currentDate.daysTo(lastDate) > 0) { previousOffset += dividerMetrics.height() + dateDeviderMargin * 2; - else - previousOffset += elementMargin; + } } lastDate = currentDate; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); -#else - QSize messageSize = itemDelegate(index)->sizeHint(option, index); -#endif - uint32_t offsetX(0); - if (specialDelegate) { - if (index.data(Models::MessageFeed::SentByMe).toBool()) - offsetX = layoutBounds.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; - else - offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; - } - + int height = itemDelegate(index)->sizeHint(option, index).height(); hints.emplace_back(Hint({ false, previousOffset, - static_cast(messageSize.height()), - static_cast(messageSize.width()), - offsetX + static_cast(height) })); - previousOffset += messageSize.height(); + previousOffset += height; } - int totalHeight = previousOffset - layoutBounds.height() + dividerMetrics.height() + dateDeviderMargin * 2; - if (modelState != Models::MessageFeed::complete) + int totalHeight = previousOffset - layoutBounds.height(); + if (modelState != Models::MessageFeed::complete) { totalHeight += progressSize; - + } vo = qMax(qMin(vo, totalHeight), 0); bar->setRange(0, totalHeight); bar->setPageStep(layoutBounds.height()); @@ -225,59 +230,48 @@ void FeedView::updateGeometries() { positionProgress(); - if (specialDelegate) + if (specialDelegate) { clearWidgetsMode = true; + } + QAbstractItemView::updateGeometries(); } -bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) { - uint32_t previousOffset = elementMargin; +bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) +{ + uint32_t previousOffset = 0; + bool success = true; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { QModelIndex index = m->index(i, 0, rootIndex()); QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); if (i > 0) { - if (currentDate.daysTo(lastDate) > 0) + if (currentDate.daysTo(lastDate) > 0) { previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); - else - previousOffset += elementMargin; + } } lastDate = currentDate; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); -#else - QSize messageSize = itemDelegate(index)->sizeHint(option, index); -#endif + int height = itemDelegate(index)->sizeHint(option, index).height(); - if (previousOffset + messageSize.height() + elementMargin > totalHeight) - return false; - - uint32_t offsetX(0); - if (specialDelegate) { - if (index.data(Models::MessageFeed::SentByMe).toBool()) - offsetX = option.rect.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; - else - offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; + if (previousOffset + height > totalHeight) { + success = false; + break; } hints.emplace_back(Hint({ false, previousOffset, - static_cast(messageSize.height()), - static_cast(messageSize.width()), - offsetX + static_cast(height) })); - previousOffset += messageSize.height(); + previousOffset += height; } - - previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); - if (previousOffset > totalHeight) - return false; - return true; + return success; } -void FeedView::paintEvent(QPaintEvent* event) { + +void FeedView::paintEvent(QPaintEvent* event) +{ //qDebug() << "paint" << event->rect(); const QAbstractItemModel* m = model(); QWidget* vp = viewport(); @@ -292,38 +286,33 @@ void FeedView::paintEvent(QPaintEvent* event) { const Hint& hint = hints[i]; int32_t relativeY1 = vph - hint.offset - hint.height; if (!inZone) { - if (y2 > relativeY1) + if (y2 > relativeY1) { inZone = true; + } } - if (inZone) + if (inZone) { toRener.emplace_back(m->index(i, 0, rootIndex())); - + } if (y1 > relativeY1) { inZone = false; break; } } - QStyleOptionViewItem option; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - initViewItemOption(&option); -#else - option = viewOptions(); -#endif - QPainter painter(vp); + QStyleOptionViewItem option = viewOptions(); option.features = QStyleOptionViewItem::WrapText; QPoint cursor = vp->mapFromGlobal(QCursor::pos()); if (specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); - if (clearWidgetsMode) + if (clearWidgetsMode) { del->beginClearWidgets(); + } } QDateTime lastDate; bool first = true; - QRect viewportRect = vp->rect(); for (const QModelIndex& index : toRener) { QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); option.rect = visualRect(index); @@ -331,39 +320,38 @@ void FeedView::paintEvent(QPaintEvent* event) { int ind = index.row() - 1; if (ind > 0) { QDateTime underDate = m->index(ind, 0, rootIndex()).data(Models::MessageFeed::Date).toDateTime(); - if (currentDate.daysTo(underDate) > 0) + if (currentDate.daysTo(underDate) > 0) { drawDateDevider(option.rect.bottom(), underDate, painter); + } } first = false; } - QRect stripe = option.rect; - stripe.setLeft(0); - stripe.setWidth(viewportRect.width()); - bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor); + bool mouseOver = option.rect.contains(cursor) && vp->rect().contains(cursor); option.state.setFlag(QStyle::State_MouseOver, mouseOver); + itemDelegate(index)->paint(&painter, option, index); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - itemDelegateForIndex(index)->paint(&painter, option, index); -#else - itemDelegate(index)->paint(&painter, option, index); -#endif - - if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) + if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) { drawDateDevider(option.rect.bottom(), lastDate, painter); - + } lastDate = currentDate; } - if (!lastDate.isNull() && inZone) //if after drawing all messages there is still space - drawDateDevider(option.rect.top() - dateDeviderMargin * 2 - dividerMetrics.height(), lastDate, painter); + if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space + drawDateDevider(option.rect.bottom(), lastDate, painter); + } if (clearWidgetsMode && specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); del->endClearWidgets(); clearWidgetsMode = false; } + + if (event->rect().height() == vp->height()) { + // draw the blurred drop shadow... + } } -void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter) { +void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter) +{ int divisionHeight = dateDeviderMargin * 2 + dividerMetrics.height(); QRect r(QPoint(0, top), QSize(viewport()->width(), divisionHeight)); painter.save(); @@ -372,178 +360,42 @@ void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter painter.restore(); } -void FeedView::verticalScrollbarValueChanged(int value) { +void FeedView::verticalScrollbarValueChanged(int value) +{ vo = verticalScrollBar()->maximum() - value; positionProgress(); - if (specialDelegate) + if (specialDelegate) { clearWidgetsMode = true; + } - if (modelState == Models::MessageFeed::incomplete && value < progressSize) + if (modelState == Models::MessageFeed::incomplete && value < progressSize) { model()->fetchMore(rootIndex()); + } QAbstractItemView::verticalScrollbarValueChanged(vo); } -void FeedView::setAnchorHovered(Shared::Hover type) { - if (hovered != type) { - hovered = type; - switch (hovered) { - case Shared::Hover::nothing: - setCursor(Qt::ArrowCursor); - break; - case Shared::Hover::text: - setCursor(Qt::IBeamCursor); - break; - case Shared::Hover::anchor: - setCursor(Qt::PointingHandCursor); - break; - } - } -} - -void FeedView::mouseMoveEvent(QMouseEvent* event) { - if (!isVisible()) +void FeedView::mouseMoveEvent(QMouseEvent* event) +{ + if (!isVisible()) { return; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - dragEndPoint = event->position().toPoint(); -#else - dragEndPoint = event->localPos().toPoint(); -#endif - - if (mousePressed) { - QPoint distance = dragStartPoint - dragEndPoint; - if (distance.manhattanLength() > 5) - dragging = true; } QAbstractItemView::mouseMoveEvent(event); - - if (specialDelegate) { - MessageDelegate* del = static_cast(itemDelegate()); - if (dragging) { - QModelIndex index = indexAt(dragStartPoint); - if (index.isValid()) { - QRect rect = visualRect(index); - if (rect.contains(dragStartPoint)) { - QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect); - if (selectedText != selected) { - selectedText = selected; - setDirtyRegion(rect); - } - } - } - } else { - QModelIndex index = indexAt(dragEndPoint); - if (index.isValid()) { - QRect rect = visualRect(index); - if (rect.contains(dragEndPoint)) - setAnchorHovered(del->hoverType(dragEndPoint, index, rect)); - else - setAnchorHovered(Shared::Hover::nothing); - } else { - setAnchorHovered(Shared::Hover::nothing); - } - } - } } -void FeedView::mousePressEvent(QMouseEvent* event) { - QAbstractItemView::mousePressEvent(event); - - mousePressed = event->button() == Qt::LeftButton; - if (mousePressed) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - dragStartPoint = event->position().toPoint(); -#else - dragStartPoint = event->localPos().toPoint(); -#endif - if (specialDelegate && specialModel) { - MessageDelegate* del = static_cast(itemDelegate()); - QString lastSelectedId = del->clearSelection(); - if (lastSelectedId.size()) { - Models::MessageFeed* feed = static_cast(model()); - QModelIndex index = feed->modelIndexById(lastSelectedId); - if (index.isValid()) - setDirtyRegion(visualRect(index)); - } - } - } -} - -void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { - QAbstractItemView::mouseDoubleClickEvent(event); - mousePressed = event->button() == Qt::LeftButton; - if (mousePressed) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - dragStartPoint = event->position().toPoint(); -#else - dragStartPoint = event->localPos().toPoint(); -#endif - if (specialDelegate && specialModel) { - MessageDelegate* del = static_cast(itemDelegate()); - QString lastSelectedId = del->clearSelection(); - selectedText = ""; - if (lastSelectedId.size()) { - Models::MessageFeed* feed = static_cast(model()); - QModelIndex index = feed->modelIndexById(lastSelectedId); - if (index.isValid()) - setDirtyRegion(visualRect(index)); - } - - QModelIndex index = indexAt(dragStartPoint); - QRect rect = visualRect(index); - if (rect.contains(dragStartPoint)) { - selectedText = del->leftDoubleClick(dragStartPoint, index, rect); - if (selectedText.size() > 0) - setDirtyRegion(rect); - } - } - } -} - -void FeedView::mouseReleaseEvent(QMouseEvent* event) { - QAbstractItemView::mouseReleaseEvent(event); - - if (mousePressed) { - if (!dragging && specialDelegate) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QPoint point = event->position().toPoint(); -#else - QPoint point = event->localPos().toPoint(); -#endif - QModelIndex index = indexAt(point); - if (index.isValid()) { - QRect rect = visualRect(index); - MessageDelegate* del = static_cast(itemDelegate()); - if (rect.contains(point)) - del->leftClick(point, index, rect); - } - } - dragging = false; - mousePressed = false; - } -} - -void FeedView::keyPressEvent(QKeyEvent* event) { - QKeyEvent *key_event = static_cast(event); - if (key_event->matches(QKeySequence::Copy)) { - if (selectedText.size() > 0) { - QClipboard* cb = QApplication::clipboard(); - cb->setText(selectedText); - } - } -} - -void FeedView::resizeEvent(QResizeEvent* event) { +void FeedView::resizeEvent(QResizeEvent* event) +{ QAbstractItemView::resizeEvent(event); positionProgress(); emit resized(); } -void FeedView::positionProgress() { +void FeedView::positionProgress() +{ QSize layoutBounds = maximumViewportSize(); int progressPosition = layoutBounds.height() - progressSize; std::deque::size_type size = hints.size(); @@ -557,7 +409,13 @@ void FeedView::positionProgress() { progress.move((width() - progressSize) / 2, progressPosition); } -void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) { +QFont FeedView::getFont() const +{ + return viewOptions().font; +} + +void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) +{ if (specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); disconnect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); @@ -569,17 +427,15 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) { MessageDelegate* del = dynamic_cast(delegate); if (del) { specialDelegate = true; - elementMargin = MessageDelegate::margin; connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath); - connect(del, &MessageDelegate::openLink, &QDesktopServices::openUrl); } else { specialDelegate = false; - elementMargin = 0; } } -void FeedView::setModel(QAbstractItemModel* p_model) { +void FeedView::setModel(QAbstractItemModel* p_model) +{ if (specialModel) { Models::MessageFeed* feed = static_cast(model()); disconnect(feed, &Models::MessageFeed::syncStateChange, this, &FeedView::onModelSyncStateChange); @@ -598,21 +454,24 @@ void FeedView::setModel(QAbstractItemModel* p_model) { } } -void FeedView::onMessageButtonPushed(const QString& messageId) { +void FeedView::onMessageButtonPushed(const QString& messageId) +{ if (specialModel) { Models::MessageFeed* feed = static_cast(model()); feed->downloadAttachment(messageId); } } -void FeedView::onMessageInvalidPath(const QString& messageId) { +void FeedView::onMessageInvalidPath(const QString& messageId) +{ if (specialModel) { Models::MessageFeed* feed = static_cast(model()); feed->reportLocalPathInvalid(messageId); } } -void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) { +void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) +{ bool needToUpdateGeometry = false; if (modelState != state) { if (state == Models::MessageFeed::complete || modelState == Models::MessageFeed::complete) { diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 19645c0..5e08946 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -16,20 +16,22 @@ * along with this program. If not, see . */ -#pragma once +#ifndef FEEDVIEW_H +#define FEEDVIEW_H #include -#include -#include #include #include #include #include -#include -class FeedView : public QAbstractItemView { +/** + * @todo write docs + */ +class FeedView : public QAbstractItemView +{ Q_OBJECT public: FeedView(QWidget* parent = nullptr); @@ -45,7 +47,7 @@ public: void setItemDelegate(QAbstractItemDelegate* delegate); void setModel(QAbstractItemModel * model) override; - QString getSelectedText() const; + QFont getFont() const; signals: void resized(); @@ -66,43 +68,31 @@ protected: void paintEvent(QPaintEvent * event) override; void updateGeometries() override; void mouseMoveEvent(QMouseEvent * event) override; - void mousePressEvent(QMouseEvent * event) override; - void mouseReleaseEvent(QMouseEvent * event) override; - void mouseDoubleClickEvent(QMouseEvent * event) override; - void keyPressEvent(QKeyEvent * event) override; void resizeEvent(QResizeEvent * event) override; private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); - void setAnchorHovered(Shared::Hover type); private: struct Hint { bool dirty; uint32_t offset; uint32_t height; - uint32_t width; - uint32_t x; }; std::deque hints; int vo; - int elementMargin; bool specialDelegate; bool specialModel; bool clearWidgetsMode; Models::MessageFeed::SyncState modelState; Progress progress; - const QFont& dividerFont; - const QFontMetrics& dividerMetrics; - bool mousePressed; - bool dragging; - Shared::Hover hovered; - QPoint dragStartPoint; - QPoint dragEndPoint; - QString selectedText; + QFont dividerFont; + QFontMetrics dividerMetrics; static const std::set geometryChangingRoles; }; + +#endif //FEEDVIEW_H diff --git a/ui/widgets/messageline/message.cpp b/ui/widgets/messageline/message.cpp new file mode 100644 index 0000000..7a004bb --- /dev/null +++ b/ui/widgets/messageline/message.cpp @@ -0,0 +1,344 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "message.h" +#include +#include +#include +#include +#include + +Message::Message(const Shared::Message& source, bool p_outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent): + QWidget(parent), + outgoing(p_outgoing), + msg(source), + body(new QWidget()), + statusBar(new QWidget()), + bodyLayout(new QVBoxLayout(body)), + layout(new QHBoxLayout(this)), + date(new QLabel(msg.getTime().toLocalTime().toString())), + sender(new QLabel(p_sender)), + text(new QLabel()), + shadow(new QGraphicsDropShadowEffect()), + button(0), + file(0), + progress(0), + fileComment(new QLabel()), + statusIcon(0), + editedLabel(0), + avatar(new Image(avatarPath.size() == 0 ? Shared::iconPath("user", true) : avatarPath, 60)), + hasButton(false), + hasProgress(false), + hasFile(false), + commentAdded(false), + hasStatusIcon(false), + hasEditedLabel(false) +{ + setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(10, 5, 10, 5); + body->setBackgroundRole(QPalette::AlternateBase); + body->setAutoFillBackground(true); + + QString bd = Shared::processMessageBody(msg.getBody()); + text->setTextFormat(Qt::RichText); + text->setText(bd);; + text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); + text->setWordWrap(true); + text->setOpenExternalLinks(true); + if (bd.size() == 0) { + text->hide(); + } + + QFont dFont = date->font(); + dFont.setItalic(true); + dFont.setPointSize(dFont.pointSize() - 2); + date->setFont(dFont); + + QFont f; + f.setBold(true); + sender->setFont(f); + + bodyLayout->addWidget(sender); + bodyLayout->addWidget(text); + + shadow->setBlurRadius(10); + shadow->setXOffset(1); + shadow->setYOffset(1); + shadow->setColor(Qt::black); + body->setGraphicsEffect(shadow); + avatar->setMaximumHeight(60); + avatar->setMaximumWidth(60); + + statusBar->setContentsMargins(0, 0, 0, 0); + QHBoxLayout* statusLay = new QHBoxLayout(); + statusLay->setContentsMargins(0, 0, 0, 0); + statusBar->setLayout(statusLay); + + if (outgoing) { + sender->setAlignment(Qt::AlignRight); + date->setAlignment(Qt::AlignRight); + statusIcon = new QLabel(); + setState(); + statusLay->addWidget(statusIcon); + statusLay->addWidget(date); + layout->addStretch(); + layout->addWidget(body); + layout->addWidget(avatar); + hasStatusIcon = true; + } else { + layout->addWidget(avatar); + layout->addWidget(body); + layout->addStretch(); + statusLay->addWidget(date); + } + if (msg.getEdited()) { + setEdited(); + } + + bodyLayout->addWidget(statusBar); + layout->setAlignment(avatar, Qt::AlignTop); +} + +Message::~Message() +{ + if (!commentAdded) { + delete fileComment; + } + //delete body; //not sure if I should delete it here, it's probably already owned by the infrastructure and gonna die with the rest of the widget + //delete avatar; +} + +QString Message::getId() const +{ + return msg.getId(); +} + +QString Message::getSenderJid() const +{ + return msg.getFromJid(); +} + +QString Message::getSenderResource() const +{ + return msg.getFromResource(); +} + +QString Message::getFileUrl() const +{ + return msg.getOutOfBandUrl(); +} + +void Message::setSender(const QString& p_sender) +{ + sender->setText(p_sender); +} + +void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip) +{ + hideFile(); + hideProgress(); + if (!hasButton) { + hideComment(); + if (msg.getBody() == msg.getOutOfBandUrl()) { + text->setText(""); + text->hide(); + } + button = new QPushButton(icon, buttonText); + button->setToolTip(tooltip); + connect(button, &QPushButton::clicked, this, &Message::buttonClicked); + bodyLayout->insertWidget(2, button); + hasButton = true; + } +} + +void Message::setProgress(qreal value) +{ + hideFile(); + hideButton(); + if (!hasProgress) { + hideComment(); + if (msg.getBody() == msg.getOutOfBandUrl()) { + text->setText(""); + text->hide(); + } + progress = new QProgressBar(); + progress->setRange(0, 100); + bodyLayout->insertWidget(2, progress); + hasProgress = true; + } + progress->setValue(value * 100); +} + +void Message::showFile(const QString& path) +{ + hideButton(); + hideProgress(); + if (!hasFile) { + hideComment(); + if (msg.getBody() == msg.getOutOfBandUrl()) { + text->setText(""); + text->hide(); + } + QMimeDatabase db; + QMimeType type = db.mimeTypeForFile(path); + QStringList parts = type.name().split("/"); + QString big = parts.front(); + QFileInfo info(path); + if (big == "image") { + file = new Image(path); + } else { + file = new QLabel(); + file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50)); + file->setAlignment(Qt::AlignCenter); + showComment(info.fileName(), true); + } + file->setContextMenuPolicy(Qt::ActionsContextMenu); + QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file); + connect(openAction, &QAction::triggered, [path]() { //TODO need to get rid of this shame + QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + }); + file->addAction(openAction); + bodyLayout->insertWidget(2, file); + hasFile = true; + } +} + +void Message::hideComment() +{ + if (commentAdded) { + bodyLayout->removeWidget(fileComment); + fileComment->hide(); + fileComment->setWordWrap(false); + commentAdded = false; + } +} + +void Message::hideButton() +{ + if (hasButton) { + button->deleteLater(); + button = 0; + hasButton = false; + } +} + +void Message::hideFile() +{ + if (hasFile) { + file->deleteLater(); + file = 0; + hasFile = false; + } +} + +void Message::hideProgress() +{ + if (hasProgress) { + progress->deleteLater(); + progress = 0; + hasProgress = false;; + } +} +void Message::showComment(const QString& comment, bool wordWrap) +{ + if (!commentAdded) { + int index = 2; + if (hasFile) { + index++; + } + if (hasButton) { + index++; + } + if (hasProgress) { + index++; + } + bodyLayout->insertWidget(index, fileComment); + fileComment->show(); + commentAdded = true; + } + fileComment->setWordWrap(wordWrap); + fileComment->setText(comment); +} + +const Shared::Message & Message::getMessage() const +{ + return msg; +} + +void Message::setAvatarPath(const QString& p_path) +{ + if (p_path.size() == 0) { + avatar->setPath(Shared::iconPath("user", true)); + } else { + avatar->setPath(p_path); + } +} + +bool Message::change(const QMap& data) +{ + bool idChanged = msg.change(data); + + QString body = msg.getBody(); + QString bd = Shared::processMessageBody(body); + if (body.size() > 0) { + text->setText(bd); + text->show(); + } else { + text->setText(body); + text->hide(); + } + if (msg.getEdited()) { + setEdited(); + } + if (hasStatusIcon) { + setState(); + } + + + return idChanged; +} + +void Message::setEdited() +{ + if (!hasEditedLabel) { + editedLabel = new QLabel(); + hasEditedLabel = true; + QIcon q(Shared::icon("edit-rename")); + editedLabel->setPixmap(q.pixmap(12, 12)); + QHBoxLayout* statusLay = static_cast(statusBar->layout()); + statusLay->insertWidget(1, editedLabel); + } + editedLabel->setToolTip("Last time edited: " + msg.getLastModified().toLocalTime().toString() + + "\nOriginal message: " + msg.getOriginalBody()); +} + +void Message::setState() +{ + Shared::Message::State state = msg.getState(); + QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(state)])); + QString tt = Shared::Global::getName(state); + if (state == Shared::Message::State::error) { + QString errText = msg.getErrorText(); + if (errText.size() > 0) { + tt += ": " + errText; + } + } + statusIcon->setToolTip(tt); + statusIcon->setPixmap(q.pixmap(12, 12)); +} + diff --git a/ui/widgets/messageline/message.h b/ui/widgets/messageline/message.h new file mode 100644 index 0000000..eef93a1 --- /dev/null +++ b/ui/widgets/messageline/message.h @@ -0,0 +1,103 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shared/message.h" +#include "shared/icons.h" +#include "shared/global.h" +#include "shared/utils.h" +#include "resizer.h" +#include "image.h" + +/** + * @todo write docs + */ +class Message : public QWidget +{ + Q_OBJECT +public: + Message(const Shared::Message& source, bool outgoing, const QString& sender, const QString& avatarPath = "", QWidget* parent = nullptr); + ~Message(); + + void setSender(const QString& sender); + QString getId() const; + QString getSenderJid() const; + QString getSenderResource() const; + QString getFileUrl() const; + const Shared::Message& getMessage() const; + + void addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip = ""); + void showComment(const QString& comment, bool wordWrap = false); + void hideComment(); + void showFile(const QString& path); + void setProgress(qreal value); + void setAvatarPath(const QString& p_path); + bool change(const QMap& data); + + bool const outgoing; + +signals: + void buttonClicked(); + +private: + Shared::Message msg; + QWidget* body; + QWidget* statusBar; + QVBoxLayout* bodyLayout; + QHBoxLayout* layout; + QLabel* date; + QLabel* sender; + QLabel* text; + QGraphicsDropShadowEffect* shadow; + QPushButton* button; + QLabel* file; + QProgressBar* progress; + QLabel* fileComment; + QLabel* statusIcon; + QLabel* editedLabel; + Image* avatar; + bool hasButton; + bool hasProgress; + bool hasFile; + bool commentAdded; + bool hasStatusIcon; + bool hasEditedLabel; + +private: + void hideButton(); + void hideProgress(); + void hideFile(); + void setState(); + void setEdited(); +}; + +#endif // MESSAGE_H diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index b82a992..649230e 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -18,109 +18,144 @@ #include #include -#include #include #include -#include -#include -#include -#include #include "messagedelegate.h" #include "messagefeed.h" -#include "shared/defines.h" - +constexpr int avatarHeight = 50; +constexpr int margin = 6; constexpr int textMargin = 2; constexpr int statusIconSize = 16; -constexpr int bubbleMargin = 6; -constexpr int bubbleBorderRadius = 3; - -int MessageDelegate::avatarHeight(50); -int MessageDelegate::margin(6); - MessageDelegate::MessageDelegate(QObject* parent): QStyledItemDelegate(parent), - bodyFont(Shared::Global::getInstance()->defaultFont), - nickFont(Shared::Global::getInstance()->headerFont), - dateFont(Shared::Global::getInstance()->smallFont), - nickMetrics(Shared::Global::getInstance()->headerFontMetrics), - dateMetrics(Shared::Global::getInstance()->smallFontMetrics), - bodyRenderer(), + bodyFont(), + nickFont(), + dateFont(), + bodyMetrics(bodyFont), + nickMetrics(nickFont), + dateMetrics(dateFont), buttonHeight(0), - buttonWidth(0), barHeight(0), - buttons(), - bars(), - statusIcons(), - pencilIcons(), - encryptionIcons(), - previews(), - idsToKeep(), - clearingWidgets(false), - currentId(""), - selection(0, 0) + buttons(new std::map()), + bars(new std::map()), + statusIcons(new std::map()), + pencilIcons(new std::map()), + bodies(new std::map()), + previews(new std::map()), + idsToKeep(new std::set()), + clearingWidgets(false) { - bodyRenderer.setDocumentMargin(0); - bodyRenderer.setDefaultFont(bodyFont); - - QPushButton btn(QCoreApplication::translate("MessageLine", "Download")); + QPushButton btn; buttonHeight = btn.sizeHint().height(); - buttonWidth = btn.sizeHint().width(); QProgressBar bar; barHeight = bar.sizeHint().height(); } -MessageDelegate::~MessageDelegate() { - for (const std::pair& pair: buttons) +MessageDelegate::~MessageDelegate() +{ + for (const std::pair& pair: *buttons){ delete pair.second; + } - for (const std::pair& pair: bars) + for (const std::pair& pair: *bars){ delete pair.second; + } - for (const std::pair& pair: statusIcons) + for (const std::pair& pair: *statusIcons){ delete pair.second; + } - for (const std::pair& pair: pencilIcons) - delete pair.second; - - for (const std::pair& pair: encryptionIcons) + for (const std::pair& pair: *pencilIcons){ delete pair.second; + } - for (const std::pair& pair: previews) + for (const std::pair& pair: *bodies){ delete pair.second; + } + + for (const std::pair& pair: *previews){ + delete pair.second; + } + + delete statusIcons; + delete pencilIcons; + delete idsToKeep; + delete buttons; + delete bars; + delete bodies; + delete previews; } -void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { +void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ QVariant vi = index.data(Models::MessageFeed::Bulk); - if (!vi.isValid()) + if (!vi.isValid()) { return; - + } Models::FeedItem data = qvariant_cast(vi); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); - paintBubble(data, painter, option); - bool ntds = needToDrawSender(index, data); - if (ntds || option.rect.y() < 1) - paintAvatar(data, index, option, painter); + if (option.state & QStyle::State_MouseOver) { + painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); + } + + QIcon icon(data.avatar); + + if (data.sentByMe) { + painter->drawPixmap(option.rect.width() - avatarHeight - margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + } else { + painter->drawPixmap(margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + } QStyleOptionViewItem opt = option; - opt.rect = option.rect.adjusted(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2); - if (!data.sentByMe) + QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2); + if (!data.sentByMe) { opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop; - else + messageRect.adjust(avatarHeight + margin, 0, avatarHeight + margin, 0); + } else { opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; - - QRect rect; - if (ntds) { - painter->setFont(nickFont); - painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); - opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); } - + opt.rect = messageRect; + + QSize messageSize(0, 0); + QSize bodySize(0, 0); + if (data.text.size() > 0) { + messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); + bodySize = messageSize; + } + messageSize.rheight() += nickMetrics.lineSpacing(); + messageSize.rheight() += dateMetrics.height(); + QString dateString = data.date.toLocalTime().toString("hh:mm"); + if (messageSize.width() < opt.rect.width()) { + QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); + if (senderSize.width() > messageSize.width()) { + messageSize.setWidth(senderSize.width()); + } + QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size(); + int addition = 0; + + if (data.correction.corrected) { + addition += margin + statusIconSize; + } + if (data.sentByMe) { + addition += margin + statusIconSize; + } + if (dateSize.width() + addition > messageSize.width()) { + messageSize.setWidth(dateSize.width() + addition); + } + } else { + messageSize.setWidth(opt.rect.width()); + } + + QRect rect; + painter->setFont(nickFont); + painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); + opt.rect.adjust(0, rect.height() + textMargin, 0, 0); painter->save(); switch (data.attach.state) { case Models::none: @@ -128,7 +163,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio break; //but it's a possible performance problem case Models::uploading: paintPreview(data, painter, opt); - [[fallthrough]]; case Models::downloading: paintBar(getBar(data), painter, data.sentByMe, opt); break; @@ -155,389 +189,164 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } painter->restore(); + int messageLeft = INT16_MAX; + int messageRight = opt.rect.x() + messageSize.width(); QWidget* vp = static_cast(painter->device()); - paintBody(data, painter, opt); + if (data.text.size() > 0) { + QLabel* body = getBody(data); + body->setParent(vp); + body->setMaximumWidth(bodySize.width()); + body->setMinimumWidth(bodySize.width()); + body->setMinimumHeight(bodySize.height()); + body->setMaximumHeight(bodySize.height()); + body->setAlignment(opt.displayAlignment); + messageLeft = opt.rect.x(); + if (data.sentByMe) { + messageLeft = opt.rect.topRight().x() - bodySize.width(); + } + body->move(messageLeft, opt.rect.y()); + body->show(); + opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0); + } painter->setFont(dateFont); QColor q = painter->pen().color(); - QString dateString = data.date.toLocalTime().toString("hh:mm"); q.setAlpha(180); painter->setPen(q); painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect); int currentY = opt.rect.y(); - int statusOffset = statusIconSize; if (data.sentByMe) { QLabel* statusIcon = getStatusIcon(data); statusIcon->setParent(vp); - statusIcon->move(opt.rect.left(), currentY); + statusIcon->move(opt.rect.topRight().x() - messageSize.width(), currentY); statusIcon->show(); opt.rect.adjust(0, statusIconSize + textMargin, 0, 0); - statusOffset = statusIconSize + margin; } if (data.correction.corrected) { QLabel* pencilIcon = getPencilIcon(data); + pencilIcon->setParent(vp); - if (data.sentByMe) - pencilIcon->move(opt.rect.left() + statusOffset, currentY); - else - pencilIcon->move(opt.rect.right() - statusOffset, currentY); - - pencilIcon->show(); - statusOffset += statusIconSize + margin; - } else { - std::map::const_iterator itr = pencilIcons.find(data.id); - if (itr != pencilIcons.end()) { - delete itr->second; - pencilIcons.erase(itr); - } - } - - if (data.encryption != Shared::EncryptionProtocol::none) { - QLabel* shieldIcon = getEncryptionIcon(data); - shieldIcon->setParent(vp); - if (data.sentByMe) - shieldIcon->move(opt.rect.left() + statusOffset, currentY); - else - shieldIcon->move(opt.rect.right() - statusOffset, currentY); - - shieldIcon->show(); - statusOffset += statusIconSize + margin; - } - - painter->restore(); - - if (clearingWidgets) - idsToKeep.insert(data.id); -} - -void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const { - painter->save(); - if (data.sentByMe) - painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight)); - else - painter->setBrush(option.palette.brush(QPalette::Window)); - - painter->setPen(Qt::NoPen); - painter->drawRoundedRect(option.rect, bubbleBorderRadius, bubbleBorderRadius); - painter->restore(); -} - - -void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const { - int currentRow = index.row(); - int y = option.rect.y(); - bool firstAttempt = true; - QIcon icon(data.avatar); - while (y < 0 && currentRow > 0) { - QRect rect; - if (firstAttempt) { - firstAttempt = false; - rect = option.rect; + if (data.sentByMe) { + pencilIcon->move(opt.rect.topRight().x() - messageSize.width() + statusIconSize + margin, currentY); } else { - QModelIndex ci = index.siblingAtRow(currentRow); - if ( - (ci.data(Models::MessageFeed::Sender).toString() != data.sender) || - (ci.data(Models::MessageFeed::Date).toDateTime().daysTo(data.date) != 0) - ) { - break; - } - //TODO this is really bad, but for now I have no idea how else can I access the view; - const QAbstractItemView* view = static_cast(option.styleObject); - rect = view->visualRect(ci); + pencilIcon->move(messageRight - statusIconSize - margin, currentY); } - y = std::min(0, rect.bottom() - margin - avatarHeight); - --currentRow; - } - - QPixmap pixmap = icon.pixmap(avatarHeight, avatarHeight); - QPainterPath path; - int ax; - - if (data.sentByMe) - ax = option.rect.x() + option.rect.width() + margin; - else - ax = margin; - - path.addEllipse(ax, y + margin / 2, avatarHeight, avatarHeight); - painter->save(); - painter->setClipPath(path); - painter->drawPixmap(ax, y + margin / 2, pixmap); - painter->restore(); -} - -bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const { - return (option.rect.y() < 1) || needToDrawSender(index, data); -} - -bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const -{ - if (index.row() == index.model()->rowCount() - 1) { - return true; + pencilIcon->show(); } else { - QModelIndex prevIndex = index.siblingAtRow(index.row() + 1); - - return (prevIndex.data(Models::MessageFeed::Sender).toString() != data.sender) || - (prevIndex.data(Models::MessageFeed::Date).toDateTime().daysTo(data.date) != 0); + std::map::const_iterator itr = pencilIcons->find(data.id); + if (itr != pencilIcons->end()) { + delete itr->second; + pencilIcons->erase(itr); + } + } + + painter->restore(); + + if (clearingWidgets) { + idsToKeep->insert(data.id); } } -QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - QRect messageRect = option.rect.adjusted(bubbleMargin, margin / 2 + bubbleMargin, -(avatarHeight + 3 * margin + bubbleMargin), -(margin + bubbleMargin) / 2); +QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QRect messageRect = option.rect.adjusted(0, margin / 2, -(avatarHeight + 3 * margin), -margin / 2); QStyleOptionViewItem opt = option; opt.rect = messageRect; - QVariant vi = index.data(Models::MessageFeed::Bulk); - Models::FeedItem data = qvariant_cast(vi); + QVariant va = index.data(Models::MessageFeed::Attach); + Models::Attachment attach = qvariant_cast(va); + QString body = index.data(Models::MessageFeed::Text).toString(); QSize messageSize(0, 0); - if (data.text.size() > 0) { - bodyRenderer.setPlainText(data.text); - bodyRenderer.setTextWidth(messageRect.size().width()); - - QSizeF size = bodyRenderer.size(); - size.setWidth(bodyRenderer.idealWidth()); - messageSize = QSize(std::ceil(size.width()), std::ceil(size.height())); + if (body.size() > 0) { + messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, body).size(); messageSize.rheight() += textMargin; } - switch (data.attach.state) { + switch (attach.state) { case Models::none: break; case Models::uploading: - messageSize.rheight() += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect).height() + textMargin; - [[fallthrough]]; + messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; case Models::downloading: messageSize.rheight() += barHeight + textMargin; - messageSize.setWidth(messageRect.width()); break; case Models::remote: messageSize.rheight() += buttonHeight + textMargin; - messageSize.setWidth(std::max(messageSize.width(), buttonWidth)); break; case Models::ready: - case Models::local: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect); - messageSize.rheight() += aSize.height() + textMargin; - messageSize.setWidth(std::max(messageSize.width(), aSize.width())); - } + case Models::local: + messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; break; - case Models::errorDownload: { - QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size(); - messageSize.rheight() += commentSize.height() + buttonHeight + textMargin * 2; - messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), buttonWidth))); - } + case Models::errorDownload: + messageSize.rheight() += buttonHeight + textMargin; + messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin; break; - case Models::errorUpload: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect); - QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size(); - messageSize.rheight() += aSize.height() + commentSize.height() + textMargin * 2; - messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), aSize.width()))); - } + case Models::errorUpload: + messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; + messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin; break; } - if (needToDrawSender(index, data)) { - QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); - messageSize.rheight() += senderSize.height() + textMargin; - messageSize.setWidth(std::max(senderSize.width(), messageSize.width())); + messageSize.rheight() += nickMetrics.lineSpacing(); + messageSize.rheight() += textMargin; + messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize; + + if (messageSize.height() < avatarHeight) { + messageSize.setHeight(avatarHeight); } - - QString dateString = data.date.toLocalTime().toString("hh:mm"); - QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size(); - messageSize.rheight() += bubbleMargin * 1.5; - messageSize.rheight() += dateSize.height() > statusIconSize ? dateSize.height() : statusIconSize; - - int statusWidth = dateSize.width() + statusIconSize + margin; - if (data.correction.corrected) - statusWidth += statusIconSize + margin; - - if (data.encryption != Shared::EncryptionProtocol::none) - statusWidth += statusIconSize + margin; - - messageSize.setWidth(std::max(statusWidth, messageSize.width())); - messageSize.rwidth() += 2 * bubbleMargin; + + messageSize.rheight() += margin; return messageSize; } -QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const { - QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2); - if (needToDrawSender(index, data)) - localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); - - int attachHeight = 0; - switch (data.attach.state) { - case Models::none: - break; - case Models::uploading: - attachHeight += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint).height() + textMargin; - [[fallthrough]]; - case Models::downloading: - attachHeight += barHeight + textMargin; - break; - case Models::remote: - attachHeight += buttonHeight + textMargin; - break; - case Models::ready: - case Models::local: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); - attachHeight += aSize.height() + textMargin; - } - break; - case Models::errorDownload: { - QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); - attachHeight += commentSize.height() + buttonHeight + textMargin * 2; - } - break; - case Models::errorUpload: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); - QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); - attachHeight += aSize.height() + commentSize.height() + textMargin * 2; - } - break; +void MessageDelegate::initializeFonts(const QFont& font) +{ + bodyFont = font; + nickFont = font; + dateFont = font; + + nickFont.setBold(true); + + float ndps = nickFont.pointSizeF(); + if (ndps != -1) { + nickFont.setPointSizeF(ndps * 1.2); + } else { + nickFont.setPointSize(nickFont.pointSize() + 2); } - - int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize); - localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin)); - - return localHint; -} - -QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { - QVariant vi = index.data(Models::MessageFeed::Bulk); - Models::FeedItem data = qvariant_cast(vi); - if (data.text.size() > 0) { - QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); - - if (localHint.contains(point)) { - QPoint translated = point - localHint.topLeft(); - - bodyRenderer.setHtml(Shared::processMessageBody(data.text)); - bodyRenderer.setTextWidth(localHint.size().width()); - - return bodyRenderer.documentLayout()->anchorAt(translated); - } + + dateFont.setItalic(true); + float dps = dateFont.pointSizeF(); + if (dps != -1) { + dateFont.setPointSizeF(dps * 0.8); + } else { + dateFont.setPointSize(dateFont.pointSize() - 2); } - - return QString(); + + bodyMetrics = QFontMetrics(bodyFont); + nickMetrics = QFontMetrics(nickFont); + dateMetrics = QFontMetrics(dateFont); + + Preview::initializeFont(bodyFont); } -void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { - QString anchor = getAnchor(point, index, sizeHint); - if (anchor.size() > 0) - emit openLink(anchor); -} - -QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) { - QVariant vi = index.data(Models::MessageFeed::Bulk); - Models::FeedItem data = qvariant_cast(vi); - if (data.text.size() > 0) { - QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); - - if (localHint.contains(point)) { - QPoint translated = point - localHint.topLeft(); - - bodyRenderer.setHtml(Shared::processMessageBody(data.text)); - bodyRenderer.setTextWidth(localHint.size().width()); - - QAbstractTextDocumentLayout* lay = bodyRenderer.documentLayout(); - - int position = lay->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); - QTextCursor cursor(&bodyRenderer); - cursor.setPosition(position, QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); - - selection.first = cursor.anchor(); - selection.second = cursor.position(); - currentId = data.id; - - if (selection.first != selection.second) { - return cursor.selectedText(); - } - } - } - return ""; -} - -Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { - QVariant vi = index.data(Models::MessageFeed::Bulk); - Models::FeedItem data = qvariant_cast(vi); - if (data.text.size() > 0) { - QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); - - if (localHint.contains(point)) { - QPoint translated = point - localHint.topLeft(); - - bodyRenderer.setHtml(Shared::processMessageBody(data.text)); - bodyRenderer.setTextWidth(localHint.size().width()); - - QAbstractTextDocumentLayout* lay = bodyRenderer.documentLayout(); - QString anchor = lay->anchorAt(translated); - - if (anchor.size() > 0) { - return Shared::Hover::anchor; - } else { - int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit); - if (position != -1) - return Shared::Hover::text; - } - } - } - return Shared::Hover::nothing; -} - -QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) { - QVariant vi = index.data(Models::MessageFeed::Bulk); - Models::FeedItem data = qvariant_cast(vi); - if (data.text.size() > 0) { - QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); - - if (localHint.contains(start)) { - QPoint tl = localHint.topLeft(); - QPoint first = start - tl; - QPoint last = end - tl; - last.setX(std::max(last.x(), 0)); - last.setX(std::min(last.x(), localHint.width() - 1)); - last.setY(std::max(last.y(), 0)); - last.setY(std::min(last.y(), localHint.height())); - - bodyRenderer.setHtml(Shared::processMessageBody(data.text)); - bodyRenderer.setTextWidth(localHint.size().width()); - selection.first = bodyRenderer.documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); - selection.second = bodyRenderer.documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit); - - currentId = data.id; - - if (selection.first != selection.second) { - QTextCursor cursor(&bodyRenderer); - cursor.setPosition(selection.first, QTextCursor::MoveAnchor); - cursor.setPosition(selection.second, QTextCursor::KeepAnchor); - return cursor.selectedText(); - } - } - } - return ""; -} - -QString MessageDelegate::clearSelection() { - QString lastSelectedId = currentId; - currentId = ""; - selection = std::pair(0, 0); - return lastSelectedId; -} - -bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { +bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) +{ //qDebug() << event->type(); + + return QStyledItemDelegate::editorEvent(event, model, option, index); } -int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { +void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const +{ QPoint start; - if (sentByMe) - start = {option.rect.x() + option.rect.width() - btn->width(), option.rect.top()}; - else + if (sentByMe) { + start = {option.rect.width() - btn->width(), option.rect.top()}; + } else { start = option.rect.topLeft(); + } QWidget* vp = static_cast(painter->device()); btn->setParent(vp); @@ -545,10 +354,10 @@ int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentB btn->show(); option.rect.adjust(0, buttonHeight + textMargin, 0, 0); - return btn->width(); } -int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { +void MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const +{ painter->setFont(dateFont); QColor q = painter->pen().color(); q.setAlpha(180); @@ -556,12 +365,10 @@ int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painte QRect rect; painter->drawText(option.rect, option.displayAlignment, data.attach.error, &rect); option.rect.adjust(0, rect.height() + textMargin, 0, 0); - - return rect.width(); } -int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { - SHARED_UNUSED(sentByMe); +void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const +{ QPoint start = option.rect.topLeft(); bar->resize(option.rect.width(), barHeight); @@ -569,44 +376,41 @@ int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByM bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren); option.rect.adjust(0, barHeight + textMargin, 0, 0); - - return option.rect.width(); } -int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { +void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const +{ Preview* preview = 0; - std::map::iterator itr = previews.find(data.id); + std::map::iterator itr = previews->find(data.id); QSize size = option.rect.size(); - QString path = Shared::resolvePath(data.attach.localPath); - if (itr != previews.end()) { + if (itr != previews->end()) { preview = itr->second; - preview->actualize(path, size, option.rect.topLeft()); + preview->actualize(data.attach.localPath, size, option.rect.topLeft()); } else { QWidget* vp = static_cast(painter->device()); - preview = new Preview(path, size, option.rect.topLeft(), vp); - previews.insert(std::make_pair(data.id, preview)); + preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), data.sentByMe, vp); + previews->insert(std::make_pair(data.id, preview)); } - if (!preview->isFileReachable()) //this is the situation when the file preview couldn't be painted because the file was moved + if (!preview->isFileReachable()) { //this is the situation when the file preview couldn't be painted because the file was moved emit invalidPath(data.id); //or deleted. This signal notifies the model, and the model notifies the core, preview can - //handle being invalid for as long as I need and can be even become valid again with a new path - QSize pSize(preview->size()); - option.rect.adjust(0, pSize.height() + textMargin, 0, 0); - - return pSize.width(); + } //handle being invalid for as long as I need and can be even become valid again with a new path + + option.rect.adjust(0, preview->size().height() + textMargin, 0, 0); } -QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { - std::map::const_iterator itr = buttons.find(data.id); +QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const +{ + std::map::const_iterator itr = buttons->find(data.id); FeedButton* result = 0; - if (itr != buttons.end()) { + if (itr != buttons->end()) { result = itr->second; } else { - std::map::const_iterator barItr = bars.find(data.id); - if (barItr != bars.end()) { + std::map::const_iterator barItr = bars->find(data.id); + if (barItr != bars->end()) { delete barItr->second; - bars.erase(barItr); + bars->erase(barItr); } } @@ -614,30 +418,31 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { result = new FeedButton(); result->messageId = data.id; result->setText(QCoreApplication::translate("MessageLine", "Download")); - buttons.insert(std::make_pair(data.id, result)); + buttons->insert(std::make_pair(data.id, result)); connect(result, &QPushButton::clicked, this, &MessageDelegate::onButtonPushed); } return result; } -QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const { - std::map::const_iterator barItr = bars.find(data.id); +QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const +{ + std::map::const_iterator barItr = bars->find(data.id); QProgressBar* result = 0; - if (barItr != bars.end()) { + if (barItr != bars->end()) { result = barItr->second; } else { - std::map::const_iterator itr = buttons.find(data.id); - if (itr != buttons.end()) { + std::map::const_iterator itr = buttons->find(data.id); + if (itr != buttons->end()) { delete itr->second; - buttons.erase(itr); + buttons->erase(itr); } } if (result == 0) { result = new QProgressBar(); result->setRange(0, 100); - bars.insert(std::make_pair(data.id, result)); + bars->insert(std::make_pair(data.id, result)); } result->setValue(data.attach.progress * 100); @@ -645,22 +450,24 @@ QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const { return result; } -QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { - std::map::const_iterator itr = statusIcons.find(data.id); +QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const +{ + std::map::const_iterator itr = statusIcons->find(data.id); QLabel* result = 0; - if (itr != statusIcons.end()) { + if (itr != statusIcons->end()) { result = itr->second; } else { result = new QLabel(); - statusIcons.insert(std::make_pair(data.id, result)); + statusIcons->insert(std::make_pair(data.id, result)); } QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(data.state)])); QString tt = Shared::Global::getName(data.state); if (data.state == Shared::Message::State::error) { - if (data.error.size() > 0) + if (data.error > 0) { tt += ": " + data.error; + } } if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally result->setPixmap(q.pixmap(statusIconSize)); //it invokes an infinite cycle of repaint @@ -670,17 +477,18 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { return result; } -QLabel* MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { - std::map::const_iterator itr = pencilIcons.find(data.id); +QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const +{ + std::map::const_iterator itr = pencilIcons->find(data.id); QLabel* result = 0; - if (itr != pencilIcons.end()) { + if (itr != pencilIcons->end()) { result = itr->second; } else { result = new QLabel(); QIcon icon = Shared::icon("edit-rename"); result->setPixmap(icon.pixmap(statusIconSize)); - pencilIcons.insert(std::make_pair(data.id, result)); + pencilIcons->insert(std::make_pair(data.id, result)); } result->setToolTip("Last time edited: " + data.correction.lastCorrection.toLocalTime().toString() @@ -689,99 +497,84 @@ QLabel* MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { return result; } -QLabel* MessageDelegate::getEncryptionIcon(const Models::FeedItem& data) const { - std::map::const_iterator itr = encryptionIcons.find(data.id); +QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const +{ + std::map::const_iterator itr = bodies->find(data.id); QLabel* result = 0; - - if (itr != encryptionIcons.end()) { + + if (itr != bodies->end()) { result = itr->second; } else { result = new QLabel(); - QIcon icon = Shared::icon("secure"); - result->setPixmap(icon.pixmap(statusIconSize)); - encryptionIcons.insert(std::make_pair(data.id, result)); - result->setToolTip("Encrypted: " + Shared::Global::getName(data.encryption)); + result->setFont(bodyFont); + result->setWordWrap(true); + result->setOpenExternalLinks(true); + result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); + bodies->insert(std::make_pair(data.id, result)); } - + + result->setText(Shared::processMessageBody(data.text)); + return result; } +void MessageDelegate::beginClearWidgets() +{ + idsToKeep->clear(); + clearingWidgets = true; +} + template -void removeElements(std::map& elements, std::set& idsToKeep) { +void removeElements(std::map* elements, std::set* idsToKeep) { std::set toRemove; - for (const std::pair& pair: elements) { - if (idsToKeep.find(pair.first) == idsToKeep.end()) { + for (const std::pair& pair: *elements) { + if (idsToKeep->find(pair.first) == idsToKeep->end()) { delete pair.second; toRemove.insert(pair.first); } } - for (const QString& key : toRemove) - elements.erase(key); -} - -int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const -{ - if (data.text.size() > 0) { - bodyRenderer.setHtml(Shared::processMessageBody(data.text)); - bodyRenderer.setTextWidth(option.rect.size().width()); - painter->save(); - painter->translate(option.rect.topLeft()); - - if (data.id == currentId) { - QTextCursor cursor(&bodyRenderer); - cursor.setPosition(selection.first, QTextCursor::MoveAnchor); - cursor.setPosition(selection.second, QTextCursor::KeepAnchor); - QTextCharFormat format = cursor.charFormat(); - format.setBackground(option.palette.color(QPalette::Active, QPalette::Highlight)); - format.setForeground(option.palette.color(QPalette::Active, QPalette::HighlightedText)); - cursor.setCharFormat(format); - } - - bodyRenderer.drawContents(painter); - - painter->restore(); - QSize bodySize(std::ceil(bodyRenderer.idealWidth()), std::ceil(bodyRenderer.size().height())); - - option.rect.adjust(0, bodySize.height() + textMargin, 0, 0); - return bodySize.width(); + for (const QString& key : toRemove) { + elements->erase(key); } - return 0; } -void MessageDelegate::beginClearWidgets() { - idsToKeep.clear(); - clearingWidgets = true; -} - -void MessageDelegate::endClearWidgets() { +void MessageDelegate::endClearWidgets() +{ if (clearingWidgets) { removeElements(buttons, idsToKeep); removeElements(bars, idsToKeep); removeElements(statusIcons, idsToKeep); removeElements(pencilIcons, idsToKeep); - removeElements(encryptionIcons, idsToKeep); + removeElements(bodies, idsToKeep); removeElements(previews, idsToKeep); - idsToKeep.clear(); + idsToKeep->clear(); clearingWidgets = false; } } -void MessageDelegate::onButtonPushed() const { +void MessageDelegate::onButtonPushed() const +{ FeedButton* btn = static_cast(sender()); emit buttonPushed(btn->messageId); } -void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const { - std::map::const_iterator itr = buttons.find(data.id); - if (itr != buttons.end()) { +void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const +{ + std::map::const_iterator itr = buttons->find(data.id); + if (itr != buttons->end()) { delete itr->second; - buttons.erase(itr); + buttons->erase(itr); } else { - std::map::const_iterator barItr = bars.find(data.id); - if (barItr != bars.end()) { + std::map::const_iterator barItr = bars->find(data.id); + if (barItr != bars->end()) { delete barItr->second; - bars.erase(barItr); + bars->erase(barItr); } } } + +// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +// { +// +// } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 90ef819..7403285 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -1,22 +1,23 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#pragma once +#ifndef MESSAGEDELEGATE_H +#define MESSAGEDELEGATE_H #include #include @@ -28,14 +29,10 @@ #include #include #include -#include -#include -#include #include "shared/icons.h" #include "shared/global.h" #include "shared/utils.h" -#include "shared/pathcheck.h" #include "preview.h" @@ -43,83 +40,66 @@ namespace Models { struct FeedItem; }; -class MessageDelegate : public QStyledItemDelegate { +class MessageDelegate : public QStyledItemDelegate +{ Q_OBJECT public: MessageDelegate(QObject *parent = nullptr); ~MessageDelegate(); - + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; //void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; - + + void initializeFonts(const QFont& font); bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; void endClearWidgets(); void beginClearWidgets(); - void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - QString leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint); - Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); - QString clearSelection(); - - static int avatarHeight; - static int margin; - + signals: void buttonPushed(const QString& messageId) const; void invalidPath(const QString& messageId) const; - void openLink(const QString& href) const; - + protected: - int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; - int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; - int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; - int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; - int paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; - void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const; - void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const; - + void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; + void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; + void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; + void paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; QPushButton* getButton(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getPencilIcon(const Models::FeedItem& data) const; - QLabel* getEncryptionIcon(const Models::FeedItem& data) const; + QLabel* getBody(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const; - - bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const; - bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const; - - QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const; - QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - + protected slots: void onButtonPushed() const; - + private: class FeedButton : public QPushButton { public: QString messageId; }; - - const QFont& bodyFont; - const QFont& nickFont; - const QFont& dateFont; - const QFontMetrics& nickMetrics; - const QFontMetrics& dateMetrics; - mutable QTextDocument bodyRenderer; - + + QFont bodyFont; + QFont nickFont; + QFont dateFont; + QFontMetrics bodyMetrics; + QFontMetrics nickMetrics; + QFontMetrics dateMetrics; + int buttonHeight; - int buttonWidth; int barHeight; - - mutable std::map buttons; - mutable std::map bars; - mutable std::map statusIcons; - mutable std::map pencilIcons; - mutable std::map encryptionIcons; - mutable std::map previews; - mutable std::set idsToKeep; + + std::map* buttons; + std::map* bars; + std::map* statusIcons; + std::map* pencilIcons; + std::map* bodies; + std::map* previews; + std::set* idsToKeep; bool clearingWidgets; - QString currentId; - std::pair selection; + }; + +#endif // MESSAGEDELEGATE_H diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 370af36..733cf1d 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -20,7 +20,6 @@ #include #include -#include #include @@ -29,7 +28,6 @@ const QHash Models::MessageFeed::roles = { {Sender, "sender"}, {Date, "date"}, {DeliveryState, "deliveryState"}, - {Encryption, "encryption"}, {Correction, "correction"}, {SentByMe,"sentByMe"}, {Avatar, "avatar"}, @@ -52,16 +50,20 @@ Models::MessageFeed::MessageFeed(const Element* ri, QObject* parent): failedUploads(), unreadMessages(new std::set()), observersAmount(0) -{} - -Models::MessageFeed::~MessageFeed() { - delete unreadMessages; - - for (Shared::Message* message : storage) - delete message; +{ } -void Models::MessageFeed::addMessage(const Shared::Message& msg) { +Models::MessageFeed::~MessageFeed() +{ + delete unreadMessages; + + for (Shared::Message* message : storage) { + delete message; + } +} + +void Models::MessageFeed::addMessage(const Shared::Message& msg) +{ QString id = msg.getId(); StorageById::const_iterator itr = indexById.find(id); if (itr != indexById.end()) { @@ -72,11 +74,11 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg) { Shared::Message* copy = new Shared::Message(msg); StorageByTime::const_iterator tItr = indexByTime.upper_bound(msg.getTime()); int position; - if (tItr == indexByTime.end()) + if (tItr == indexByTime.end()) { position = storage.size(); - else + } else { position = indexByTime.rank(tItr); - + } beginInsertRows(QModelIndex(), position, position); storage.insert(copy); endInsertRows(); @@ -84,13 +86,14 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg) { emit newMessage(msg); if (observersAmount == 0 && !msg.getForwarded()) { //not to notify when the message is delivered by the carbon copy - unreadMessages->insert(id); //cuz it could be my own one or the one I read on another device + unreadMessages->insert(msg.getId()); //cuz it could be my own one or the one I read on another device emit unreadMessagesCountChanged(); emit unnoticedMessage(msg); } } -void Models::MessageFeed::changeMessage(const QString& id, const QMap& data) { +void Models::MessageFeed::changeMessage(const QString& id, const QMap& data) +{ StorageById::iterator itr = indexById.find(id); if (itr == indexById.end()) { qDebug() << "received a command to change a message, but the message couldn't be found, skipping"; @@ -155,28 +158,22 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap cr; - for (MessageRoles role : changeRoles) + for (MessageRoles role : changeRoles) { cr.push_back(role); + } emit dataChanged(index, index, cr); - - if (observersAmount == 0 && !msg->getForwarded() && changeRoles.count(MessageRoles::Text) > 0) { - unreadMessages->insert(id); - emit unreadMessagesCountChanged(); - emit unnoticedMessage(*msg); - } } } -std::set Models::MessageFeed::detectChanges( - const Shared::Message& msg, - const QMap& data -) const { +std::set Models::MessageFeed::detectChanges(const Shared::Message& msg, const QMap& data) const +{ std::set roles; Shared::Message::State state = msg.getState(); QMap::const_iterator itr = data.find("state"); - if (itr != data.end() && static_cast(itr.value().toUInt()) != state) + if (itr != data.end() && static_cast(itr.value().toUInt()) != state) { roles.insert(MessageRoles::DeliveryState); + } itr = data.find("outOfBandUrl"); bool att = false; @@ -225,21 +222,12 @@ std::set Models::MessageFeed::detectChanges( return roles; } -void Models::MessageFeed::removeMessage(const QString& id) { - SHARED_UNUSED(id); - //todo; +void Models::MessageFeed::removeMessage(const QString& id) +{ } -Shared::Message Models::MessageFeed::getMessage(const QString& id) { - StorageById::iterator itr = indexById.find(id); - if (itr == indexById.end()) - throw NotFound(id.toStdString(), rosterItem->getJid().toStdString(), rosterItem->getAccountName().toStdString()); - - return **itr; -} - - -QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { +QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const +{ int i = index.row(); QVariant answer; @@ -251,18 +239,20 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: case Text: { QString body = msg->getBody(); - if (body != msg->getOutOfBandUrl()) + if (body != msg->getOutOfBandUrl()) { answer = body; + } } break; case Sender: if (sentByMe(*msg)) { answer = rosterItem->getAccountName(); } else { - if (rosterItem->isRoom()) + if (rosterItem->isRoom()) { answer = msg->getFromResource(); - else + } else { answer = rosterItem->getDisplayedName(); + } } break; case Date: @@ -271,9 +261,6 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { case DeliveryState: answer = static_cast(msg->getState()); break; - case Encryption: - answer = QVariant::fromValue(msg->getEncryption()); - break; case Correction: answer.setValue(fillCorrection(*msg));; break; @@ -285,17 +272,19 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { if (sentByMe(*msg)) { path = rosterItem->getAccountAvatarPath(); } else if (!rosterItem->isRoom()) { - if (rosterItem->getAvatarState() != Shared::Avatar::empty) + if (rosterItem->getAvatarState() != Shared::Avatar::empty) { path = rosterItem->getAvatarPath(); + } } else { const Room* room = static_cast(rosterItem); path = room->getParticipantIconPath(msg->getFromResource()); } - if (path.size() == 0) + if (path.size() == 0) { answer = Shared::iconPath("user", true); - else + } else { answer = path; + } } break; case Attach: @@ -311,18 +300,23 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { case Bulk: { FeedItem item; item.id = msg->getId(); - markMessageAsRead(item.id); + + std::set::const_iterator umi = unreadMessages->find(item.id); + if (umi != unreadMessages->end()) { + unreadMessages->erase(umi); + emit unreadMessagesCountChanged(); + } item.sentByMe = sentByMe(*msg); item.date = msg->getTime(); item.state = msg->getState(); - item.encryption = msg->getEncryption(); item.error = msg->getErrorText(); item.correction = fillCorrection(*msg); QString body = msg->getBody(); - if (body != msg->getOutOfBandUrl()) + if (body != msg->getOutOfBandUrl()) { item.text = body; + } item.avatar.clear(); if (item.sentByMe) { @@ -335,14 +329,15 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { item.avatar = room->getParticipantIconPath(msg->getFromResource()); } else { item.sender = rosterItem->getDisplayedName(); - if (rosterItem->getAvatarState() != Shared::Avatar::empty) + if (rosterItem->getAvatarState() != Shared::Avatar::empty) { item.avatar = rosterItem->getAvatarPath(); + } } } - if (item.avatar.size() == 0) + if (item.avatar.size() == 0) { item.avatar = Shared::iconPath("user", true); - + } item.attach = fillAttach(*msg); answer.setValue(item); } @@ -355,44 +350,37 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { return answer; } -int Models::MessageFeed::rowCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int Models::MessageFeed::rowCount(const QModelIndex& parent) const +{ return storage.size(); } -bool Models::MessageFeed::markMessageAsRead(const QString& id) const { - std::set::const_iterator umi = unreadMessages->find(id); - if (umi != unreadMessages->end()) { - unreadMessages->erase(umi); - emit unreadMessagesCountChanged(); - return true; - } - return false; -} - -unsigned int Models::MessageFeed::unreadMessagesCount() const { +unsigned int Models::MessageFeed::unreadMessagesCount() const +{ return unreadMessages->size(); } -bool Models::MessageFeed::canFetchMore(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +bool Models::MessageFeed::canFetchMore(const QModelIndex& parent) const +{ return syncState == incomplete; } -void Models::MessageFeed::fetchMore(const QModelIndex& parent) { - SHARED_UNUSED(parent); +void Models::MessageFeed::fetchMore(const QModelIndex& parent) +{ if (syncState == incomplete) { syncState = syncing; emit syncStateChange(syncState); - if (storage.size() == 0) + if (storage.size() == 0) { emit requestArchive(""); - else + } else { emit requestArchive((*indexByTime.rbegin())->getId()); + } } } -void Models::MessageFeed::responseArchive(const std::list list, bool last) { +void Models::MessageFeed::responseArchive(const std::list list, bool last) +{ Storage::size_type size = storage.size(); beginInsertRows(QModelIndex(), size, size + list.size() - 1); @@ -403,30 +391,38 @@ void Models::MessageFeed::responseArchive(const std::list list, endInsertRows(); if (syncState == syncing) { - if (last) + if (last) { syncState = complete; - else + } else { syncState = incomplete; - + } emit syncStateChange(syncState); } } -QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& parent) const{ - if (!hasIndex(row, column, parent)) +QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) { return QModelIndex(); - + } + StorageByTime::iterator itr = indexByTime.nth(row); - if (itr == indexByTime.end()) + if (itr != indexByTime.end()) { + Shared::Message* msg = *itr; + + return createIndex(row, column, msg); + } else { return QModelIndex(); - - Shared::Message* msg = *itr; - return createIndex(row, column, msg); + } } -QHash Models::MessageFeed::roleNames() const {return roles;} +QHash Models::MessageFeed::roleNames() const +{ + return roles; +} -bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const { +bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const +{ if (rosterItem->isRoom()) { const Room* room = static_cast(rosterItem); return room->getNick().toLower() == msg.getFromResource().toLower(); @@ -435,7 +431,8 @@ bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const { } } -Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const { +Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const +{ ::Models::Attachment att; QString id = msg.getId(); @@ -483,14 +480,16 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c return att; } -Models::Edition Models::MessageFeed::fillCorrection(const Shared::Message& msg) const { +Models::Edition Models::MessageFeed::fillCorrection(const Shared::Message& msg) const +{ ::Models::Edition ed({msg.getEdited(), msg.getOriginalBody(), msg.getLastModified()}); return ed; } -void Models::MessageFeed::downloadAttachment(const QString& messageId) { +void Models::MessageFeed::downloadAttachment(const QString& messageId) +{ bool notify = false; Err::const_iterator eitr = failedDownloads.find(messageId); if (eitr != failedDownloads.end()) { @@ -512,11 +511,13 @@ void Models::MessageFeed::downloadAttachment(const QString& messageId) { qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId; } - if (notify) + if (notify) { emit dataChanged(ind, ind, {MessageRoles::Attach}); + } } -bool Models::MessageFeed::registerUpload(const QString& messageId) { +bool Models::MessageFeed::registerUpload(const QString& messageId) +{ bool success = uploads.insert(std::make_pair(messageId, 0)).second; QVector roles({}); @@ -534,7 +535,8 @@ bool Models::MessageFeed::registerUpload(const QString& messageId) { return success; } -void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up) { +void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up) +{ Progress* pr = 0; Err* err = 0; if (up) { @@ -560,11 +562,13 @@ void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bo } } -void Models::MessageFeed::fileComplete(const QString& messageId, bool up) { +void Models::MessageFeed::fileComplete(const QString& messageId, bool up) +{ fileProgress(messageId, 1, up); } -void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up) { +void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up) +{ Err* failed; Progress* loads; if (up) { @@ -576,28 +580,33 @@ void Models::MessageFeed::fileError(const QString& messageId, const QString& err } Progress::iterator pitr = loads->find(messageId); - if (pitr != loads->end()) + if (pitr != loads->end()) { loads->erase(pitr); + } std::pair pair = failed->insert(std::make_pair(messageId, error)); - if (!pair.second) + if (!pair.second) { pair.first->second = error; - + } QModelIndex ind = modelIndexById(messageId); - if (ind.isValid()) + if (ind.isValid()) { emit dataChanged(ind, ind, {MessageRoles::Attach}); + } } -void Models::MessageFeed::incrementObservers() { +void Models::MessageFeed::incrementObservers() +{ ++observersAmount; } -void Models::MessageFeed::decrementObservers() { +void Models::MessageFeed::decrementObservers() +{ --observersAmount; } -QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const { +QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const +{ StorageById::const_iterator itr = indexById.find(id); if (itr != indexById.end()) { Shared::Message* msg = *itr; @@ -607,7 +616,8 @@ QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const { return QModelIndex(); } -QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const { +QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const +{ if (indexByTime.size() > 0) { StorageByTime::const_iterator tItr = indexByTime.lower_bound(time); StorageByTime::const_iterator tEnd = indexByTime.upper_bound(time); @@ -629,7 +639,8 @@ QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDate return QModelIndex(); } -void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) { +void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) +{ StorageById::iterator itr = indexById.find(messageId); if (itr == indexById.end()) { qDebug() << "received a command to change a message, but the message couldn't be found, skipping"; @@ -647,11 +658,13 @@ void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) { emit dataChanged(index, index, {MessageRoles::Attach}); } -Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const { +Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const +{ return syncState; } -void Models::MessageFeed::requestLatestMessages() { +void Models::MessageFeed::requestLatestMessages() +{ if (syncState != syncing) { syncState = syncing; emit syncStateChange(syncState); diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index 5cceae9..2273b15 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -16,12 +16,12 @@ * along with this program. If not, see . */ -#pragma once +#ifndef MESSAGEFEED_H +#define MESSAGEFEED_H #include #include #include -#include #include @@ -32,7 +32,6 @@ #include #include -#include namespace Models { @@ -56,9 +55,6 @@ public: void addMessage(const Shared::Message& msg); void changeMessage(const QString& id, const QMap& data); void removeMessage(const QString& id); - Shared::Message getMessage(const QString& id); - QModelIndex modelIndexById(const QString& id) const; - QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -74,7 +70,6 @@ public: void reportLocalPathInvalid(const QString& messageId); unsigned int unreadMessagesCount() const; - bool markMessageAsRead(const QString& id) const; void fileProgress(const QString& messageId, qreal value, bool up); void fileError(const QString& messageId, const QString& error, bool up); void fileComplete(const QString& messageId, bool up); @@ -100,7 +95,6 @@ public: Sender, Date, DeliveryState, - Encryption, Correction, SentByMe, Avatar, @@ -109,26 +103,13 @@ public: Error, Bulk }; - - class NotFound: - public Utils::Exception - { - public: - NotFound(const std::string& k, const std::string& j, const std::string& acc):Exception(), key(k), jid(j), account(acc){} - - std::string getMessage() const { - return "Message with id " + key + " wasn't found in messageFeed " + account + " of the chat with " + jid; - } - private: - std::string key; - std::string jid; - std::string account; - }; protected: bool sentByMe(const Shared::Message& msg) const; Attachment fillAttach(const Shared::Message& msg) const; Edition fillCorrection(const Shared::Message& msg) const; + QModelIndex modelIndexById(const QString& id) const; + QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; std::set detectChanges(const Shared::Message& msg, const QMap& data) const; private: @@ -217,7 +198,6 @@ struct FeedItem { Edition correction; QDateTime date; Shared::Message::State state; - Shared::EncryptionProtocol encryption; Attachment attach; }; }; @@ -226,4 +206,4 @@ Q_DECLARE_METATYPE(Models::Attachment); Q_DECLARE_METATYPE(Models::Edition); Q_DECLARE_METATYPE(Models::FeedItem); -#pragma once +#endif // MESSAGEFEED_H diff --git a/ui/widgets/messageline/messageline.cpp b/ui/widgets/messageline/messageline.cpp new file mode 100644 index 0000000..fec0037 --- /dev/null +++ b/ui/widgets/messageline/messageline.cpp @@ -0,0 +1,504 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "messageline.h" +#include +#include +#include + +MessageLine::MessageLine(bool p_room, QWidget* parent): + QWidget(parent), + messageIndex(), + messageOrder(), + myMessages(), + palMessages(), + uploadPaths(), + palAvatars(), + exPalAvatars(), + layout(new QVBoxLayout(this)), + myName(), + myAvatarPath(), + palNames(), + uploading(), + downloading(), + room(p_room), + busyShown(false), + progress() +{ + setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addStretch(); +} + +MessageLine::~MessageLine() +{ + for (Index::const_iterator itr = messageIndex.begin(), end = messageIndex.end(); itr != end; ++itr) { + delete itr->second; + } +} + +MessageLine::Position MessageLine::message(const Shared::Message& msg, bool forceOutgoing) +{ + QString id = msg.getId(); + Index::iterator itr = messageIndex.find(id); + if (itr != messageIndex.end()) { + qDebug() << "received more then one message with the same id, skipping yet the new one"; + return invalid; + } + + QString sender; + QString aPath; + bool outgoing; + + if (forceOutgoing) { + sender = myName; + aPath = myAvatarPath; + outgoing = true; + } else { + if (room) { + if (msg.getFromResource() == myName) { + sender = myName; + aPath = myAvatarPath; + outgoing = true; + } else { + sender = msg.getFromResource(); + std::map::iterator aItr = palAvatars.find(sender); + if (aItr != palAvatars.end()) { + aPath = aItr->second; + } else { + aItr = exPalAvatars.find(sender); + if (aItr != exPalAvatars.end()) { + aPath = aItr->second; + } + } + outgoing = false; + } + } else { + if (msg.getOutgoing()) { + sender = myName; + aPath = myAvatarPath; + outgoing = true; + } else { + QString jid = msg.getFromJid(); + std::map::iterator itr = palNames.find(jid); + if (itr != palNames.end()) { + sender = itr->second; + } else { + sender = jid; + } + + std::map::iterator aItr = palAvatars.find(jid); + if (aItr != palAvatars.end()) { + aPath = aItr->second; + } + + outgoing = false; + } + } + } + + Message* message = new Message(msg, outgoing, sender, aPath); + + std::pair result = messageOrder.insert(std::make_pair(msg.getTime(), message)); + if (!result.second) { + qDebug() << "Error appending a message into a message list - seems like the time of that message exactly matches the time of some other message, can't put them in order, skipping yet"; + delete message; + return invalid; + } + if (outgoing) { + myMessages.insert(std::make_pair(id, message)); + } else { + QString senderId; + if (room) { + senderId = sender; + } else { + senderId = msg.getFromJid(); + } + + std::map::iterator pItr = palMessages.find(senderId); + if (pItr == palMessages.end()) { + pItr = palMessages.insert(std::make_pair(senderId, Index())).first; + } + pItr->second.insert(std::make_pair(id, message)); + } + messageIndex.insert(std::make_pair(id, message)); + unsigned long index = std::distance(messageOrder.begin(), result.first); //need to make with binary indexed tree + Position res = invalid; + if (index == 0) { + res = beggining; + } else if (index == messageIndex.size() - 1) { + res = end; + } else { + res = middle; + } + + if (busyShown) { + index += 1; + } + + + if (res == end) { + layout->addWidget(message); + } else { + layout->insertWidget(index + 1, message); + } + + if (msg.hasOutOfBandUrl()) { + emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); + connect(message, &Message::buttonClicked, this, &MessageLine::onDownload); + } + + return res; +} + +void MessageLine::changeMessage(const QString& id, const QMap& data) +{ + Index::const_iterator itr = messageIndex.find(id); + if (itr != messageIndex.end()) { + Message* msg = itr->second; + if (msg->change(data)) { //if ID changed (stanza in replace of another) + QString newId = msg->getId(); //need to updated IDs of that message in all maps + messageIndex.erase(itr); + messageIndex.insert(std::make_pair(newId, msg)); + if (msg->outgoing) { + QString senderId; + if (room) { + senderId = msg->getSenderResource(); + } else { + senderId = msg->getSenderJid(); + } + + std::map::iterator pItr = palMessages.find(senderId); + if (pItr != palMessages.end()) { + Index::const_iterator sItr = pItr->second.find(id); + if (sItr != pItr->second.end()) { + pItr->second.erase(sItr); + pItr->second.insert(std::make_pair(newId, msg)); + } else { + qDebug() << "Was trying to replace message in open conversations, couldn't find it among pal's messages, probably an error"; + } + } else { + qDebug() << "Was trying to replace message in open conversations, couldn't find pal messages, probably an error"; + } + } else { + Index::const_iterator mItr = myMessages.find(id); + if (mItr != myMessages.end()) { + myMessages.erase(mItr); + myMessages.insert(std::make_pair(newId, msg)); + } else { + qDebug() << "Was trying to replace message in open conversations, couldn't find it among my messages, probably an error"; + } + } + } + } +} + +void MessageLine::onDownload() +{ + Message* msg = static_cast(sender()); + QString messageId = msg->getId(); + Index::const_iterator itr = downloading.find(messageId); + if (itr == downloading.end()) { + downloading.insert(std::make_pair(messageId, msg)); + msg->setProgress(0); + msg->showComment(tr("Downloading...")); + emit downloadFile(messageId, msg->getFileUrl()); + } else { + qDebug() << "An attempt to initiate download for already downloading file" << msg->getFileUrl() << ", skipping"; + } +} + +void MessageLine::setMyName(const QString& name) +{ + myName = name; + for (Index::const_iterator itr = myMessages.begin(), end = myMessages.end(); itr != end; ++itr) { + itr->second->setSender(name); + } +} + +void MessageLine::setPalName(const QString& jid, const QString& name) +{ + std::map::iterator itr = palNames.find(jid); + if (itr == palNames.end()) { + palNames.insert(std::make_pair(jid, name)); + } else { + itr->second = name; + } + + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { + itr->second->setSender(name); + } + } +} + +void MessageLine::setPalAvatar(const QString& jid, const QString& path) +{ + std::map::iterator itr = palAvatars.find(jid); + if (itr == palAvatars.end()) { + palAvatars.insert(std::make_pair(jid, path)); + + std::map::const_iterator eitr = exPalAvatars.find(jid); + if (eitr != exPalAvatars.end()) { + exPalAvatars.erase(eitr); + } + } else { + itr->second = path; + } + + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { + itr->second->setAvatarPath(path); + } + } +} + +void MessageLine::dropPalAvatar(const QString& jid) +{ + std::map::iterator itr = palAvatars.find(jid); + if (itr != palAvatars.end()) { + palAvatars.erase(itr); + } + + std::map::const_iterator eitr = exPalAvatars.find(jid); + if (eitr != exPalAvatars.end()) { + exPalAvatars.erase(eitr); + } + + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { + itr->second->setAvatarPath(""); + } + } +} + +void MessageLine::movePalAvatarToEx(const QString& name) +{ + std::map::iterator itr = palAvatars.find(name); + if (itr != palAvatars.end()) { + std::map::iterator eitr = exPalAvatars.find(name); + if (eitr != exPalAvatars.end()) { + eitr->second = itr->second; + } else { + exPalAvatars.insert(std::make_pair(name, itr->second)); + } + + palAvatars.erase(itr); + } +} + +void MessageLine::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + emit resize(event->size().height() - event->oldSize().height()); +} + + +QString MessageLine::firstMessageId() const +{ + if (messageOrder.size() == 0) { + return ""; + } else { + return messageOrder.begin()->second->getId(); + } +} + +void MessageLine::showBusyIndicator() +{ + if (!busyShown) { + layout->insertWidget(0, &progress); + progress.start(); + busyShown = true; + } +} + +void MessageLine::hideBusyIndicator() +{ + if (busyShown) { + progress.stop(); + layout->removeWidget(&progress); + busyShown = false; + } +} + +void MessageLine::fileProgress(const QString& messageId, qreal progress) +{ + Index::const_iterator itr = messageIndex.find(messageId); + if (itr == messageIndex.end()) { + //TODO may be some logging, that's not normal + } else { + itr->second->setProgress(progress); + } +} + +void MessageLine::responseLocalFile(const QString& messageId, const QString& path) +{ + Index::const_iterator itr = messageIndex.find(messageId); + if (itr == messageIndex.end()) { + + } else { + Index::const_iterator uItr = uploading.find(messageId); + if (path.size() > 0) { + Index::const_iterator dItr = downloading.find(messageId); + if (dItr != downloading.end()) { + downloading.erase(dItr); + itr->second->showFile(path); + } else { + if (uItr != uploading.end()) { + uploading.erase(uItr); + std::map::const_iterator muItr = uploadPaths.find(messageId); + if (muItr != uploadPaths.end()) { + uploadPaths.erase(muItr); + } + Shared::Message msg = itr->second->getMessage(); + removeMessage(messageId); + msg.setCurrentTime(); + message(msg); + itr = messageIndex.find(messageId); + itr->second->showFile(path); + } else { + itr->second->showFile(path); //then it is already cached file + } + } + } else { + if (uItr == uploading.end()) { + const Shared::Message& msg = itr->second->getMessage(); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); + itr->second->showComment(tr("Push the button to download the file")); + } else { + qDebug() << "An unhandled state for file uploading - empty path"; + } + } + } +} + +void MessageLine::removeMessage(const QString& messageId) +{ + Index::const_iterator itr = messageIndex.find(messageId); + if (itr != messageIndex.end()) { + Message* ui = itr->second; + const Shared::Message& msg = ui->getMessage(); + messageIndex.erase(itr); + Order::const_iterator oItr = messageOrder.find(msg.getTime()); + if (oItr != messageOrder.end()) { + messageOrder.erase(oItr); + } else { + qDebug() << "An attempt to remove message from messageLine, but it wasn't found in order"; + } + if (msg.getOutgoing()) { + Index::const_iterator mItr = myMessages.find(messageId); + if (mItr != myMessages.end()) { + myMessages.erase(mItr); + } else { + qDebug() << "Error removing message: it seems to be outgoing yet it wasn't found in outgoing messages"; + } + } else { + if (room) { + + } else { + QString jid = msg.getFromJid(); + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + Index& pMsgs = pItr->second; + Index::const_iterator pmitr = pMsgs.find(messageId); + if (pmitr != pMsgs.end()) { + pMsgs.erase(pmitr); + } else { + qDebug() << "Error removing message: it seems to be incoming yet it wasn't found among messages from that penpal"; + } + } + } + } + ui->deleteLater(); + qDebug() << "message" << messageId << "has been removed"; + } else { + qDebug() << "An attempt to remove non existing message from messageLine"; + } +} + +void MessageLine::fileError(const QString& messageId, const QString& error) +{ + Index::const_iterator itr = downloading.find(messageId); + if (itr == downloading.end()) { + Index::const_iterator itr = uploading.find(messageId); + if (itr == uploading.end()) { + //TODO may be some logging, that's not normal + } else { + itr->second->showComment(tr("Error uploading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); + itr->second->addButton(QIcon::fromTheme("upload"), tr("Upload")); + } + } else { + const Shared::Message& msg = itr->second->getMessage(); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); + itr->second->showComment(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); + } +} + +void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path) +{ + appendMessageWithUploadNoSiganl(msg, path); + emit uploadFile(msg, path); +} + +void MessageLine::appendMessageWithUploadNoSiganl(const Shared::Message& msg, const QString& path) +{ + message(msg, true); + QString id = msg.getId(); + Message* ui = messageIndex.find(id)->second; + connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry; + ui->setProgress(0); + ui->showComment(tr("Uploading...")); + uploading.insert(std::make_pair(id, ui)); + uploadPaths.insert(std::make_pair(id, path)); +} + + +void MessageLine::onUpload() +{ + //TODO retry +} + +void MessageLine::setMyAvatarPath(const QString& p_path) +{ + if (myAvatarPath != p_path) { + myAvatarPath = p_path; + for (std::pair pair : myMessages) { + pair.second->setAvatarPath(myAvatarPath); + } + } +} + +void MessageLine::setExPalAvatars(const std::map& data) +{ + exPalAvatars = data; + + for (const std::pair& pair : palMessages) { + if (palAvatars.find(pair.first) == palAvatars.end()) { + std::map::const_iterator eitr = exPalAvatars.find(pair.first); + if (eitr != exPalAvatars.end()) { + for (const std::pair& mp : pair.second) { + mp.second->setAvatarPath(eitr->second); + } + } + } + } +} diff --git a/ui/widgets/messageline/messageline.h b/ui/widgets/messageline/messageline.h new file mode 100644 index 0000000..a0a7b6c --- /dev/null +++ b/ui/widgets/messageline/messageline.h @@ -0,0 +1,108 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MESSAGELINE_H +#define MESSAGELINE_H + +#include +#include +#include +#include +#include +#include + +#include "shared/message.h" +#include "message.h" +#include "progress.h" + +class MessageLine : public QWidget +{ + Q_OBJECT +public: + enum Position { + beggining, + middle, + end, + invalid + }; + MessageLine(bool p_room, QWidget* parent = 0); + ~MessageLine(); + + Position message(const Shared::Message& msg, bool forceOutgoing = false); + void setMyName(const QString& name); + void setPalName(const QString& jid, const QString& name); + QString firstMessageId() const; + void showBusyIndicator(); + void hideBusyIndicator(); + void responseLocalFile(const QString& messageId, const QString& path); + void fileError(const QString& messageId, const QString& error); + void fileProgress(const QString& messageId, qreal progress); + void appendMessageWithUpload(const Shared::Message& msg, const QString& path); + void appendMessageWithUploadNoSiganl(const Shared::Message& msg, const QString& path); + void removeMessage(const QString& messageId); + void setMyAvatarPath(const QString& p_path); + void setPalAvatar(const QString& jid, const QString& path); + void dropPalAvatar(const QString& jid); + void changeMessage(const QString& id, const QMap& data); + void setExPalAvatars(const std::map& data); + void movePalAvatarToEx(const QString& name); + +signals: + void resize(int amount); + void downloadFile(const QString& messageId, const QString& url); + void uploadFile(const Shared::Message& msg, const QString& path); + void requestLocalFile(const QString& messageId, const QString& url); + +protected: + void resizeEvent(QResizeEvent * event) override; + +protected: + void onDownload(); + void onUpload(); + +private: + struct Comparator { + bool operator()(const Shared::Message& a, const Shared::Message& b) const { + return a.getTime() < b.getTime(); + } + bool operator()(const Shared::Message* a, const Shared::Message* b) const { + return a->getTime() < b->getTime(); + } + }; + typedef std::map Order; + typedef std::map Index; + Index messageIndex; + Order messageOrder; + Index myMessages; + std::map palMessages; + std::map uploadPaths; + std::map palAvatars; + std::map exPalAvatars; + QVBoxLayout* layout; + + QString myName; + QString myAvatarPath; + std::map palNames; + Index uploading; + Index downloading; + bool room; + bool busyShown; + Progress progress; +}; + +#endif // MESSAGELINE_H diff --git a/ui/widgets/messageline/preview.cpp b/ui/widgets/messageline/preview.cpp index 8c844ae..a64c036 100644 --- a/ui/widgets/messageline/preview.cpp +++ b/ui/widgets/messageline/preview.cpp @@ -18,22 +18,16 @@ #include "preview.h" -#include constexpr int margin = 6; constexpr int maxAttachmentHeight = 500; -QFont getFont () { - QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); - font.setBold(true); - return font; -} - -QFont Preview::font = getFont(); +QFont Preview::font; QFontMetrics Preview::metrics(Preview::font); -Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* pParent): +Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* pParent): info(Shared::Global::getFileInfo(pPath)), + path(pPath), maxSize(pMaxSize), actualSize(constrainAttachSize(info.size, maxSize)), cachedLabelSize(0, 0), @@ -43,14 +37,23 @@ Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, parent(pParent), movie(0), fileReachable(true), - actualPreview(false) + actualPreview(false), + right(pRight) { + initializeElements(); if (fileReachable) { positionElements(); } } +void Preview::initializeFont(const QFont& newFont) +{ + font = newFont; + font.setBold(true); + metrics = QFontMetrics(font); +} + Preview::~Preview() { clean(); @@ -101,6 +104,9 @@ void Preview::actualize(const QString& newPath, const QSize& newSize, const QPoi } } else if (maxSizeChanged) { applyNewMaxSize(); + if (right) { + positionChanged = true; + } } if (positionChanged || !actualPreview) { positionElements(); @@ -129,6 +135,9 @@ void Preview::setSize(const QSize& newSize) } if (maxSizeChanged || !actualPreview) { applyNewMaxSize(); + if (right) { + positionElements(); + } } } } @@ -137,7 +146,7 @@ void Preview::applyNewSize() { switch (info.preview) { case Shared::Global::FileInfo::Preview::picture: { - QImageReader img(info.path); + QImageReader img(path); if (!img.canRead()) { delete widget; fileReachable = false; @@ -155,9 +164,6 @@ void Preview::applyNewSize() break; default: { QIcon icon = QIcon::fromTheme(info.mime.iconName()); - if (icon.isNull()) { - icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); - } widget->setPixmap(icon.pixmap(actualSize)); widget->resize(actualSize); } @@ -207,8 +213,9 @@ void Preview::setPosition(const QPoint& newPoint) bool Preview::setPath(const QString& newPath) { - if (info.path != newPath) { - info = Shared::Global::getFileInfo(newPath); + if (path != newPath) { + path = newPath; + info = Shared::Global::getFileInfo(path); actualSize = constrainAttachSize(info.size, maxSize); clean(); initializeElements(); @@ -225,7 +232,7 @@ void Preview::initializeElements() { switch (info.preview) { case Shared::Global::FileInfo::Preview::picture: { - QImageReader img(info.path); + QImageReader img(path); if (!img.canRead()) { fileReachable = false; } else { @@ -238,7 +245,7 @@ void Preview::initializeElements() } break; case Shared::Global::FileInfo::Preview::animation:{ - movie = new QMovie(info.path); + movie = new QMovie(path); QObject::connect(movie, &QMovie::error, std::bind(&Preview::handleQMovieError, this, std::placeholders::_1) ); @@ -257,10 +264,6 @@ void Preview::initializeElements() break; default: { QIcon icon = QIcon::fromTheme(info.mime.iconName()); - if (icon.isNull()) { - icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); - } - widget = new QLabel(parent); widget->setPixmap(icon.pixmap(actualSize)); widget->show(); @@ -279,6 +282,9 @@ void Preview::initializeElements() void Preview::positionElements() { int start = position.x(); + if (right) { + start += maxSize.width() - size().width(); + } widget->move(start, position.y()); if (!actualPreview) { int x = start + actualSize.width() + margin; @@ -287,36 +293,11 @@ void Preview::positionElements() } } -bool Preview::canVisualize(const Shared::Global::FileInfo& info) -{ - switch (info.preview) { - case Shared::Global::FileInfo::Preview::picture: { - QImageReader img(info.path); - return img.canRead(); - } - break; - case Shared::Global::FileInfo::Preview::animation:{ - QMovie movie(info.path); - return movie.isValid(); - } - break; - default: { - return false; - } - } -} - QSize Preview::calculateAttachSize(const QString& path, const QRect& bounds) { Shared::Global::FileInfo info = Shared::Global::getFileInfo(path); - QSize constrained = constrainAttachSize(info.size, bounds.size()); - if (!canVisualize(info)) { - int maxLabelWidth = bounds.width() - info.size.width() - margin; - QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, maxLabelWidth); - int labelWidth = metrics.boundingRect(elidedName).size().width(); - constrained.rwidth() += margin + labelWidth; - } - return constrained; + + return constrainAttachSize(info.size, bounds.size()); } QSize Preview::constrainAttachSize(QSize src, QSize bounds) diff --git a/ui/widgets/messageline/preview.h b/ui/widgets/messageline/preview.h index 96a6530..004ed45 100644 --- a/ui/widgets/messageline/preview.h +++ b/ui/widgets/messageline/preview.h @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef PREVIEW_H +#define PREVIEW_H #include #include @@ -37,7 +38,7 @@ */ class Preview { public: - Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* parent); + Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* parent); ~Preview(); void actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint); @@ -47,9 +48,9 @@ public: bool isFileReachable() const; QSize size() const; + static void initializeFont(const QFont& newFont); static QSize constrainAttachSize(QSize src, QSize bounds); static QSize calculateAttachSize(const QString& path, const QRect& bounds); - static bool canVisualize(const Shared::Global::FileInfo& info); static QFont font; static QFontMetrics metrics; @@ -63,6 +64,7 @@ private: private: Shared::Global::FileInfo info; + QString path; QSize maxSize; QSize actualSize; QSize cachedLabelSize; @@ -73,4 +75,7 @@ private: QMovie* movie; bool fileReachable; bool actualPreview; + bool right; }; + +#endif // PREVIEW_H diff --git a/ui/widgets/newcontact.cpp b/ui/widgets/newcontact.cpp index 9303963..920c757 100644 --- a/ui/widgets/newcontact.cpp +++ b/ui/widgets/newcontact.cpp @@ -25,10 +25,10 @@ NewContact::NewContact(const Models::Accounts* accounts, QWidget* parent): m_ui(new Ui::NewContact()) { m_ui->setupUi ( this ); - std::deque names = accounts->getActiveNames(); + std::deque names = accounts->getNames(); - for (const QString& name : names) { - m_ui->account->addItem(name); + for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { + m_ui->account->addItem(*i); } m_ui->account->setCurrentIndex(0); @@ -40,7 +40,7 @@ NewContact::NewContact(const QString& acc, const Models::Accounts* accounts, QWi { m_ui->setupUi ( this ); - std::deque names = accounts->getActiveNames(); + std::deque names = accounts->getNames(); int index = 0; bool found = false; diff --git a/ui/widgets/room.cpp b/ui/widgets/room.cpp index 56ce5a0..91d7255 100644 --- a/ui/widgets/room.cpp +++ b/ui/widgets/room.cpp @@ -18,8 +18,6 @@ #include "room.h" -#include "shared/defines.h" - Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent): Conversation(true, acc, p_room, p_room->getJid(), "", parent), room(p_room) @@ -33,10 +31,12 @@ Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent): connect(room, &Models::Room::participantLeft, this, &Room::onParticipantLeft); } -Room::~Room() { +Room::~Room() +{ } -Shared::Message Room::createMessage() const { +Shared::Message Room::createMessage() const +{ Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::groupChat); msg.setFromJid(room->getJid()); @@ -45,12 +45,13 @@ Shared::Message Room::createMessage() const { return msg; } -bool Room::autoJoined() const { +bool Room::autoJoined() const +{ return room->getAutoJoin(); } -void Room::onRoomChanged(Models::Item* item, int row, int col) { - SHARED_UNUSED(row); +void Room::onRoomChanged(Models::Item* item, int row, int col) +{ if (item == room) { switch (col) { case 0: @@ -66,10 +67,11 @@ void Room::onRoomChanged(Models::Item* item, int row, int col) { } } -void Room::onParticipantJoined(const Models::Participant& participant) { - SHARED_UNUSED(participant); +void Room::onParticipantJoined(const Models::Participant& participant) +{ + } -void Room::onParticipantLeft(const QString& name) { - SHARED_UNUSED(name); +void Room::onParticipantLeft(const QString& name) +{ } diff --git a/ui/widgets/room.h b/ui/widgets/room.h index 3d231b8..3f74e4e 100644 --- a/ui/widgets/room.h +++ b/ui/widgets/room.h @@ -20,7 +20,7 @@ #define ROOM_H #include "conversation.h" -#include "ui/models/room.h" +#include "../models/room.h" /** * @todo write docs diff --git a/ui/widgets/settings/CMakeLists.txt b/ui/widgets/settings/CMakeLists.txt deleted file mode 100644 index e100bfe..0000000 --- a/ui/widgets/settings/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -target_sources(squawk PRIVATE - settingslist.h - settingslist.cpp - settings.h - settings.cpp - settings.ui - pagegeneral.h - pagegeneral.cpp - pagegeneral.ui - pageappearance.h - pageappearance.cpp - pageappearance.ui -) diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp deleted file mode 100644 index 64d6de4..0000000 --- a/ui/widgets/settings/pageappearance.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "pageappearance.h" -#include "ui_pageappearance.h" - -#include -#include -#include - -static const QStringList filters = {"*.colors"}; - -PageAppearance::PageAppearance(QWidget* parent): - QWidget(parent), - m_ui(new Ui::PageAppearance()), - styles(), - themes() -{ - m_ui->setupUi(this); - - m_ui->styleInput->addItem(tr("System")); - styles.push_back("system"); - QStringList systemStyles = QStyleFactory::keys(); - for (const QString& key : systemStyles) { - m_ui->styleInput->addItem(key); - styles.push_back(key); - } - - QSettings settings; - QVariant vs = settings.value("style"); - if (vs.isValid()) { - m_ui->styleInput->setCurrentText(vs.toString()); - } else { - m_ui->styleInput->setCurrentText(tr("System")); - } - - connect(m_ui->styleInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onStyleChanged); - - if (Shared::Global::supported("colorSchemeTools")) { - themes.push_back("system"); - m_ui->themeInput->addItem(tr("System")); - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "color-schemes", QStandardPaths::LocateDirectory); - QStringList schemeFiles; - QString currentTheme(tr("System")); - QString requestedTheme(""); - QVariant vtheme = settings.value("theme"); - if (vtheme.isValid()) { - requestedTheme = vtheme.toString(); - } - for (const QString &dir : dirs) { - const QStringList fileNames = QDir(dir).entryList(filters); - for (const QString &file : fileNames) { - QString path = dir + QDir::separator() + file; - themes.push_back(path); - QString themeName = Shared::Global::getColorSchemeName(path); - m_ui->themeInput->addItem(Shared::Global::createThemePreview(path), themeName); - - if (path == requestedTheme) { - currentTheme = themeName; - } - } - } - - m_ui->themeInput->setCurrentText(currentTheme); - - connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); - } else { - m_ui->themeInput->setEnabled(false); - } -} - -PageAppearance::~PageAppearance() -{ -} - -void PageAppearance::onThemeChanged(int index) -{ - if (index >= 0) { - emit variableModified("theme", themes[index]); - } -} - -void PageAppearance::onStyleChanged(int index) -{ - if (index >= 0) { - emit variableModified("style", styles[index]); - } -} - diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h deleted file mode 100644 index c182ea2..0000000 --- a/ui/widgets/settings/pageappearance.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef PAGEAPPEARANCE_H -#define PAGEAPPEARANCE_H - -#include -#include -#include -#include -#include -#include - -#include "shared/global.h" - -namespace Ui -{ -class PageAppearance; -} - -/** - * @todo write docs - */ -class PageAppearance : public QWidget -{ - Q_OBJECT -public: - PageAppearance(QWidget* parent = nullptr); - ~PageAppearance(); - -signals: - void variableModified(const QString& key, const QVariant& value); - -protected slots: - void onStyleChanged(int index); - void onThemeChanged(int index); - -private: - QScopedPointer m_ui; - std::vector styles; - std::vector themes; -}; - -#endif // PAGEAPPEARANCE_H diff --git a/ui/widgets/settings/pageappearance.ui b/ui/widgets/settings/pageappearance.ui deleted file mode 100644 index 570eefa..0000000 --- a/ui/widgets/settings/pageappearance.ui +++ /dev/null @@ -1,38 +0,0 @@ - - - PageAppearance - - - - 0 - 0 - 400 - 300 - - - - - - - Theme - - - - - - - - - - - - - Color scheme - - - - - - - - diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp deleted file mode 100644 index 9e73574..0000000 --- a/ui/widgets/settings/pagegeneral.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "pagegeneral.h" -#include "ui_pagegeneral.h" - -PageGeneral::PageGeneral(QWidget* parent): - QWidget(parent), - m_ui(new Ui::PageGeneral()), - dialog(nullptr) -{ - m_ui->setupUi(this); - - QSettings settings; - m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString()); - if (QSystemTrayIcon::isSystemTrayAvailable()) { - bool tray = settings.value("tray", false).toBool(); - m_ui->trayInput->setChecked(tray); - if (tray) { - m_ui->hideTrayInput->setChecked(settings.value("hideTray", false).toBool()); - } else { - m_ui->hideTrayInput->setEnabled(false); - } - - connect(m_ui->trayInput, &QCheckBox::stateChanged, this, &PageGeneral::onTrayChecked); - connect(m_ui->hideTrayInput, &QCheckBox::stateChanged, this, &PageGeneral::onHideTrayChecked); - } else { - m_ui->trayInput->setEnabled(false); - m_ui->hideTrayInput->setEnabled(false); - m_ui->trayInput->setToolTip(tr("Tray is not available for your system")); //TODO translate - } - - connect(m_ui->downloadsPathButton, &QPushButton::clicked, this, &PageGeneral::onBrowseButtonClicked); -} - -PageGeneral::~PageGeneral() -{ - if (dialog != nullptr) { - dialog->deleteLater(); - } -} - -void PageGeneral::onBrowseButtonClicked() -{ - if (dialog == nullptr) { - QSettings settings; - dialog = new QFileDialog(this, tr("Select where downloads folder is going to be"), settings.value("downloadsPath").toString()); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->setAcceptMode(QFileDialog::AcceptSave); //I find it the most convinient way - dialog->setFileMode(QFileDialog::AnyFile); //Otherwise the directory is supposed to be - dialog->setOption(QFileDialog::ShowDirsOnly, true); //selected and not to be navigated - dialog->setOption(QFileDialog::DontConfirmOverwrite, true); - dialog->setModal(true); - connect(dialog, &QFileDialog::accepted, this, &PageGeneral::onDialogAccepted); - connect(dialog, &QFileDialog::destroyed, this, &PageGeneral::onDialogDestroyed); - dialog->show(); - } else { - dialog->show(); - dialog->raise(); - dialog->activateWindow(); - } -} - -void PageGeneral::onDialogAccepted() -{ - QStringList files = dialog->selectedFiles(); - QString path; - if (files.size() > 0) { - path = files[0]; - } else { - path = dialog->directory().canonicalPath(); - } - m_ui->downloadsPathInput->setText(path); - emit variableModified("downloadsPath", path); -} - -void PageGeneral::onDialogDestroyed() -{ - dialog = nullptr; -} - -void PageGeneral::onTrayChecked(int state) -{ - bool enabled = state == Qt::Checked; - emit variableModified("tray", enabled); - m_ui->hideTrayInput->setEnabled(enabled); - if (!enabled) { - m_ui->hideTrayInput->setChecked(false); - } -} - -void PageGeneral::onHideTrayChecked(int state) -{ - bool enabled = state == Qt::Checked; - emit variableModified("hideTray", enabled); -} diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h deleted file mode 100644 index 15738be..0000000 --- a/ui/widgets/settings/pagegeneral.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef PAGEGENERAL_H -#define PAGEGENERAL_H - -#include -#include -#include -#include -#include -#include - -namespace Ui -{ -class PageGeneral; -} - -/** - * @todo write docs - */ -class PageGeneral : public QWidget -{ - Q_OBJECT -public: - PageGeneral(QWidget* parent = nullptr); - ~PageGeneral(); - -signals: - void variableModified(const QString& key, const QVariant& value); - -private slots: - void onBrowseButtonClicked(); - void onDialogAccepted(); - void onDialogDestroyed(); - void onTrayChecked(int state); - void onHideTrayChecked(int state); - -private: - QScopedPointer m_ui; - QFileDialog* dialog; -}; - -#endif // PAGEGENERAL_H diff --git a/ui/widgets/settings/pagegeneral.ui b/ui/widgets/settings/pagegeneral.ui deleted file mode 100644 index e08b606..0000000 --- a/ui/widgets/settings/pagegeneral.ui +++ /dev/null @@ -1,74 +0,0 @@ - - - PageGeneral - - - - 0 - 0 - 477 - 310 - - - - - - - Downloads path - - - - - - - 6 - - - - - false - - - - - - - Browse - - - - - - - - - Tray icon - - - - - - - Mimimize Squawk to tray when closing main window - - - - - - - Hide tray icon - - - - - - - Hide tray icon when Squawk main window is visible - - - - - - - - diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp deleted file mode 100644 index 62dd80b..0000000 --- a/ui/widgets/settings/settings.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "settings.h" -#include "ui_settings.h" - -Settings::Settings(QWidget* parent): - QWidget(parent), - m_ui(new Ui::Settings()), - modifiedSettings() -{ - m_ui->setupUi(this); - - connect(m_ui->list, &QListWidget::currentItemChanged, this, &Settings::onCurrentPageChanged); - - connect(m_ui->General, &PageGeneral::variableModified, this, &Settings::onVariableModified); - connect(m_ui->Appearance, &PageAppearance::variableModified, this, &Settings::onVariableModified); - - connect(m_ui->applyButton, &QPushButton::clicked, this, &Settings::apply); - connect(m_ui->okButton, &QPushButton::clicked, this, &Settings::confirm); - connect(m_ui->cancelButton, &QPushButton::clicked, this, &Settings::close); -} - -Settings::~Settings() -{ -} - -void Settings::onCurrentPageChanged(QListWidgetItem* current) -{ - if (current != nullptr) { - m_ui->header->setText(current->text()); - - m_ui->content->setCurrentIndex(m_ui->list->currentRow()); - } -} - -void Settings::onVariableModified(const QString& key, const QVariant& value) -{ - modifiedSettings[key] = value; -} - -void Settings::apply() -{ - QSettings settings; - bool trayChanged = false; - for (const std::pair& pair: modifiedSettings) { - if (pair.first == "style") { - Shared::Global::setStyle(pair.second.toString()); - settings.setValue(pair.first, pair.second); - } else if (pair.first == "theme") { - Shared::Global::setTheme(pair.second.toString()); - settings.setValue(pair.first, pair.second); - } else if (pair.first == "downloadsPath") { - QString path = pair.second.toString(); - if (!Shared::isSubdirectoryOfSettings(path)) { - path = Shared::getAbsoluteWritablePath(path); - if (path.size() > 0) { - settings.setValue(pair.first, path); - emit changeDownloadsPath(path); - } - } - } else if (pair.first == "tray" || pair.first == "hideTray") { - bool oldValue = settings.value(pair.first, false).toBool(); - bool newValue = pair.second.toBool(); - if (oldValue != newValue) { - trayChanged = true; - settings.setValue(pair.first, pair.second); - } - } - } - - if (trayChanged) { - emit changeTray(settings.value("tray", false).toBool(), settings.value("hideTray", false).toBool()); - } - - modifiedSettings.clear(); -} - -void Settings::confirm() -{ - apply(); - close(); -} diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h deleted file mode 100644 index 59a65cb..0000000 --- a/ui/widgets/settings/settings.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef SETTINGS_H -#define SETTINGS_H - -#include -#include -#include -#include -#include - -#include "shared/global.h" -#include "shared/pathcheck.h" - -namespace Ui -{ -class Settings; -} - -/** - * @todo write docs - */ -class Settings : public QWidget -{ - Q_OBJECT -public: - Settings(QWidget* parent = nullptr); - ~Settings(); - -signals: - void changeDownloadsPath(const QString& path); - void changeTray(bool enable, bool hide); - -public slots: - void apply(); - void confirm(); - -protected slots: - void onCurrentPageChanged(QListWidgetItem* current); - void onVariableModified(const QString& key, const QVariant& value); - -private: - QScopedPointer m_ui; - std::map modifiedSettings; -}; - -#endif // SETTINGS_H diff --git a/ui/widgets/settings/settings.ui b/ui/widgets/settings/settings.ui deleted file mode 100644 index d97a3f2..0000000 --- a/ui/widgets/settings/settings.ui +++ /dev/null @@ -1,230 +0,0 @@ - - - Settings - - - - 0 - 0 - 502 - 363 - - - - Preferences - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoDragDrop - - - QAbstractItemView::ScrollPerPixel - - - QListView::Static - - - QListView::TopToBottom - - - false - - - QListView::Adjust - - - QListView::SinglePass - - - QListView::IconMode - - - true - - - Qt::AlignHCenter - - - 0 - - - - General - - - - .. - - - ItemIsSelectable|ItemIsEnabled - - - - - Appearance - - - - .. - - - ItemIsSelectable|ItemIsEnabled - - - - - - - - - 0 - 0 - - - - - - - Apply - - - - .. - - - - - - - Cancel - - - - .. - - - - - - - Ok - - - - .. - - - - - - - font-size: 14pt; - - - General - - - - - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - SettingsList - QListWidget -
ui/widgets/settings/settingslist.h
-
- - PageGeneral - QWidget -
ui/widgets/settings/pagegeneral.h
-
- - PageAppearance - QWidget -
ui/widgets/settings/pageappearance.h
-
-
- - -
diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp deleted file mode 100644 index ae071ff..0000000 --- a/ui/widgets/settings/settingslist.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "settingslist.h" - -SettingsList::SettingsList(QWidget* parent): - QListWidget(parent), - lastWidth(0) -{} - -SettingsList::~SettingsList() {} - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -void SettingsList::initViewItemOption(QStyleOptionViewItem* option) const { - QListWidget::initViewItemOption(option); - if (!iconSize().isValid()) - option->decorationSize.setWidth(lastWidth); - - option->rect.setWidth(lastWidth); -} -#else -QStyleOptionViewItem SettingsList::viewOptions() const { - QStyleOptionViewItem option = QListWidget::viewOptions(); - if (!iconSize().isValid()) - option.decorationSize.setWidth(lastWidth); - - option.rect.setWidth(lastWidth); - return option; -} -#endif - -void SettingsList::resizeEvent(QResizeEvent* event) { - lastWidth = event->size().width(); - QListWidget::resizeEvent(event); -} - -QRect SettingsList::visualRect(const QModelIndex& index) const { - QRect res = QListWidget::visualRect(index); - if (index.isValid()) - res.setWidth(lastWidth); - - return res; -} - diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt new file mode 100644 index 0000000..51cbaab --- /dev/null +++ b/ui/widgets/vcard/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(squawk PRIVATE + emailsmodel.cpp + emailsmodel.h + phonesmodel.cpp + phonesmodel.h + vcard.cpp + vcard.h + vcard.ui + ) diff --git a/ui/models/info/emails.cpp b/ui/widgets/vcard/emailsmodel.cpp similarity index 71% rename from ui/models/info/emails.cpp rename to ui/widgets/vcard/emailsmodel.cpp index 694c0c9..994fcc3 100644 --- a/ui/models/info/emails.cpp +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -16,35 +16,30 @@ * along with this program. If not, see . */ -#include "emails.h" +#include "emailsmodel.h" #include "shared/icons.h" -#include "shared/defines.h" #include -Models::EMails::EMails(bool p_edit, QObject* parent): +UI::VCard::EMailsModel::EMailsModel(bool p_edit, QObject* parent): QAbstractTableModel(parent), edit(p_edit), - deque() {} + deque() +{ +} -int Models::EMails::columnCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int UI::VCard::EMailsModel::columnCount(const QModelIndex& parent) const +{ return 3; } -int Models::EMails::rowCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int UI::VCard::EMailsModel::rowCount(const QModelIndex& parent) const +{ return deque.size(); - } -void Models::EMails::revertPreferred(quint32 row) { - setData(createIndex(row, 2), !isPreferred(row));} - -QString Models::EMails::getEmail(quint32 row) const { - return deque[row].address;} - -QVariant Models::EMails::data(const QModelIndex& index, int role) const { +QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const +{ if (index.isValid()) { int col = index.column(); switch (col) { @@ -87,31 +82,17 @@ QVariant Models::EMails::data(const QModelIndex& index, int role) const { return QVariant(); } -Qt::ItemFlags Models::EMails::flags(const QModelIndex& index) const { +Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const +{ Qt::ItemFlags f = QAbstractTableModel::flags(index); - if (edit && index.column() != 2) + if (edit && index.column() != 2) { f = Qt::ItemIsEditable | f; - + } return f; } -bool Models::EMails::setEditable(bool editable) { - if (edit != editable) { - edit = editable; - - if (deque.size() > 0) { - int lastRow = deque.size() - 1; - QModelIndex begin = createIndex(0, 0, &(deque[0])); - QModelIndex end = createIndex(lastRow, columnCount() - 1, &(deque[lastRow])); - emit dataChanged(begin, end); - } - return true; - } - return false; -} - - -bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, int role) { +bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ if (role == Qt::EditRole && checkIndex(index)) { Shared::VCard::Email& item = deque[index.row()]; switch (index.column()) { @@ -120,9 +101,9 @@ bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, in return true; case 1: { quint8 newRole = value.toUInt(); - if (newRole > Shared::VCard::Email::work) + if (newRole > Shared::VCard::Email::work) { return false; - + } item.role = static_cast(newRole); return true; } @@ -143,7 +124,8 @@ bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, in } -bool Models::EMails::dropPrefered() { +bool UI::VCard::EMailsModel::dropPrefered() +{ bool dropped = false; int i = 0; for (Shared::VCard::Email& email : deque) { @@ -158,25 +140,30 @@ bool Models::EMails::dropPrefered() { return dropped; } -QModelIndex Models::EMails::addNewEmptyLine() { +QModelIndex UI::VCard::EMailsModel::addNewEmptyLine() +{ beginInsertRows(QModelIndex(), deque.size(), deque.size()); deque.emplace_back(""); endInsertRows(); return createIndex(deque.size() - 1, 0, &(deque.back())); } -bool Models::EMails::isPreferred(quint32 row) const { - if (row < deque.size()) +bool UI::VCard::EMailsModel::isPreferred(quint32 row) const +{ + if (row < deque.size()) { return deque[row].prefered; - else + } else { return false; + } } -void Models::EMails::removeLines(quint32 index, quint32 count) { +void UI::VCard::EMailsModel::removeLines(quint32 index, quint32 count) +{ if (index < deque.size()) { quint32 maxCount = deque.size() - index; - if (count > maxCount) + if (count > maxCount) { count = maxCount; + } if (count > 0) { beginRemoveRows(QModelIndex(), index, index + count - 1); @@ -188,20 +175,34 @@ void Models::EMails::removeLines(quint32 index, quint32 count) { } } -void Models::EMails::getEmails(std::deque& emails) const { - for (const Shared::VCard::Email& my : deque) +void UI::VCard::EMailsModel::getEmails(std::deque& emails) const +{ + for (const Shared::VCard::Email& my : deque) { emails.emplace_back(my); + } } -void Models::EMails::setEmails(const std::deque& emails) { - if (deque.size() > 0) +void UI::VCard::EMailsModel::setEmails(const std::deque& emails) +{ + if (deque.size() > 0) { removeLines(0, deque.size()); + } if (emails.size() > 0) { beginInsertRows(QModelIndex(), 0, emails.size() - 1); - for (const Shared::VCard::Email& comming : emails) + for (const Shared::VCard::Email& comming : emails) { deque.emplace_back(comming); - + } endInsertRows(); } } + +void UI::VCard::EMailsModel::revertPreferred(quint32 row) +{ + setData(createIndex(row, 2), !isPreferred(row)); +} + +QString UI::VCard::EMailsModel::getEmail(quint32 row) const +{ + return deque[row].address; +} diff --git a/ui/models/info/emails.h b/ui/widgets/vcard/emailsmodel.h similarity index 87% rename from ui/models/info/emails.h rename to ui/widgets/vcard/emailsmodel.h index f07d238..bafbe9f 100644 --- a/ui/models/info/emails.h +++ b/ui/widgets/vcard/emailsmodel.h @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#ifndef MODELS_EMAILS_H -#define MODELS_EMAILS_H +#ifndef UI_VCARD_EMAILSMODEL_H +#define UI_VCARD_EMAILSMODEL_H #include #include @@ -26,14 +26,15 @@ #include "shared/vcard.h" -namespace Models { +namespace UI { +namespace VCard { -class EMails : public QAbstractTableModel { +class EMailsModel : public QAbstractTableModel +{ Q_OBJECT public: - EMails(bool edit = false, QObject *parent = nullptr); + EMailsModel(bool edit = false, QObject *parent = nullptr); - bool setEditable(bool editable); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -58,6 +59,6 @@ private: bool dropPrefered(); }; -} +}} -#endif // MODELS_EMAILS_H +#endif // UI_VCARD_EMAILSMODEL_H diff --git a/ui/models/info/phones.cpp b/ui/widgets/vcard/phonesmodel.cpp similarity index 73% rename from ui/models/info/phones.cpp rename to ui/widgets/vcard/phonesmodel.cpp index 2b6523a..d3cace8 100644 --- a/ui/models/info/phones.cpp +++ b/ui/widgets/vcard/phonesmodel.cpp @@ -16,35 +16,30 @@ * along with this program. If not, see . */ -#include "phones.h" +#include "phonesmodel.h" #include "shared/icons.h" -#include "shared/defines.h" #include -Models::Phones::Phones(bool p_edit, QObject* parent): +UI::VCard::PhonesModel::PhonesModel(bool p_edit, QObject* parent): QAbstractTableModel(parent), edit(p_edit), - deque() {} + deque() +{ +} -int Models::Phones::columnCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int UI::VCard::PhonesModel::columnCount(const QModelIndex& parent) const +{ return 4; } -int Models::Phones::rowCount(const QModelIndex& parent) const { - SHARED_UNUSED(parent); +int UI::VCard::PhonesModel::rowCount(const QModelIndex& parent) const +{ return deque.size(); } -void Models::Phones::revertPreferred(quint32 row) { - setData(createIndex(row, 3), !isPreferred(row)); -} - -QString Models::Phones::getPhone(quint32 row) const { - return deque[row].number;} - -QVariant Models::Phones::data(const QModelIndex& index, int role) const { +QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const +{ if (index.isValid()) { int col = index.column(); switch (col) { @@ -82,9 +77,9 @@ QVariant Models::Phones::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: return QVariant(); case Qt::DecorationRole: - if (deque[index.row()].prefered) + if (deque[index.row()].prefered) { return Shared::icon("favorite", false); - + } return QVariant(); default: return QVariant(); @@ -97,38 +92,25 @@ QVariant Models::Phones::data(const QModelIndex& index, int role) const { return QVariant(); } -QModelIndex Models::Phones::addNewEmptyLine() { +QModelIndex UI::VCard::PhonesModel::addNewEmptyLine() +{ beginInsertRows(QModelIndex(), deque.size(), deque.size()); deque.emplace_back("", Shared::VCard::Phone::other); endInsertRows(); return createIndex(deque.size() - 1, 0, &(deque.back())); } -Qt::ItemFlags Models::Phones::flags(const QModelIndex& index) const { +Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const +{ Qt::ItemFlags f = QAbstractTableModel::flags(index); - if (edit && index.column() != 3) + if (edit && index.column() != 3) { f = Qt::ItemIsEditable | f; - + } return f; } -bool Models::Phones::setEditable(bool editable) { - if (edit != editable) { - edit = editable; - - if (deque.size() > 0) { - int lastRow = deque.size() - 1; - QModelIndex begin = createIndex(0, 0, &(deque[0])); - QModelIndex end = createIndex(lastRow, columnCount() - 1, &(deque[lastRow])); - emit dataChanged(begin, end); - } - return true; - } - return false; -} - - -bool Models::Phones::dropPrefered() { +bool UI::VCard::PhonesModel::dropPrefered() +{ bool dropped = false; int i = 0; for (Shared::VCard::Phone& phone : deque) { @@ -143,23 +125,29 @@ bool Models::Phones::dropPrefered() { return dropped; } -void Models::Phones::getPhones(std::deque& phones) const { - for (const Shared::VCard::Phone& my : deque) +void UI::VCard::PhonesModel::getPhones(std::deque& phones) const +{ + for (const Shared::VCard::Phone& my : deque) { phones.emplace_back(my); + } } -bool Models::Phones::isPreferred(quint32 row) const { - if (row < deque.size()) +bool UI::VCard::PhonesModel::isPreferred(quint32 row) const +{ + if (row < deque.size()) { return deque[row].prefered; - else + } else { return false; + } } -void Models::Phones::removeLines(quint32 index, quint32 count) { +void UI::VCard::PhonesModel::removeLines(quint32 index, quint32 count) +{ if (index < deque.size()) { quint32 maxCount = deque.size() - index; - if (count > maxCount) + if (count > maxCount) { count = maxCount; + } if (count > 0) { beginRemoveRows(QModelIndex(), index, index + count - 1); @@ -171,7 +159,13 @@ void Models::Phones::removeLines(quint32 index, quint32 count) { } } -bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, int role) { +void UI::VCard::PhonesModel::revertPreferred(quint32 row) +{ + setData(createIndex(row, 3), !isPreferred(row)); +} + +bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ if (role == Qt::EditRole && checkIndex(index)) { Shared::VCard::Phone& item = deque[index.row()]; switch (index.column()) { @@ -180,17 +174,17 @@ bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, in return true; case 1: { quint8 newRole = value.toUInt(); - if (newRole > Shared::VCard::Phone::work) + if (newRole > Shared::VCard::Phone::work) { return false; - + } item.role = static_cast(newRole); return true; } case 2: { quint8 newType = value.toUInt(); - if (newType > Shared::VCard::Phone::other) + if (newType > Shared::VCard::Phone::other) { return false; - + } item.type = static_cast(newType); return true; } @@ -210,16 +204,22 @@ bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, in return false; } -void Models::Phones::setPhones(const std::deque& phones) { - if (deque.size() > 0) +void UI::VCard::PhonesModel::setPhones(const std::deque& phones) +{ + if (deque.size() > 0) { removeLines(0, deque.size()); - + } if (phones.size() > 0) { beginInsertRows(QModelIndex(), 0, phones.size() - 1); - for (const Shared::VCard::Phone& comming : phones) + for (const Shared::VCard::Phone& comming : phones) { deque.emplace_back(comming); - + } endInsertRows(); } } + +QString UI::VCard::PhonesModel::getPhone(quint32 row) const +{ + return deque[row].number; +} diff --git a/ui/models/info/phones.h b/ui/widgets/vcard/phonesmodel.h similarity index 79% rename from ui/models/info/phones.h rename to ui/widgets/vcard/phonesmodel.h index aea03ed..32d08b6 100644 --- a/ui/models/info/phones.h +++ b/ui/widgets/vcard/phonesmodel.h @@ -16,24 +16,29 @@ * along with this program. If not, see . */ -#ifndef MODELS_PHONES_H -#define MODELS_PHONES_H +#ifndef UI_VCARD_PHONESMODEL_H +#define UI_VCARD_PHONESMODEL_H #include #include #include "shared/vcard.h" -namespace Models { -class Phones : public QAbstractTableModel { +namespace UI { +namespace VCard { + +/** + * @todo write docs + */ +class PhonesModel : public QAbstractTableModel +{ Q_OBJECT public: - Phones(bool edit = false, QObject *parent = nullptr); + PhonesModel(bool edit = false, QObject *parent = nullptr); - bool setEditable(bool editable); QVariant data(const QModelIndex& index, int role) const override; - int columnCount(const QModelIndex& parent = QModelIndex()) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent) const override; + int rowCount(const QModelIndex& parent) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool isPreferred(quint32 row) const; @@ -55,6 +60,6 @@ private: bool dropPrefered(); }; -} +}} -#endif // MODELS_PHONES_H +#endif // UI_VCARD_PHONESMODEL_H diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp new file mode 100644 index 0000000..f1cfe3b --- /dev/null +++ b/ui/widgets/vcard/vcard.cpp @@ -0,0 +1,464 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vcard.h" +#include "ui_vcard.h" +#include "shared/icons.h" +#include + +#include + +const std::set VCard::supportedTypes = {"image/jpeg", "image/png"}; + +VCard::VCard(const QString& jid, bool edit, QWidget* parent): + QWidget(parent), + m_ui(new Ui::VCard()), + avatarButtonMargins(), + avatarMenu(nullptr), + editable(edit), + currentAvatarType(Shared::Avatar::empty), + currentAvatarPath(""), + progress(new Progress(100)), + progressLabel(new QLabel()), + overlay(new QWidget()), + contextMenu(new QMenu()), + emails(edit), + phones(edit), + roleDelegate(new ComboboxDelegate()), + phoneTypeDelegate(new ComboboxDelegate()) +{ + m_ui->setupUi(this); + m_ui->jabberID->setText(jid); + m_ui->jabberID->setReadOnly(true); + + QAction* setAvatar = m_ui->actionSetAvatar; + QAction* clearAvatar = m_ui->actionClearAvatar; + + connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar); + connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar); + + setAvatar->setEnabled(true); + clearAvatar->setEnabled(false); + + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); + + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); + + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->emailsView->setModel(&emails); + m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->setColumnWidth(2, 25); + m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); + m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->phonesView->setModel(&phones); + m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); + m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); + m_ui->phonesView->setColumnWidth(3, 25); + m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); + m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); + connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); + + if (edit) { + avatarMenu = new QMenu(); + m_ui->avatarButton->setMenu(avatarMenu); + avatarMenu->addAction(setAvatar); + avatarMenu->addAction(clearAvatar); + m_ui->title->setText(tr("Account %1 card").arg(jid)); + } else { + m_ui->buttonBox->hide(); + m_ui->fullName->setReadOnly(true); + m_ui->firstName->setReadOnly(true); + m_ui->middleName->setReadOnly(true); + m_ui->lastName->setReadOnly(true); + m_ui->nickName->setReadOnly(true); + m_ui->birthday->setReadOnly(true); + m_ui->organizationName->setReadOnly(true); + m_ui->organizationDepartment->setReadOnly(true); + m_ui->organizationTitle->setReadOnly(true); + m_ui->organizationRole->setReadOnly(true); + m_ui->description->setReadOnly(true); + m_ui->url->setReadOnly(true); + m_ui->title->setText(tr("Contact %1 card").arg(jid)); + } + + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close); + + avatarButtonMargins = m_ui->avatarButton->size(); + + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + m_ui->avatarButton->setIconSize(QSize(height, height)); + + QGridLayout* gr = static_cast(layout()); + gr->addWidget(overlay, 0, 0, 4, 1); + QVBoxLayout* nl = new QVBoxLayout(); + QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); + opacity->setOpacity(0.8); + overlay->setLayout(nl); + overlay->setBackgroundRole(QPalette::Base); + overlay->setAutoFillBackground(true); + overlay->setGraphicsEffect(opacity); + progressLabel->setAlignment(Qt::AlignCenter); + QFont pf = progressLabel->font(); + pf.setBold(true); + pf.setPointSize(26); + progressLabel->setFont(pf); + progressLabel->setWordWrap(true); + nl->addStretch(); + nl->addWidget(progress); + nl->addWidget(progressLabel); + nl->addStretch(); + overlay->hide(); +} + +VCard::~VCard() +{ + if (editable) { + avatarMenu->deleteLater(); + } + + phoneTypeDelegate->deleteLater(); + roleDelegate->deleteLater(); + contextMenu->deleteLater(); +} + +void VCard::setVCard(const QString& jid, const Shared::VCard& card) +{ + m_ui->jabberID->setText(jid); + setVCard(card); +} + +void VCard::setVCard(const Shared::VCard& card) +{ + m_ui->fullName->setText(card.getFullName()); + m_ui->firstName->setText(card.getFirstName()); + m_ui->middleName->setText(card.getMiddleName()); + m_ui->lastName->setText(card.getLastName()); + m_ui->nickName->setText(card.getNickName()); + m_ui->birthday->setDate(card.getBirthday()); + m_ui->organizationName->setText(card.getOrgName()); + m_ui->organizationDepartment->setText(card.getOrgUnit()); + m_ui->organizationTitle->setText(card.getOrgTitle()); + m_ui->organizationRole->setText(card.getOrgRole()); + m_ui->description->setText(card.getDescription()); + m_ui->url->setText(card.getUrl()); + + QDateTime receivingTime = card.getReceivingTime(); + m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); + currentAvatarType = card.getAvatarType(); + currentAvatarPath = card.getAvatarPath(); + + updateAvatar(); + + const std::deque& ems = card.getEmails(); + const std::deque& phs = card.getPhones(); + emails.setEmails(ems); + phones.setPhones(phs); +} + +QString VCard::getJid() const +{ + return m_ui->jabberID->text(); +} + +void VCard::onButtonBoxAccepted() +{ + Shared::VCard card; + card.setFullName(m_ui->fullName->text()); + card.setFirstName(m_ui->firstName->text()); + card.setMiddleName(m_ui->middleName->text()); + card.setLastName(m_ui->lastName->text()); + card.setNickName(m_ui->nickName->text()); + card.setBirthday(m_ui->birthday->date()); + card.setDescription(m_ui->description->toPlainText()); + card.setUrl(m_ui->url->text()); + card.setOrgName(m_ui->organizationName->text()); + card.setOrgUnit(m_ui->organizationDepartment->text()); + card.setOrgRole(m_ui->organizationRole->text()); + card.setOrgTitle(m_ui->organizationTitle->text()); + card.setAvatarPath(currentAvatarPath); + card.setAvatarType(currentAvatarType); + + emails.getEmails(card.getEmails()); + phones.getPhones(card.getPhones()); + + emit saveVCard(card); +} + +void VCard::onClearAvatar() +{ + currentAvatarType = Shared::Avatar::empty; + currentAvatarPath = ""; + + updateAvatar(); +} + +void VCard::onSetAvatar() +{ + QFileDialog* d = new QFileDialog(this, tr("Chose your new avatar")); + d->setFileMode(QFileDialog::ExistingFile); + d->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); + + connect(d, &QFileDialog::accepted, this, &VCard::onAvatarSelected); + connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater); + + d->show(); +} + +void VCard::updateAvatar() +{ + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + switch (currentAvatarType) { + case Shared::Avatar::empty: + m_ui->avatarButton->setIcon(Shared::icon("user", true)); + m_ui->avatarButton->setIconSize(QSize(height, height)); + m_ui->actionClearAvatar->setEnabled(false); + break; + case Shared::Avatar::autocreated: + case Shared::Avatar::valid: + QPixmap pixmap(currentAvatarPath); + qreal h = pixmap.height(); + qreal w = pixmap.width(); + qreal aspectRatio = w / h; + m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); + m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); + m_ui->actionClearAvatar->setEnabled(true); + break; + } +} + +void VCard::onAvatarSelected() +{ + QFileDialog* d = static_cast(sender()); + QMimeDatabase db; + QString path = d->selectedFiles().front(); + QMimeType type = db.mimeTypeForFile(path); + d->deleteLater(); + + if (supportedTypes.find(type.name()) == supportedTypes.end()) { + qDebug() << "Selected for avatar file is not supported, skipping"; + } else { + QImage src(path); + QImage dst; + if (src.width() > 160 || src.height() > 160) { + dst = src.scaled(160, 160, Qt::KeepAspectRatio); + } + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + m_ui->jabberID->text() + ".temp." + type.preferredSuffix(); + QFile oldTemp(path); + if (oldTemp.exists()) { + if (!oldTemp.remove()) { + qDebug() << "Error removing old temp avatar" << path; + return; + } + } + bool success = dst.save(path); + if (success) { + currentAvatarPath = path; + currentAvatarType = Shared::Avatar::valid; + + updateAvatar(); + } else { + qDebug() << "couldn't save avatar" << path << ", skipping"; + } + } +} + +void VCard::showProgress(const QString& line) +{ + progressLabel->setText(line); + overlay->show(); + progress->start(); +} + +void VCard::hideProgress() +{ + overlay->hide(); + progress->stop(); +} + +void VCard::onContextMenu(const QPoint& point) +{ + contextMenu->clear(); + bool hasMenu = false; + QAbstractItemView* snd = static_cast(sender()); + if (snd == m_ui->emailsView) { + hasMenu = true; + if (editable) { + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); + connect(add, &QAction::triggered, this, &VCard::onAddEmail); + + QItemSelectionModel* sm = m_ui->emailsView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (emails.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses")); + connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); + } + } + + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard")); + connect(cp, &QAction::triggered, this, &VCard::onCopyEmail); + } else if (snd == m_ui->phonesView) { + hasMenu = true; + if (editable) { + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number")); + connect(add, &QAction::triggered, this, &VCard::onAddPhone); + + QItemSelectionModel* sm = m_ui->phonesView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (phones.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers")); + connect(del, &QAction::triggered, this, &VCard::onRemovePhone); + } + } + + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard")); + connect(cp, &QAction::triggered, this, &VCard::onCopyPhone); + } + + if (hasMenu) { + contextMenu->popup(snd->viewport()->mapToGlobal(point)); + } +} + +void VCard::onAddEmail() +{ + QModelIndex index = emails.addNewEmptyLine(); + m_ui->emailsView->setCurrentIndex(index); + m_ui->emailsView->edit(index); +} + +void VCard::onAddAddress() +{ + +} +void VCard::onAddPhone() +{ + QModelIndex index = phones.addNewEmptyLine(); + m_ui->phonesView->setCurrentIndex(index); + m_ui->phonesView->edit(index); +} +void VCard::onRemoveAddress() +{ +} +void VCard::onRemoveEmail() +{ + QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + emails.removeLines(current, 1); + prev = current; + } + } +} + +void VCard::onRemovePhone() +{ + QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + phones.removeLines(current, 1); + prev = current; + } + } +} + +void VCard::onCopyEmail() +{ + QList selection(m_ui->emailsView->selectionModel()->selectedRows()); + + QList addrs; + for (const QModelIndex& index : selection) { + addrs.push_back(emails.getEmail(index.row())); + } + + QString list = addrs.join("\n"); + + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); +} + +void VCard::onCopyPhone() +{ + QList selection(m_ui->phonesView->selectionModel()->selectedRows()); + + QList phs; + for (const QModelIndex& index : selection) { + phs.push_back(phones.getPhone(index.row())); + } + + QString list = phs.join("\n"); + + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); +} diff --git a/ui/widgets/vcard/vcard.h b/ui/widgets/vcard/vcard.h new file mode 100644 index 0000000..4d579e1 --- /dev/null +++ b/ui/widgets/vcard/vcard.h @@ -0,0 +1,106 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VCARD_H +#define VCARD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared/vcard.h" +#include "emailsmodel.h" +#include "phonesmodel.h" +#include "ui/utils/progress.h" +#include "ui/utils/comboboxdelegate.h" + +namespace Ui +{ +class VCard; +} + +/** + * @todo write docs + */ +class VCard : public QWidget +{ + Q_OBJECT +public: + VCard(const QString& jid, bool edit = false, QWidget* parent = nullptr); + ~VCard(); + + void setVCard(const Shared::VCard& card); + void setVCard(const QString& jid, const Shared::VCard& card); + QString getJid() const; + void showProgress(const QString& = ""); + void hideProgress(); + +signals: + void saveVCard(const Shared::VCard& card); + +private slots: + void onButtonBoxAccepted(); + void onClearAvatar(); + void onSetAvatar(); + void onAvatarSelected(); + void onAddAddress(); + void onRemoveAddress(); + void onAddEmail(); + void onCopyEmail(); + void onRemoveEmail(); + void onAddPhone(); + void onCopyPhone(); + void onRemovePhone(); + void onContextMenu(const QPoint& point); + +private: + QScopedPointer m_ui; + QSize avatarButtonMargins; + QMenu* avatarMenu; + bool editable; + Shared::Avatar currentAvatarType; + QString currentAvatarPath; + Progress* progress; + QLabel* progressLabel; + QWidget* overlay; + QMenu* contextMenu; + UI::VCard::EMailsModel emails; + UI::VCard::PhonesModel phones; + ComboboxDelegate* roleDelegate; + ComboboxDelegate* phoneTypeDelegate; + + static const std::set supportedTypes; + +private: + void updateAvatar(); +}; + +#endif // VCARD_H diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui new file mode 100644 index 0000000..26db8f9 --- /dev/null +++ b/ui/widgets/vcard/vcard.ui @@ -0,0 +1,892 @@ + + + VCard + + + true + + + + 0 + 0 + 578 + 671 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + font: 16pt + + + Contact john@dow.org card + + + Qt::AlignCenter + + + + + + + font: italic 8pt; + + + Received 12.07.2007 at 17.35 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + Qt::TabFocus + + + QTabWidget::North + + + QTabWidget::Rounded + + + 0 + + + Qt::ElideNone + + + true + + + false + + + + General + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + font: 600 16pt; + + + Organization + + + Qt::AlignCenter + + + + + + + QLayout::SetDefaultConstraint + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Middle name + + + middleName + + + + + + + First name + + + firstName + + + + + + + Last name + + + lastName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Nick name + + + nickName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Birthday + + + birthday + + + + + + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Organization name + + + organizationName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Unit / Department + + + organizationDepartment + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Role / Profession + + + organizationRole + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Job title + + + organizationTitle + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + Full name + + + fullName + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + font: 600 24pt ; + + + General + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + font: 600 16pt; + + + QFrame::NoFrame + + + QFrame::Plain + + + Personal information + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + .. + + + + 0 + 0 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonIconOnly + + + Qt::NoArrow + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Contact + + + + 0 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + font: 600 24pt ; + + + Contact + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 566 + 498 + + + + + + + Qt::Horizontal + + + + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + font: 600 16pt; + + + Addresses + + + Qt::AlignCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + font: 600 16pt; + + + E-Mail addresses + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Jabber ID + + + jabberID + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Web site + + + url + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + font: 600 16pt; + + + Phone numbers + + + Qt::AlignCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + + + + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + font: 600 24pt ; + + + Description + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Save + + + false + + + + + + + + + + .. + + + Set avatar + + + + + + .. + + + Clear avatar + + + + + fullName + firstName + middleName + lastName + nickName + birthday + avatarButton + organizationName + organizationDepartment + organizationRole + organizationTitle + jabberID + url + description + + + + + +