Cinnamon-2.8.6/0000775000175000017500000000000012625666430012227 5ustar fabiofabioCinnamon-2.8.6/.settings/0000775000175000017500000000000012625666430014145 5ustar fabiofabioCinnamon-2.8.6/.settings/.jsdtscope0000664000175000017500000000067312625666430016152 0ustar fabiofabio Cinnamon-2.8.6/.settings/org.eclipse.cdt.ui.prefs0000664000175000017500000000022212625666430020601 0ustar fabiofabio#Fri Nov 28 14:33:30 EST 2008 eclipse.preferences.version=1 formatter_profile=org.eclipse.cdt.ui.default.gnu_profile formatter_settings_version=1 Cinnamon-2.8.6/.settings/org.eclipse.wst.jsdt.ui.prefs0000664000175000017500000000070412625666430021614 0ustar fabiofabio#Fri Nov 28 14:39:12 EST 2008 eclipse.preferences.version=1 formatter_profile=_gjs formatter_settings_version=11 org.eclipse.wst.jsdt.ui.exception.name=e org.eclipse.wst.jsdt.ui.gettersetter.use.is=true org.eclipse.wst.jsdt.ui.javadoc=false org.eclipse.wst.jsdt.ui.keywordthis=false org.eclipse.wst.jsdt.ui.overrideannotation=true org.eclipse.wst.jsdt.ui.text.custom_code_templates= Cinnamon-2.8.6/.settings/org.eclipse.wst.jsdt.ui.superType.name0000664000175000017500000000000612625666430023407 0ustar fabiofabioWindowCinnamon-2.8.6/.settings/org.eclipse.wst.jsdt.core.prefs0000664000175000017500000006466412625666430022146 0ustar fabiofabio#Fri Nov 28 14:43:43 EST 2008 eclipse.preferences.version=1 org.eclipse.wst.jsdt.core.codeComplete.argumentPrefixes= org.eclipse.wst.jsdt.core.codeComplete.argumentSuffixes= org.eclipse.wst.jsdt.core.codeComplete.fieldPrefixes= org.eclipse.wst.jsdt.core.codeComplete.fieldSuffixes= org.eclipse.wst.jsdt.core.codeComplete.localPrefixes= org.eclipse.wst.jsdt.core.codeComplete.localSuffixes= org.eclipse.wst.jsdt.core.codeComplete.staticFieldPrefixes= org.eclipse.wst.jsdt.core.codeComplete.staticFieldSuffixes= org.eclipse.wst.jsdt.core.compiler.codegen.inlineJsrBytecode=disabled org.eclipse.wst.jsdt.core.compiler.codegen.targetPlatform=1.2 org.eclipse.wst.jsdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.wst.jsdt.core.compiler.compliance=1.4 org.eclipse.wst.jsdt.core.compiler.debug.lineNumber=generate org.eclipse.wst.jsdt.core.compiler.debug.localVariable=generate org.eclipse.wst.jsdt.core.compiler.debug.sourceFile=generate org.eclipse.wst.jsdt.core.compiler.problem.assertIdentifier=warning org.eclipse.wst.jsdt.core.compiler.problem.deprecation=warning org.eclipse.wst.jsdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.wst.jsdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.wst.jsdt.core.compiler.problem.discouragedReference=warning org.eclipse.wst.jsdt.core.compiler.problem.emptyStatement=ignore org.eclipse.wst.jsdt.core.compiler.problem.enumIdentifier=warning org.eclipse.wst.jsdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.wst.jsdt.core.compiler.problem.fieldHiding=ignore org.eclipse.wst.jsdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.wst.jsdt.core.compiler.problem.forbiddenReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.wst.jsdt.core.compiler.problem.indirectStaticAccess=ignore org.eclipse.wst.jsdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.wst.jsdt.core.compiler.problem.looseVarDecleration=warning org.eclipse.wst.jsdt.core.compiler.problem.missingDeprecatedAnnotation=ignore org.eclipse.wst.jsdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.wst.jsdt.core.compiler.problem.nonExternalizedStringLiteral=ignore org.eclipse.wst.jsdt.core.compiler.problem.nullReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.optionalSemicolon=warning org.eclipse.wst.jsdt.core.compiler.problem.parameterAssignment=ignore org.eclipse.wst.jsdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore org.eclipse.wst.jsdt.core.compiler.problem.potentialNullReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.redundantNullCheck=ignore org.eclipse.wst.jsdt.core.compiler.problem.undefinedField=warning org.eclipse.wst.jsdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.wst.jsdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.wst.jsdt.core.compiler.problem.unresolvedFieldReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.unresolvedMethodReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.unresolvedTypeReference=ignore org.eclipse.wst.jsdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled org.eclipse.wst.jsdt.core.compiler.problem.unusedLabel=warning org.eclipse.wst.jsdt.core.compiler.problem.unusedLocal=warning org.eclipse.wst.jsdt.core.compiler.problem.unusedParameter=ignore org.eclipse.wst.jsdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled org.eclipse.wst.jsdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.wst.jsdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.wst.jsdt.core.compiler.source=1.3 org.eclipse.wst.jsdt.core.formatter.align_type_members_on_columns=false org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_assignment=0 org.eclipse.wst.jsdt.core.formatter.alignment_for_binary_expression=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_compact_if=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_conditional_expression=80 org.eclipse.wst.jsdt.core.formatter.alignment_for_enum_constants=0 org.eclipse.wst.jsdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_multiple_fields=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_selector_in_method_invocation=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_superclass_in_type_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.wst.jsdt.core.formatter.blank_lines_after_imports=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_after_package=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_field=0 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_first_class_body_declaration=0 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_imports=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_member_type=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_method=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_new_chunk=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_before_package=0 org.eclipse.wst.jsdt.core.formatter.blank_lines_between_import_groups=1 org.eclipse.wst.jsdt.core.formatter.blank_lines_between_type_declarations=0 org.eclipse.wst.jsdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_array_initializer=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_block=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_block_in_case=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_constructor_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_constant=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_method_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_objlit_initializer=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_switch=end_of_line org.eclipse.wst.jsdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false org.eclipse.wst.jsdt.core.formatter.comment.format_block_comments=true org.eclipse.wst.jsdt.core.formatter.comment.format_header=false org.eclipse.wst.jsdt.core.formatter.comment.format_html=true org.eclipse.wst.jsdt.core.formatter.comment.format_javadoc_comments=true org.eclipse.wst.jsdt.core.formatter.comment.format_line_comments=true org.eclipse.wst.jsdt.core.formatter.comment.format_source_code=true org.eclipse.wst.jsdt.core.formatter.comment.indent_parameter_description=true org.eclipse.wst.jsdt.core.formatter.comment.indent_root_tags=true org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_before_root_tags=insert org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_for_parameter=insert org.eclipse.wst.jsdt.core.formatter.comment.line_length=80 org.eclipse.wst.jsdt.core.formatter.compact_else_if=true org.eclipse.wst.jsdt.core.formatter.continuation_indentation=2 org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_array_initializer=2 org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_objlit_initializer=1 org.eclipse.wst.jsdt.core.formatter.format_guardian_clause_on_one_line=false org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_type_header=true org.eclipse.wst.jsdt.core.formatter.indent_breaks_compare_to_cases=true org.eclipse.wst.jsdt.core.formatter.indent_empty_lines=false org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_block=true org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_switch=false org.eclipse.wst.jsdt.core.formatter.indentation.size=4 org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_annotation=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_comma_in_objlit_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_objlit_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_objlit_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_block=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_constant=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_method_body=insert org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_and_in_type_parameter=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_binary_operator=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_brace_in_block=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_paren_in_cast=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_assert=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_case=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_conditional=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_for=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_annotation=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_increments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_inits=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_parameters=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_question_in_wildcard=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_unary_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_and_in_type_parameter=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_binary_operator=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_assert=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_case=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_conditional=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_default=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_for=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_annotation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_ellipsis=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_block=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_switch=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_catch=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_for=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_if=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_switch=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_while=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_postfix_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_question_in_wildcard=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert org.eclipse.wst.jsdt.core.formatter.keep_else_statement_on_same_line=false org.eclipse.wst.jsdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.wst.jsdt.core.formatter.keep_empty_objlit_initializer_on_one_line=false org.eclipse.wst.jsdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.wst.jsdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.wst.jsdt.core.formatter.lineSplit=80 org.eclipse.wst.jsdt.core.formatter.never_indent_block_comments_on_first_column=false org.eclipse.wst.jsdt.core.formatter.never_indent_line_comments_on_first_column=false org.eclipse.wst.jsdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 org.eclipse.wst.jsdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.wst.jsdt.core.formatter.put_empty_statement_on_new_line=true org.eclipse.wst.jsdt.core.formatter.tabulation.char=space org.eclipse.wst.jsdt.core.formatter.tabulation.size=4 org.eclipse.wst.jsdt.core.formatter.use_tabs_only_for_leading_indentations=false org.eclipse.wst.jsdt.core.formatter.wrap_before_binary_operator=true Cinnamon-2.8.6/.settings/org.eclipse.cdt.core.prefs0000664000175000017500000002776112625666430021135 0ustar fabiofabio#Fri Nov 28 14:33:30 EST 2008 eclipse.preferences.version=1 org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration=80 org.eclipse.cdt.core.formatter.alignment_for_compact_if=0 org.eclipse.cdt.core.formatter.alignment_for_conditional_expression=80 org.eclipse.cdt.core.formatter.alignment_for_declarator_list=16 org.eclipse.cdt.core.formatter.alignment_for_enumerator_list=48 org.eclipse.cdt.core.formatter.alignment_for_expression_list=0 org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.cdt.core.formatter.brace_position_for_array_initializer=next_line_shifted org.eclipse.cdt.core.formatter.brace_position_for_block=next_line_shifted org.eclipse.cdt.core.formatter.brace_position_for_block_in_case=next_line_shifted org.eclipse.cdt.core.formatter.brace_position_for_method_declaration=next_line org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration=next_line org.eclipse.cdt.core.formatter.brace_position_for_switch=next_line_shifted org.eclipse.cdt.core.formatter.brace_position_for_type_declaration=next_line org.eclipse.cdt.core.formatter.compact_else_if=true org.eclipse.cdt.core.formatter.continuation_indentation=2 org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer=2 org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line=false org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header=false org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier=true org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header=true org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases=true org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header=true org.eclipse.cdt.core.formatter.indent_empty_lines=false org.eclipse.cdt.core.formatter.indent_statements_compare_to_block=true org.eclipse.cdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch=false org.eclipse.cdt.core.formatter.indentation.size=8 org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration=insert org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement=insert org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration=insert org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement=insert org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block=insert org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.cdt.core.formatter.insert_space_after_binary_operator=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.cdt.core.formatter.insert_space_after_unary_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.cdt.core.formatter.insert_space_before_binary_operator=insert org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause=insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while=insert org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line=false org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.cdt.core.formatter.lineSplit=80 org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line=true org.eclipse.cdt.core.formatter.tabulation.char=space org.eclipse.cdt.core.formatter.tabulation.size=2 org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations=false Cinnamon-2.8.6/.settings/org.eclipse.wst.jsdt.ui.superType.container0000664000175000017500000000006112625666430024452 0ustar fabiofabioorg.eclipse.wst.jsdt.launching.baseBrowserLibraryCinnamon-2.8.6/restore0000775000175000017500000000014312625666430013636 0ustar fabiofabio#!/bin/bash rm -rf /usr/share/cinnamon/js mv /usr/share/cinnamon/js-backup /usr/share/cinnamon/js Cinnamon-2.8.6/COPYING0000664000175000017500000004310712625666430013267 0ustar fabiofabio GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Cinnamon-2.8.6/debian/0000775000175000017500000000000012625666430013451 5ustar fabiofabioCinnamon-2.8.6/test0000775000175000017500000000107612625666430013140 0ustar fabiofabio#!/bin/bash rm -rf /usr/share/cinnamon/js-backup mv /usr/share/cinnamon/js /usr/share/cinnamon/js-backup cp -R js /usr/share/cinnamon/ rm -rf /usr/share/cinnamon/theme-backup mv /usr/share/cinnamon/theme /usr/share/cinnamon/theme-backup cp -R data/theme /usr/share/cinnamon/ cp data/org.cinnamon.gschema.xml /usr/share/glib-2.0/schemas/ glib-compile-schemas /usr/share/glib-2.0/schemas/ sudo rm -rf /usr/lib/cinnamon-settings sudo rm -rf /usr/lib/cinnamon-menu-editor sudo cp -R files/usr / sudo cp -R files/etc / sudo glib-compile-schemas /usr/share/glib-2.0/schemas/ Cinnamon-2.8.6/files/0000775000175000017500000000000012625666430013331 5ustar fabiofabioCinnamon-2.8.6/files/usr/0000775000175000017500000000000012625666430014142 5ustar fabiofabioCinnamon-2.8.6/files/usr/bin/0000775000175000017500000000000012625666430014712 5ustar fabiofabioCinnamon-2.8.6/files/usr/bin/cinnamon2d0000775000175000017500000000033512625666430016671 0ustar fabiofabio#!/bin/bash CLUTTER_PAINT=disable-clipped-redraws:disable-culling LIBGL_ALWAYS_SOFTWARE=1 CINNAMON_SOFTWARE_RENDERING=1 CINNAMON_2D=1 CINNAMON_SLOWDOWN_FACTOR=0.0001 MUFFIN_NO_SHADOWS=1 CLUTTER_DEFAULT_FPS=15 cinnamon $@ Cinnamon-2.8.6/files/usr/bin/cinnamon-session-cinnamon2d0000775000175000017500000000007412625666430022152 0ustar fabiofabio#! /bin/sh exec cinnamon-session --session cinnamon2d "$@" Cinnamon-2.8.6/files/usr/bin/cinnamon-settings-users0000775000175000017500000000017112625666430021436 0ustar fabiofabio#!/usr/bin/python2 import os os.system("gksu /usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.py") Cinnamon-2.8.6/files/usr/bin/cinnamon-launcher0000775000175000017500000000264712625666430020252 0ustar fabiofabio#! /usr/bin/python2 # -*- coding=utf-8 -*- FALLBACK_COMMAND = "metacity" FALLBACK_ARGS = ("--replace",) import os, sys, gettext import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk gettext.install("cinnamon", "/usr/share/locale") def confirm_restart(): d = Gtk.MessageDialog(parent=None, flags=0, message_type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.YES_NO) d.set_keep_above(True) d.set_markup("%s\n\n%s" % (_("Cinnamon just crashed. You are currently running in Fallback Mode."), _("Do you want to restart Cinnamon?"))) d.show_all() resp = d.run() d.destroy() return (resp == Gtk.ResponseType.YES) if __name__ == "__main__": cinnamon_pid = os.fork() if cinnamon_pid == 0: os.execvp("cinnamon", ("cinnamon", "--replace", ) + tuple(sys.argv[1:])) else: exit_status = os.waitpid(cinnamon_pid, 0)[1] if exit_status != 0: if os.fork() == 0: if os.path.exists("/usr/bin/gnome-panel"): os.system("gnome-panel --replace &") elif os.path.exists("/usr/bin/tint2"): os.system("killall tint2") os.system("tint2 &") os.execvp(FALLBACK_COMMAND, (FALLBACK_COMMAND,) + FALLBACK_ARGS) else: if confirm_restart(): os.execvp(sys.argv[0], (sys.argv[0], "--replace")) Cinnamon-2.8.6/files/usr/bin/cinnamon-slideshow0000775000175000017500000000016112625666430020437 0ustar fabiofabio#!/usr/bin/python2 import os os.execvp("/usr/share/cinnamon/cinnamon-slideshow/cinnamon-slideshow.py", ("",)) Cinnamon-2.8.6/files/usr/bin/cinnamon-preview-gtk-theme0000775000175000017500000000211612625666430022004 0ustar fabiofabio#!/usr/bin/env python2 import gi, sys from gi.repository import Gtk if __name__ == '__main__': import signal signal.signal(signal.SIGINT, signal.SIG_DFL) if len(sys.argv) != 2: print "Usage: cinnamon-preview-gtk-theme theme-name" exit(1) else: theme_name = sys.argv[1] settings = Gtk.Settings.get_default() settings.set_string_property ("gtk-theme-name", theme_name, "gtkrc:0"); window = Gtk.Window() vbox = Gtk.VBox() hbox = Gtk.HBox() button = Gtk.Button() check = Gtk.CheckButton() check.set_active(True) radio = Gtk.RadioButton() hbox.pack_start(button, False, False, 2) button.set_label("Button") hbox.pack_start(check, False, False, 2) hbox.pack_start(radio, False, False, 2) vbox.pack_start(hbox, False, False, 2) window.add(vbox) window.set_default_size(320, 200) window.set_decorated(False) window.show_all() window.connect("destroy", Gtk.main_quit) Gtk.main() Cinnamon-2.8.6/files/usr/bin/cinnamon-menu-editor0000775000175000017500000000056412625666430020675 0ustar fabiofabio#! /usr/bin/python2 -OOt import sys sys.path.insert(0,'/usr/share/cinnamon/cinnamon-menu-editor') from cme import MainWindow def main(): try: from MenuEditor import config datadir = config.pkgdatadir version = config.VERSION except: datadir = '.' version = '0.9' app = MainWindow.MainWindow(datadir, version) app.run() if __name__ == '__main__': main() Cinnamon-2.8.6/files/usr/bin/cinnamon-looking-glass0000775000175000017500000000022412625666430021207 0ustar fabiofabio#!/usr/bin/python2 import os, sys os.execvp("/usr/share/cinnamon/cinnamon-looking-glass/cinnamon-looking-glass.py", ("",) + tuple(sys.argv[1:])) Cinnamon-2.8.6/files/usr/bin/cinnamon-json-makepot0000775000175000017500000000022212625666430021043 0ustar fabiofabio#!/usr/bin/python2 import os, sys os.execvp("/usr/share/cinnamon/cinnamon-json-makepot/cinnamon-json-makepot.py", ("",) + tuple(sys.argv[1:])) Cinnamon-2.8.6/files/usr/bin/cinnamon-killer-daemon0000775000175000017500000000332712625666430021170 0ustar fabiofabio#!/usr/bin/python2 # -*- coding: utf-8 -*- import gi gi.require_version('Keybinder', '3.0') gi.require_version('Gtk', '3.0') from gi.repository import Keybinder from gi.repository import Gtk, Gio import os import syslog SETTINGS_SCHEMA = 'org.cinnamon.desktop.keybindings.media-keys' SETTINGS_KEY = 'restart-cinnamon' class KillerDaemon: def __init__(self): Keybinder.init() self.bindings = None self.settings = Gio.Settings(schema_id=SETTINGS_SCHEMA) self.apply_bindings() self.settings.connect('changed::' + SETTINGS_KEY, self.apply_bindings) def apply_bindings(self, settings=None, key=None): # Ubind the previous bindings try: if self.bindings is not None: for binding in self.bindings: Keybinder.unbind(binding) except Exception, detail: syslog.syslog(detail) # Get the new ones self.bindings = self.settings.get_strv(SETTINGS_KEY) # Bind them try: if self.bindings is not None: for binding in self.bindings: if Keybinder.bind(binding, self.restart_cinnamon, None): syslog.syslog("Bound Cinnamon restart to %s." % binding) except Exception, detail: syslog.syslog(detail) def restart_cinnamon(self, keystring, data): os.system("nemo -n &") # restart nemo if it's not running already os.system("cinnamon-settings-daemon &") # restart CSD if it's not running already os.system("cinnamon --replace &") # restart Cinnamon whether it's running or not (can be handy in case of a DE freeze) if __name__ == '__main__': daemon = KillerDaemon() Gtk.main() Cinnamon-2.8.6/files/usr/bin/cinnamon-subprocess-wrapper0000775000175000017500000000066612625666430022316 0ustar fabiofabio#! /usr/bin/env python2 # -*- coding=utf-8 -*- import subprocess import sys import dbus if __name__ == "__main__": process_id = int(sys.argv[1]) result = subprocess.check_output(sys.argv[2:]) session_bus = dbus.SessionBus() dbus = session_bus.get_object("org.Cinnamon", "/org/Cinnamon") PushSubprocessResult = dbus.get_dbus_method('PushSubprocessResult', 'org.Cinnamon') PushSubprocessResult(process_id, result) Cinnamon-2.8.6/files/usr/bin/cinnamon-desktop-editor0000775000175000017500000000022612625666430021375 0ustar fabiofabio#!/usr/bin/python2 import os, sys os.execvp("/usr/share/cinnamon/cinnamon-desktop-editor/cinnamon-desktop-editor.py", ("",) + tuple(sys.argv[1:])) Cinnamon-2.8.6/files/usr/bin/cinnamon-settings0000775000175000017500000000170512625666430020303 0ustar fabiofabio#!/usr/bin/python2 import os, sys if len(sys.argv) > 1: module = sys.argv[1] if os.path.exists("/usr/share/cinnamon/cinnamon-settings/modules/cs_%s.py" % module): print "Python module" os.execvp("/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py", ("",) + tuple(sys.argv[1:])) elif os.path.exists("/usr/bin/cinnamon-control-center"): print "Unknown module %s, using cinnamon-control-center" % module os.execvp("/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py", ("",) + tuple(sys.argv[1:])) elif os.path.exists("/usr/bin/gnome-control-center"): print "Unknown module %s, calling gnome-control-center" % module os.execvp("gnome-control-center", ("",) + tuple(sys.argv[1:])) else: print "Unkown module %s" % module os.execvp("/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py", ("",) + tuple(sys.argv[1:])) else: os.execvp("/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py", ("",) + tuple(sys.argv[1:])) Cinnamon-2.8.6/files/usr/bin/cinnamon-screensaver-lock-dialog0000775000175000017500000000020512625666430023140 0ustar fabiofabio#!/usr/bin/python2 import os os.system("/usr/share/cinnamon/cinnamon-screensaver-lock-dialog/cinnamon-screensaver-lock-dialog.py") Cinnamon-2.8.6/files/usr/bin/cinnamon-session-cinnamon0000775000175000017500000000007212625666430021722 0ustar fabiofabio#! /bin/sh exec cinnamon-session --session cinnamon "$@" Cinnamon-2.8.6/files/usr/bin/cinnamon-file-dialog0000664000175000017500000000362712625666430020621 0ustar fabiofabio#!/usr/bin/env python3 import sys from gi.repository import Gio, Gtk cancelButton = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) typeArg = int(sys.argv[1]) fdType = typeArg % 3 if fdType == 0: msg = "Open" action = Gtk.FileChooserAction.OPEN okButton = (Gtk.STOCK_OPEN, Gtk.ResponseType.OK) elif fdType == 1: msg = "Select Folder" action = Gtk.FileChooserAction.SELECT_FOLDER okButton = (Gtk.STOCK_OPEN, Gtk.ResponseType.OK) elif fdType == 2: msg = "Save" action = Gtk.FileChooserAction.SAVE okButton = (Gtk.STOCK_SAVE, Gtk.ResponseType.OK) buttons = cancelButton + okButton filechooserdialog = Gtk.FileChooserDialog(title=msg, action=action, buttons=buttons) for i in range(2, len(sys.argv)): if sys.argv[i] == "-p": filechooserdialog.set_filename(sys.argv[i+1]) elif sys.argv[i] == "-n": filechooserdialog.set_current_name(sys.argv[i+1]) elif sys.argv[i] == "-d": filechooserdialog.set_current_folder(sys.argv[i+1]) elif sys.argv[i] == "-f": filterList = sys.argv[i+1].split(",") for filterInfo in filterList: [name,filters] = filterInfo.split(";") filterObj = Gtk.FileFilter() filterObj.set_name(name) rules = filters.split(":") for rule in rules: [ftype,value] = rule.split("=") if ftype == "m": filterObj.add_mime_type(value) elif ftype == "p": filterObj.add_pattern(value) filechooserdialog.add_filter(filterObj) if fdType == 2: filechooserdialog.set_do_overwrite_confirmation(True) if typeArg > 2: filechooserdialog.set_select_multiple(True) response=filechooserdialog.run() if response == Gtk.ResponseType.OK: if typeArg > 2: print (filechooserdialog.get_filenames()) else: print (filechooserdialog.get_filename()) filechooserdialog.destroy() Cinnamon-2.8.6/files/usr/share/0000775000175000017500000000000012625666430015244 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/applications/0000775000175000017500000000000012625666430017732 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-applets.desktop0000664000175000017500000002037312625666430026440 0ustar fabiofabio[Desktop Entry] Icon=cs-applets Exec=cinnamon-settings applets Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Applets Name[af]=Toepassingtjies Name[am]=ፕሮግራሞች Name[ar]=البريمجات Name[be]=Аплеты Name[be@latin]=Аплеты Name[bg]=Аплети Name[bg_BG]=Аплети Name[bn]=অ্যাপলেট Name[bn_BD]=অ্যাপলেট Name[bn_IN]=অ্যাপলেট Name[bs]=Apleti Name[cs]=Aplety Name[cs_CZ]=Aplety Name[csb]=Apletë Name[cy]=Rhaglenigau Name[da]=Panelprogrammer Name[da_DK]=Panelprogrammer Name[el]=Μικροεφαρμογές Name[eo]=Aplikaĵetoj Name[et]=Apletid Name[et_EE]=Apletid Name[eu]=Applet-ak Name[eu_ES]=Applet-ak Name[fa]=ابزارک‌ها Name[fa_AF]=ابزارک‌ها Name[fa_IR]=ابزارک‌ها Name[fi]=Paneelisovelmat Name[fi_FI]=Paneelisovelmat Name[ga]=Feidhmchláiríní Name[gd]=Aplaidean Name[gl]=Miniaplicativos Name[gl_ES]=Miniaplicativos Name[he]=יישומונים Name[he_IL]=יישומונים Name[hi]=एप्लेट्स Name[hr]=Apleti Name[hu]=Kisalkalmazások Name[hu_HU]=Kisalkalmazások Name[id]=Applet Name[id_ID]=Applet Name[is]=Smáforrit Name[it]=Applet Name[ja]=アプレット Name[ja_JP]=アプレット Name[km]=អាប់​ភ្លេត Name[km_KH]=អាប់​ភ្លេត Name[ko]=애플릿 Name[ko_KR]=애플릿 Name[lo]=ແອັບເພັດ Name[lt]=Įskiepiai Name[lt_LT]=Įskiepiai Name[lv]=Sīklietotnes Name[lv_LV]=Sīklietotnes Name[ml]=ആപ്‌ലെറ്റുകൾ Name[ml_IN]=ആപ്‌ലെറ്റുകൾ Name[ms]=Aplet Name[nb]=Miniprogrammer Name[nb_NO]=Miniprogrammer Name[ne]=एप्प्लेट्स Name[nl]=Hulptoepassingen Name[nl_NL]=Hulptoepassingen Name[nn]=Småprogram Name[nn_NO]=Småprogram Name[om]=Apleetota Name[pa]=ਐਪਲਿਟ Name[pl]=Aplety Name[pl_PL]=Aplety Name[pt_BR]=Miniaplicativos Name[ro]=Appleturi Name[ro_RO]=Appleturi Name[ru]=Апплеты Name[rue]=Апплеты Name[sk]=Applety Name[sk_SK]=Applety Name[sl]=Apleti Name[sl_SI]=Apleti Name[so]=Kaabayaasha Name[sq]=Programthe Name[sr]=Програмчићи Name[sr@Latn]=Apleti Name[sr@ije]=Програмчићи Name[sr@ijekavianlatin]=Програмчићи Name[sr@latin]=Apleti Name[sr_RS]=Програмчићи Name[sv]=Panelprogram Name[sv_SE]=Panelprogram Name[ta]=குறுநிரல்கள் Name[ta_LK]=குறுநிரல்கள் Name[te]=ఆప్లెట్లు Name[tg]=Барномаҳо Name[th]=แอปเพล็ต Name[th_TH]=แอปเพล็ต Name[tr]=Uygulamacıklar Name[ts]=Tiphurogireme-ntsongo Name[uk]=Аплети Name[uk_UA]=Аплети Name[ur]=ایپلیٹ Name[ur_PK]=ایپلیٹ Name[uz]=Апплетлар Name[uz@Latn]=Апплетлар Name[uz@cyrillic]=Апплетлар Name[vi]=Tiểu trình Name[vi_VN]=Tiểu trình Name[zh_CN]=小程序 Name[zh_HK]=應用小程式 Name[zh_TW]=面板小程式 Comment=Manage Cinnamon applets Comment[am]=የ ሲናሞን አፕሌቶች አስተዳዳሪ Comment[ar]=إدارة بريمجات سينامُن Comment[ast]=Alministra los applets de Cinnamon Comment[be]=Кіраванне аплетамi Cinnamon Comment[be@latin]=Кіраванне аплетамi Cinnamon Comment[bg]=Управление на аплетите за Cinnamon Comment[bg_BG]=Управление на аплетите за Cinnamon Comment[bs]=Upravljaj Cinnamon apletima Comment[ca]=Administrar applets de Cinnamon Comment[ca@valencia]=Administra applets de Cinnamon Comment[ca_ES@valencia]=Administra applets de Cinnamon Comment[cs]=Správa Cinnamon appletů Comment[cs_CZ]=Správa Cinnamon appletů Comment[cy]=Rheolwch raglenigau Cinnamon Comment[da]=Administrér Cinnamon-panelprogrammer Comment[da_DK]=Administrér Cinnamon-panelprogrammer Comment[de]=Cinnamon-Applets verwalten Comment[de_DE]=Cinnamon-Applets verwalten Comment[el]=Διαχείριση μικροεφαρμογών Cinnamon Comment[es]=Administrar applets de Cinnamon Comment[es_AR]=Administrar applets de Cinnamon Comment[es_CL]=Administrar applets de Cinnamon Comment[es_CO]=Administrar applets de Cinnamon Comment[es_CR]=Administrar applets de Cinnamon Comment[es_DO]=Administrar applets de Cinnamon Comment[es_EC]=Administrar applets de Cinnamon Comment[es_ES]=Administrar applets de Cinnamon Comment[es_GT]=Administrar applets de Cinnamon Comment[es_HN]=Administrar applets de Cinnamon Comment[es_MX]=Administrar applets de Cinnamon Comment[es_NI]=Administrar applets de Cinnamon Comment[es_PA]=Administrar applets de Cinnamon Comment[es_PE]=Administrar applets de Cinnamon Comment[es_PR]=Administrar applets de Cinnamon Comment[es_SV]=Administrar applets de Cinnamon Comment[es_UY]=Administrar applets de Cinnamon Comment[es_VE]=Administrar applets de Cinnamon Comment[eu]=Kudeatu Cinnamon applet-ak Comment[eu_ES]=Kudeatu Cinnamon applet-ak Comment[fi]=Hallitse Cinnamon-sovelmia Comment[fi_FI]=Hallitse Cinnamon-sovelmia Comment[fr]=Gérer les applets de Cinnamon Comment[fr_CA]=Gérer les applets Cinnamon Comment[fr_FR]=Gérer les applets de Cinnamon Comment[fy]=Cinnamon applets beheare Comment[gd]=Stiùirich aplaidean Cinnamon Comment[gl]=Administración dos miniaplicativos de Cinnamon Comment[gl_ES]=Administración dos miniaplicativos de Cinnamon Comment[hr]=Upravljajte Cinnamon apletima Comment[hu]=Cinnamon kisalkalmazások kezelése Comment[hu_HU]=Cinnamon kisalkalmazások kezelése Comment[id]=Atur applets Cinnamon Comment[id_ID]=Atur applets Cinnamon Comment[is]=Sýsla með Cinnamon forritlinga Comment[it]=Gestione applet di Cinnamon Comment[ja]=Cinnamon アプレットの管理 Comment[ja_JP]=Cinnamon アプレットの管理 Comment[ko]=시나몬 애플릿 관리 Comment[ko_KR]=시나몬 애플릿 관리 Comment[lo]=ຈັດການແອັບເພັດ Cinnamon Comment[lt]=Tvarkyti Cinnamon įskiepius Comment[lt_LT]=Tvarkyti Cinnamon įskiepius Comment[lv]=Pārvaldīt Cinnamon sīkrīkus Comment[lv_LV]=Pārvaldīt Cinnamon sīkrīkus Comment[ms]=Mengurus applet Cinnamon Comment[nb]=Håndter miniprogrammer i Cinnamon Comment[nb_NO]=Håndter miniprogrammer i Cinnamon Comment[nds]=Cinnamon Minianwendungen bearbeiten Comment[nl]=Hulptoepassingen van Cinnamon beheren Comment[nl_NL]=Hulptoepassingen van Cinnamon beheren Comment[pl]=Zarządzaj apletami Cinnamona Comment[pl_PL]=Zarządzaj apletami Cinnamona Comment[pt]=Gerir applets do Cinnamon Comment[pt_BR]=Gerenciar miniaplicativos do Cinnamon Comment[pt_PT]=Gerir applets do Cinnamon Comment[ro]=Gestionează appleturile Cinnamon Comment[ro_RO]=Gestionează appleturile Cinnamon Comment[ru]=Управление апплетами Cinnamon Comment[sk]=Spravovať aplety Cinnamon Comment[sk_SK]=Spravovať aplety Cinnamon Comment[sl]=Upravljajte z apleti Cinnamona Comment[sl_SI]=Upravljajte z apleti Cinnamona Comment[sq]=Menaxho programthet e Cinnamon Comment[sr]=Управљање програмчићима Цимета Comment[sr@Latn]=Управљање програмчићима Цимета Comment[sr@ije]=Управљање програмчићима Цимета Comment[sr@ijekavianlatin]=Управљање програмчићима Цимета Comment[sr@latin]=Управљање програмчићима Цимета Comment[sr_RS]=Управљање програмчићима Цимета Comment[sv]=Hantera Cinnamons panelprogram Comment[sv_SE]=Hantera Cinnamons panelprogram Comment[tg]=Идоракунии зербарномаҳои Cinnamon Comment[th]=จัดการแอปเพล็ตต่าง ๆ ของ Cinnamon Comment[th_TH]=จัดการแอปเพล็ตต่าง ๆ ของ Cinnamon Comment[tr]=Cinnamon uygulamacıklarını yönet Comment[uk]=Керування аплетами Cinnamon Comment[uk_UA]=Керування аплетами Cinnamon Comment[ur]=سنامون کے ایپلیٹس کا انتظام Comment[ur_PK]=سنامون کے ایپلیٹس کا انتظام Comment[uz]=Синнамон апплетларини бошқариш Comment[uz@Latn]=Синнамон апплетларини бошқариш Comment[uz@cyrillic]=Синнамон апплетларини бошқариш Comment[vi]=Quản lý tiểu dụng Cinnamon Comment[vi_VN]=Quản lý tiểu dụng Cinnamon Comment[zh_CN]=管理Cinnamon小程序 Comment[zh_HK]=管理 Cinnamon 應用小程式 Comment[zh_TW]=管理 Cinnamon 面板小程式 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-workspaces.desktop0000664000175000017500000002234212625666430027147 0ustar fabiofabio[Desktop Entry] Icon=cs-workspaces Exec=cinnamon-settings workspaces Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Workspaces Name[am]=የስራ ቦታዎች Name[ar]=مساحات العمل Name[ast]=Estayes de trabayu Name[be]=Рабочыя месцы Name[be@latin]=Рабочыя месцы Name[bg]=Работни места Name[bg_BG]=Работни места Name[bs]=Radne površine Name[ca]=Espais de treball Name[ca@valencia]=Espais de treball Name[ca_ES@valencia]=Espais de treball Name[cs]=Pracovní plochy Name[cs_CZ]=Pracovní plochy Name[cy]=Mannau Gwaith Name[da]=Arbejdsområder Name[da_DK]=Arbejdsområder Name[de]=Arbeitsflächen Name[de_DE]=Arbeitsflächen Name[el]=Χώροι Εργασίας Name[es]=Áreas de trabajo Name[es_AR]=Áreas de trabajo Name[es_CL]=Áreas de trabajo Name[es_CO]=Áreas de trabajo Name[es_CR]=Áreas de trabajo Name[es_DO]=Áreas de trabajo Name[es_EC]=Áreas de trabajo Name[es_ES]=Áreas de trabajo Name[es_GT]=Áreas de trabajo Name[es_HN]=Áreas de trabajo Name[es_MX]=Áreas de trabajo Name[es_NI]=Áreas de trabajo Name[es_PA]=Áreas de trabajo Name[es_PE]=Áreas de trabajo Name[es_PR]=Áreas de trabajo Name[es_SV]=Áreas de trabajo Name[es_UY]=Áreas de trabajo Name[es_VE]=Áreas de trabajo Name[et]=Tööruumid Name[et_EE]=Tööruumid Name[eu]=Laneko areak Name[eu_ES]=Laneko areak Name[fi]=Työtilat Name[fi_FI]=Työtilat Name[fr]=Espaces de travail Name[fr_CA]=Espaces de travail Name[fr_FR]=Espaces de travail Name[ga]=Spásanna Oibre Name[gd]=Rumannan-obrach Name[gl]=Espazos de traballo Name[gl_ES]=Espazos de traballo Name[hr]=Radni prostori Name[hu]=Munkaterületek Name[hu_HU]=Munkaterületek Name[id]=Area kerja Name[id_ID]=Area kerja Name[is]=Vinnusvæði Name[it]=Aree di lavoro Name[ja]=ワークスペース Name[ja_JP]=ワークスペース Name[ko]=작업공간 Name[ko_KR]=작업공간 Name[lo]=ພື້ນທີ່ເຮັດວຽກ Name[lt]=Darbo sritys Name[lt_LT]=Darbo sritys Name[lv]=Darbvietas Name[lv_LV]=Darbvietas Name[ms]=Ruang Kerja Name[nb]=Arbeidsområder Name[nb_NO]=Arbeidsområder Name[nds]=Arbeitsflächen Name[nl]=Werkbladen Name[nl_NL]=Werkbladen Name[oc]=Espacis de trabalh Name[om]=Iddoowwan hojii Name[pa]=ਵਰਕਸਪੇਸਾਂ Name[pl]=Obszary robocze Name[pl_PL]=Obszary robocze Name[pt]=Áreas de Trabalho Name[pt_BR]=Espaços de trabalho Name[pt_PT]=Áreas de Trabalho Name[ro]=Spații de lucru Name[ro_RO]=Spații de lucru Name[ru]=Рабочие области Name[sk]=Pracovné plochy Name[sk_SK]=Pracovné plochy Name[sl]=Delovni prostor Name[sl_SI]=Delovni prostor Name[sq]=Hapësirat e punës Name[sr]=Радни простори Name[sr@Latn]=Радни простори Name[sr@ije]=Радни простори Name[sr@ijekavianlatin]=Радни простори Name[sr@latin]=Радни простори Name[sr_RS]=Радни простори Name[sv]=Arbetsytor Name[sv_SE]=Arbetsytor Name[ta]=பணியிடங்கள் Name[ta_LK]=பணியிடங்கள் Name[tg]=Фазоҳои корӣ Name[th]=พื้นที่ทำงาน Name[th_TH]=พื้นที่ทำงาน Name[tr]=Çalışma Alanları Name[uk]=Робочі простори Name[uk_UA]=Робочі простори Name[ur]=ورک سپیسز Name[ur_PK]=ورک سپیسز Name[vi]=Vùng làm việc Name[vi_VN]=Vùng làm việc Name[zh_CN]=工作区 Name[zh_HK]=工作區 Name[zh_TW]=工作區 Comment=Manage workspace preferences Comment[am]=የ ስራ ቦታ ምርጫዎች አስተዳዳሪ Comment[ar]=إدارة تفضيلات مساحات العمل Comment[be]=Кіраванне наладамі працоўнага стала Comment[be@latin]=Кіраванне наладамі працоўнага стала Comment[bg]=Управление на настройките за работни плотове Comment[bg_BG]=Управление на настройките за работни плотове Comment[bs]=Upravljaj postavkama radne površine Comment[ca]=Administra preferències d'espai de treball Comment[ca@valencia]=Administra preferències d'espai de treball Comment[ca_ES@valencia]=Administra preferències d'espai de treball Comment[cs]=Spravovat nastavení pracovních ploch Comment[cs_CZ]=Spravovat nastavení pracovních ploch Comment[cy]=Rheoli dewisiadau gweithfannau Comment[da]=Administrér arbejdsområdeindstillinger Comment[da_DK]=Administrér arbejdsområdeindstillinger Comment[de]=Arbeitsflächeneinstellungen verwalten Comment[de_DE]=Arbeitsflächeneinstellungen verwalten Comment[el]=Ρυθμίστε τους χώρους εργασίας Comment[es]=Administrar preferencias de espacio de trabajo Comment[es_AR]=Administrar preferencias de espacio de trabajo Comment[es_CL]=Administrar preferencias de espacio de trabajo Comment[es_CO]=Administrar preferencias de espacio de trabajo Comment[es_CR]=Administrar preferencias de espacio de trabajo Comment[es_DO]=Administrar preferencias de espacio de trabajo Comment[es_EC]=Administrar preferencias de espacio de trabajo Comment[es_ES]=Administrar preferencias de espacio de trabajo Comment[es_GT]=Administrar preferencias de espacio de trabajo Comment[es_HN]=Administrar preferencias de espacio de trabajo Comment[es_MX]=Administrar preferencias de espacio de trabajo Comment[es_NI]=Administrar preferencias de espacio de trabajo Comment[es_PA]=Administrar preferencias de espacio de trabajo Comment[es_PE]=Administrar preferencias de espacio de trabajo Comment[es_PR]=Administrar preferencias de espacio de trabajo Comment[es_SV]=Administrar preferencias de espacio de trabajo Comment[es_UY]=Administrar preferencias de espacio de trabajo Comment[es_VE]=Administrar preferencias de espacio de trabajo Comment[eu]=Kudeatu laneko areen hobespenak Comment[eu_ES]=Kudeatu laneko areen hobespenak Comment[fi]=Hallitse työtilojen asetuksia Comment[fi_FI]=Hallitse työtilojen asetuksia Comment[fr]=Gérer les préférences de l'espace de travail Comment[fr_CA]=Gérer les préférences de l'espace de travail Comment[fr_FR]=Gérer les préférences de l'espace de travail Comment[gd]=Stiùirich roghainnean nan rumannan-obrach Comment[gl]=Administra as preferencias do espazo de traballo Comment[gl_ES]=Administra as preferencias do espazo de traballo Comment[hr]=Upravljajte osobitostima radnog prostora Comment[hu]=Munkaterület beállítások kezelése Comment[hu_HU]=Munkaterület beállítások kezelése Comment[id]=Kelola preferensi ruang kerja Comment[id_ID]=Kelola preferensi ruang kerja Comment[is]=Sýsla með kjörstillingar vinnusvæða Comment[it]=Gestione preferenze area di lavoro Comment[ko]=작업공간 설정관리 Comment[ko_KR]=작업공간 설정관리 Comment[lt]=Tvarkyti darbo srities nuostatas Comment[lt_LT]=Tvarkyti darbo srities nuostatas Comment[lv]=Pārvaldīt darbvietas preferences Comment[lv_LV]=Pārvaldīt darbvietas preferences Comment[ms]=Mengurus pilihan ruang kerja Comment[nb]=Håndter innstillinger for arbeidsområdene Comment[nb_NO]=Håndter innstillinger for arbeidsområdene Comment[nds]=Arbeitsflächeneinstellungen verwalten Comment[nl]=Werkbladvoorkeuren beheren Comment[nl_NL]=Werkbladvoorkeuren beheren Comment[pl]=Zarządzaj preferencjami obszarów roboczych Comment[pl_PL]=Zarządzaj preferencjami obszarów roboczych Comment[pt]=Gerir preferência do espaço de trabalho Comment[pt_BR]=Gerenciar preferências de espaço de trabalho Comment[pt_PT]=Gerir preferência do espaço de trabalho Comment[ro]=Gestionează preferințele spațiului de lucru Comment[ro_RO]=Gestionează preferințele spațiului de lucru Comment[ru]=Настройки рабочего стола Comment[sk]=Spravovať predvoľby pracovnej plochy Comment[sk_SK]=Spravovať predvoľby pracovnej plochy Comment[sl]=Upravljanje z možnostmi delovnih površin Comment[sl_SI]=Upravljanje z možnostmi delovnih površin Comment[sq]=Menaxho parapëlqimet e hapësirave të punës Comment[sr]=Управљање поставкама радног простора Comment[sr@Latn]=Управљање поставкама радног простора Comment[sr@ije]=Управљање поставкама радног простора Comment[sr@ijekavianlatin]=Управљање поставкама радног простора Comment[sr@latin]=Управљање поставкама радног простора Comment[sr_RS]=Управљање поставкама радног простора Comment[sv]=Hantera inställningar för arbetsyta Comment[sv_SE]=Hantera inställningar för arbetsyta Comment[tg]=Идоракунии хусусиятҳои фазои корӣ Comment[th]=จัดการการตั้งค่าพื้นที่ทำงานขั้นสูง Comment[th_TH]=จัดการการตั้งค่าพื้นที่ทำงานขั้นสูง Comment[tr]=Çalışma alanı seçeneklerini yönet Comment[uk]=Керувати параметрами робочого простору Comment[uk_UA]=Керувати параметрами робочого простору Comment[ur]=ورک سپیس کی ترجیحات Comment[ur_PK]=ورک سپیس کی ترجیحات Comment[vi]=Quản lý tuỳ thích vùng làm việc Comment[vi_VN]=Quản lý tuỳ thích vùng làm việc Comment[zh_CN]=工作区设置 Comment[zh_HK]=管理工作區偏好設定 Comment[zh_TW]=管理工作區偏好設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-backgrounds.desktop0000664000175000017500000002032112625666430027263 0ustar fabiofabio[Desktop Entry] Icon=cs-backgrounds Exec=cinnamon-settings backgrounds Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Backgrounds Name[am]=መደቦች Name[ar]=الخلفيات Name[ast]=Fondos de pantalla Name[be]=фон Name[be@latin]=фон Name[bg]=Фон Name[bg_BG]=Фон Name[bs]=Pozadine Name[ca]=Fons del escriptori Name[ca@valencia]=Fons de pantalla Name[ca_ES@valencia]=Fons de pantalla Name[cs]=Pozadí Name[cs_CZ]=Pozadí Name[cy]=Cefndiroedd Name[da]=Baggrunde Name[da_DK]=Baggrunde Name[de]=Hintergrundbilder Name[de_DE]=Hintergrundbilder Name[el]=Φόντο Name[eo]=Fonoj Name[es]=Fondos Name[es_AR]=Fondos Name[es_CL]=Fondos Name[es_CO]=Fondos Name[es_CR]=Fondos Name[es_DO]=Fondos Name[es_EC]=Fondos Name[es_ES]=Fondos Name[es_GT]=Fondos Name[es_HN]=Fondos Name[es_MX]=Fondos Name[es_NI]=Fondos Name[es_PA]=Fondos Name[es_PE]=Fondos Name[es_PR]=Fondos Name[es_SV]=Fondos Name[es_UY]=Fondos Name[es_VE]=Fondos Name[et]=Taustad Name[et_EE]=Taustad Name[eu]=Atzeko planoak Name[eu_ES]=Atzeko planoak Name[fi]=Taustat Name[fi_FI]=Taustat Name[fr]=Fonds d'écran Name[fr_CA]=Fonds d'écran Name[fr_FR]=Fonds d'écran Name[fy]=Eftergrûn Name[ga]=Cúlraí Name[gd]=Cùlaibhean Name[gl]=Fondos Name[gl_ES]=Fondos Name[he]=רקעים Name[he_IL]=רקעים Name[hr]=Pozadine Name[hu]=Háttérképek Name[hu_HU]=Háttérképek Name[id]=Latar belakang Name[id_ID]=Latar belakang Name[is]=Bakgrunnar Name[it]=Sfondi Name[ja]=背景 Name[ja_JP]=背景 Name[ko]=배경화면 Name[ko_KR]=배경화면 Name[lo]=ພື້ນຫຼັງ Name[lt]=Fonai Name[lt_LT]=Fonai Name[lv]=Fona attēli Name[lv_LV]=Fona attēli Name[ms]=Latar Belakang Name[nb]=Bakgrunner Name[nb_NO]=Bakgrunner Name[nds]=Hintergrundbilder Name[nl]=Achtergronden Name[nl_NL]=Achtergronden Name[oc]=Rèire plans Name[pa]=ਬੈਕਗਰਾਉਂਡ Name[pl]=Tła Name[pl_PL]=Tła Name[pt]=Fundos Name[pt_BR]=Planos de Fundo Name[pt_PT]=Fundos Name[ro]=Fundaluri Name[ro_RO]=Fundaluri Name[ru]=Фоновые рисунки Name[sk]=Pozadia Name[sk_SK]=Pozadia Name[sl]=Ozadja Name[sl_SI]=Ozadja Name[sq]=Sfondet Name[sr]=Позадине Name[sr@Latn]=Позадине Name[sr@ije]=Позадине Name[sr@ijekavianlatin]=Позадине Name[sr@latin]=Позадине Name[sr_RS]=Позадине Name[sv]=Bakgrundsbilder Name[sv_SE]=Bakgrundsbilder Name[ta]=பின்னணிகள் Name[ta_LK]=பின்னணிகள் Name[tg]=Пасзаминаҳо Name[th]=พื้นหลัง Name[th_TH]=พื้นหลัง Name[tr]=Arkaplanlar Name[uk]=Зображення тла Name[uk_UA]=Зображення тла Name[ur]=پسِ منظر Name[ur_PK]=پسِ منظر Name[uz]=Фонлар Name[uz@Latn]=Фонлар Name[uz@cyrillic]=Фонлар Name[vi]=Hình nền Name[vi_VN]=Hình nền Name[zh_CN]=背景 Name[zh_HK]=背景 Name[zh_TW]=背景 Comment=Change your desktop's background Comment[am]=የዴስክቶፕ መደብ መቀየሪያ Comment[ar]=غيّر خلفية سطح المكتب Comment[ast]=Camuda'l fondu del to escritoriu Comment[be]=Змянiце фон працоўнага стала Comment[be@latin]=Змянiце фон працоўнага стала Comment[bg]=Промяна на фона на работния плот Comment[bg_BG]=Промяна на фона на работния плот Comment[bs]=Promjenite vašu pozadinu na desktopu Comment[ca]=Canviar el fons de l'escriptori Comment[ca@valencia]=Canviar el fons d'escriptori Comment[ca_ES@valencia]=Canviar el fons d'escriptori Comment[cs]=Změna pozadí plochy Comment[cs_CZ]=Změna pozadí plochy Comment[cy]=Newidiwch gefndir eich bwrdd gwaith Comment[da]=Ændr din skrivebordsbaggrund Comment[da_DK]=Ændr din skrivebordsbaggrund Comment[de]=Schreibtischhintergrund ändern Comment[de_DE]=Schreibtischhintergrund ändern Comment[el]=Άλλαξε το φόντο της επιφάνειας εργασίας Comment[es]=Cambiar el fondo de escritorio Comment[es_AR]=Cambiar el fondo de escritorio Comment[es_CL]=Cambiar el fondo de escritorio Comment[es_CO]=Cambiar el fondo de escritorio Comment[es_CR]=Cambiar el fondo de escritorio Comment[es_DO]=Cambiar el fondo de escritorio Comment[es_EC]=Cambiar el fondo de escritorio Comment[es_ES]=Cambiar el fondo de escritorio Comment[es_GT]=Cambiar el fondo de escritorio Comment[es_HN]=Cambiar el fondo de escritorio Comment[es_MX]=Cambiar el fondo de escritorio Comment[es_NI]=Cambiar el fondo de escritorio Comment[es_PA]=Cambiar el fondo de escritorio Comment[es_PE]=Cambiar el fondo de escritorio Comment[es_PR]=Cambiar el fondo de escritorio Comment[es_SV]=Cambiar el fondo de escritorio Comment[es_UY]=Cambiar el fondo de escritorio Comment[es_VE]=Cambiar el fondo de escritorio Comment[eu]=Aldatu mahaigainaren atzeko planoa Comment[eu_ES]=Aldatu mahaigainaren atzeko planoa Comment[fi]=Muuta työpöydän taustakuvaa Comment[fi_FI]=Muuta työpöydän taustakuvaa Comment[fr]=Changer le fond d'écran Comment[fr_CA]=Modifier votre fond d'écran Comment[fr_FR]=Changer le fond d'écran Comment[fy]=Bureaublêd eftergrûn feroarje Comment[gd]=Atharraich cùlaibh an desktop agad Comment[gl]=Cambia o fondo do escritorio Comment[gl_ES]=Cambia o fondo do escritorio Comment[hr]=Promijenite pozadinu svoje redne površine Comment[hu]=Asztal hátterének megváltoztatása Comment[hu_HU]=Asztal hátterének megváltoztatása Comment[id]=Ubah background desktop Comment[id_ID]=Ubah background desktop Comment[is]=Breyta bakgrunni skjáborðs Comment[it]=Cambia sfondo del desktop Comment[ja]=デスクトップ背景の変更 Comment[ja_JP]=デスクトップ背景の変更 Comment[ko]=배경화면 변경 Comment[ko_KR]=배경화면 변경 Comment[lo]=ປ່ຽນພື້ນຫຼັງໜ້າຈໍຂອງເຈົ້າ Comment[lt]=Pakeisti darbalaukio foną Comment[lt_LT]=Pakeisti darbalaukio foną Comment[lv]=Mainīt darbvirsmas fona attēlu Comment[lv_LV]=Mainīt darbvirsmas fona attēlu Comment[ms]=Tukar latar belakang desktop Comment[nb]=Endre skrivebordsbakgrunnen din Comment[nb_NO]=Endre skrivebordsbakgrunnen din Comment[nds]=Desktophintergrund ändern Comment[nl]=Verander de achtergrond van uw bureaublad Comment[nl_NL]=Verander de achtergrond van uw bureaublad Comment[pl]=Zmień tło pulpitu Comment[pl_PL]=Zmień tło pulpitu Comment[pt]=Mudar o fundo do seu ambiente de trabalho Comment[pt_BR]=Alterar plano de fundo de sua área de trabalho Comment[pt_PT]=Mudar o fundo do seu ambiente de trabalho Comment[ro]=Schimbă imaginea de fundal Comment[ro_RO]=Schimbă imaginea de fundal Comment[ru]=Изменить обои рабочего стола Comment[sk]=Zmeniť pozadie plochy Comment[sk_SK]=Zmeniť pozadie plochy Comment[sl]=Spremenite ozadje namizja Comment[sl_SI]=Spremenite ozadje namizja Comment[sq]=Ndryshoni sfondin e desktopit tuaj Comment[sr]=Промени позадину Comment[sr@Latn]=Промени позадину Comment[sr@ije]=Промени позадину Comment[sr@ijekavianlatin]=Промени позадину Comment[sr@latin]=Промени позадину Comment[sr_RS]=Промени позадину Comment[sv]=Byt skrivbordsbakgrund Comment[sv_SE]=Byt skrivbordsbakgrund Comment[ta]=பணிமேடையின் பின்னணியை மாற்று Comment[ta_LK]=பணிமேடையின் பின்னணியை மாற்று Comment[tg]=Тағйир додани пасзаминаи мизи корӣ Comment[th]=เปลี่ยนพื้นหลังบนพื้นโต๊ะของคุณ Comment[th_TH]=เปลี่ยนพื้นหลังบนพื้นโต๊ะของคุณ Comment[tr]=Masaüstü arkaplan resmini değiştir Comment[uk]=Змінити тло стільниці Comment[uk_UA]=Змінити тло стільниці Comment[ur]=ڈیسک ٹاپ پس منظر تبدیل کریں Comment[ur_PK]=ڈیسک ٹاپ پس منظر تبدیل کریں Comment[uz]=Иш столи фонини ўзгартиринг Comment[uz@Latn]=Иш столи фонини ўзгартиринг Comment[uz@cyrillic]=Иш столи фонини ўзгартиринг Comment[vi]=Đổi nền màn hình làm việc Comment[vi_VN]=Đổi nền màn hình làm việc Comment[zh_CN]=修改桌面背景 Comment[zh_HK]=變更你桌面的背景 Comment[zh_TW]=變更您的桌面背景 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-keyboard.desktop0000664000175000017500000002567212625666430026577 0ustar fabiofabio[Desktop Entry] Icon=cs-keyboard Exec=cinnamon-settings keyboard Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Keyboard Name[af]=Sleutelbord Name[am]=የፊደል ገበታ Name[ar]=لوحة المفاتيح Name[ast]=Tecláu Name[az]=Klaviatura Name[az_IR]=Klaviatura Name[be]=Клавіятура Name[be@latin]=Клавіятура Name[bg]=Клавиатура Name[bg_BG]=Клавиатура Name[bn]=কীবোর্ড Name[bn_BD]=কীবোর্ড Name[bn_IN]=কীবোর্ড Name[bs]=Tastatura Name[ca]=Teclat Name[ca@valencia]=Teclat Name[ca_ES@valencia]=Teclat Name[cs]=Klávesnice Name[cs_CZ]=Klávesnice Name[csb]=Klawiatura Name[cy]=Bysellfwrdd Name[da]=Tastatur Name[da_DK]=Tastatur Name[de]=Tastatur Name[de_DE]=Tastatur Name[el]=Πληκτρολόγιο Name[eo]=Klavaro Name[es]=Teclado Name[es_AR]=Teclado Name[es_CL]=Teclado Name[es_CO]=Teclado Name[es_CR]=Teclado Name[es_DO]=Teclado Name[es_EC]=Teclado Name[es_ES]=Teclado Name[es_GT]=Teclado Name[es_HN]=Teclado Name[es_MX]=Teclado Name[es_NI]=Teclado Name[es_PA]=Teclado Name[es_PE]=Teclado Name[es_PR]=Teclado Name[es_SV]=Teclado Name[es_UY]=Teclado Name[es_VE]=Teclado Name[et]=Klaviatuur Name[et_EE]=Klaviatuur Name[eu]=Teklatua Name[eu_ES]=Teklatua Name[fa]=صفحه‌کلید Name[fa_AF]=صفحه‌کلید Name[fa_IR]=صفحه‌کلید Name[fi]=Näppäimistö Name[fi_FI]=Näppäimistö Name[fo]=Knappaborð Name[fr]=Clavier Name[fr_CA]=Clavier Name[fr_FR]=Clavier Name[fy]=Toetseboerd Name[ga]=Méarchlár Name[gd]=Meur-chlàr Name[gl]=Teclado Name[gl_ES]=Teclado Name[he]=מקלדת Name[he_IL]=מקלדת Name[hi]=कीबोर्ड Name[hr]=Tipkovnica Name[hu]=Billentyűzet Name[hu_HU]=Billentyűzet Name[hy]=Ստեղնաշար Name[id]=Papan ketik Name[id_ID]=Papan ketik Name[is]=Lyklaborð Name[it]=Tastiera Name[ja]=キーボード Name[ja_JP]=キーボード Name[jv]=Kibot Name[ka]=კლავიატურა Name[km]=ក្ដារចុច Name[km_KH]=ក្ដារចុច Name[kn]=ಕೀಲಿಮಣೆ Name[ko]=키보드 Name[ko_KR]=키보드 Name[la]=Claviatura Name[li]=Toetsebord Name[lo]=ແປ້ນພີມ Name[lt]=Klaviatūra Name[lt_LT]=Klaviatūra Name[lv]=Tastatūra Name[lv_LV]=Tastatūra Name[ml]=കീബോര്‍ഡ് Name[ml_IN]=കീബോര്‍ഡ് Name[mr]=कळफलक Name[ms]=Papan Kekunci Name[nb]=Tastatur Name[nb_NO]=Tastatur Name[nds]=Tastatur Name[ne]=कीबोर्ड Name[nl]=Toetsenbord Name[nl_NL]=Toetsenbord Name[nn]=Tastatur Name[nn_NO]=Tastatur Name[oc]=Clavièr Name[om]=Gabatee furtuu Name[pa]=ਕੀਬੋਰਡ Name[pl]=Klawiatura Name[pl_PL]=Klawiatura Name[pt]=Teclado Name[pt_BR]=Teclado Name[pt_PT]=Teclado Name[ro]=Tastatură Name[ro_RO]=Tastatură Name[ru]=Клавиатура Name[rue]=Клавїатура Name[si]=යතුරුපුවරුව Name[sk]=Klávesnica Name[sk_SK]=Klávesnica Name[sl]=Tipkovnica Name[sl_SI]=Tipkovnica Name[so]=Kiiboorka Name[sq]=Tastiera Name[sr]=Тастатура Name[sr@Latn]=Tastatura Name[sr@ije]=Тастатура Name[sr@ijekavianlatin]=Тастатура Name[sr@latin]=Tastatura Name[sr_RS]=Тастатура Name[sv]=Tangentbord Name[sv_SE]=Tangentbord Name[ta]=விசைப்பலகை Name[ta_LK]=விசைப்பலகை Name[te]=కీబోర్డు Name[tg]=Клавиатура Name[th]=แป้นพิมพ์ Name[th_TH]=แป้นพิมพ์ Name[tl]=Tiklado Name[tl_PH]=Tiklado Name[tpi]=Kibot Name[tr]=Klavye Name[ts]=Khibodo Name[uk]=Клавіатура Name[uk_UA]=Клавіатура Name[ur]=کی بورڈ Name[ur_PK]=کی بورڈ Name[uz]=Клавиатура Name[uz@Latn]=Клавиатура Name[uz@cyrillic]=Клавиатура Name[vi]=Bàn phím Name[vi_VN]=Bàn phím Name[zh_CN]=键盘 Name[zh_HK]=鍵盤 Name[zh_TW]=鍵盤 Comment=Manage keyboard settings and shortcuts Comment[am]=የፊደል ገበታ ማሰናጃዎች እና የ አቋራጭ ቁልፍ አስተዳዳሪ Comment[ar]=إدارة إعدادات لوحة المفاتيح والاختصارات Comment[be]=Кіраванне наладамі клавіятуры і гарачымі клавішамі Comment[be@latin]=Кіраванне наладамі клавіятуры і гарачымі клавішамі Comment[bg]=Управление на клавишните настройки и преки пътища Comment[bg_BG]=Управление на клавишните настройки и преки пътища Comment[bs]=Upravljaj postavkama tastature i prečica Comment[ca]=Administrar configuració de teclat i dreceres Comment[ca@valencia]=Administrar configuració i atalls de teclat Comment[ca_ES@valencia]=Administrar configuració i atalls de teclat Comment[cs]=Nastavení klásenice a klávesových zkratek Comment[cs_CZ]=Nastavení klásenice a klávesových zkratek Comment[cy]=Rheoli gosodiadau a llwybrau byr bysellfwrdd Comment[da]=Administrér tastaturindstillinger og genveje Comment[da_DK]=Administrér tastaturindstillinger og genveje Comment[de]=Tastatureinstellungen und Tastenkombination verwalten Comment[de_DE]=Tastatureinstellungen und Tastenkombination verwalten Comment[el]=Διαχείριση ρυθμίσεων πληκτρολογίου και πλήκτρων συντόμευσης Comment[es]=Administrar configuración de teclado y accesos directos Comment[es_AR]=Administrar configuración de teclado y accesos directos Comment[es_CL]=Administrar configuración de teclado y accesos directos Comment[es_CO]=Administrar configuración de teclado y accesos directos Comment[es_CR]=Administrar configuración de teclado y accesos directos Comment[es_DO]=Administrar configuración de teclado y accesos directos Comment[es_EC]=Administrar configuración de teclado y accesos directos Comment[es_ES]=Administrar configuración de teclado y accesos directos Comment[es_GT]=Administrar configuración de teclado y accesos directos Comment[es_HN]=Administrar configuración de teclado y accesos directos Comment[es_MX]=Administrar configuración de teclado y accesos directos Comment[es_NI]=Administrar configuración de teclado y accesos directos Comment[es_PA]=Administrar configuración de teclado y accesos directos Comment[es_PE]=Administrar configuración de teclado y accesos directos Comment[es_PR]=Administrar configuración de teclado y accesos directos Comment[es_SV]=Administrar configuración de teclado y accesos directos Comment[es_UY]=Administrar configuración de teclado y accesos directos Comment[es_VE]=Administrar configuración de teclado y accesos directos Comment[eu]=Kudeatu teklatuaren ezarpen eta laster-teklak Comment[eu_ES]=Kudeatu teklatuaren ezarpen eta laster-teklak Comment[fi]=Muuta näppäimistön asetuksia ja pikanäppäimiä Comment[fi_FI]=Muuta näppäimistön asetuksia ja pikanäppäimiä Comment[fr]=Gérer les paramètres du clavier et les raccourcis Comment[fr_CA]=Gérer les paramètres et les raccourcis du clavier Comment[fr_FR]=Gérer les paramètres du clavier et les raccourcis Comment[gd]=Stiùirich roghainnean is ath-ghoiridean a' mheur-chlàir Comment[gl]=Administra os atallos e axustes do teclado Comment[gl_ES]=Administra os atallos e axustes do teclado Comment[hr]=Upravljajte postavkama i prečacima tipkovnice Comment[hu]=Billentyűzet beállítások és billentyűparancsok kezelése Comment[hu_HU]=Billentyűzet beállítások és billentyűparancsok kezelése Comment[id]=Atur pengaturan keyboard dan shortcuts Comment[id_ID]=Atur pengaturan keyboard dan shortcuts Comment[is]=Sýsla með stillingar lyklaborðs og flýtilykla Comment[it]=Gestisci impostazioni e scorciatoie da tastiera Comment[ja]=キーボード設定・ショートカットの管理 Comment[ja_JP]=キーボード設定・ショートカットの管理 Comment[ko]=키보드 설정과 바로가기 관리 Comment[ko_KR]=키보드 설정과 바로가기 관리 Comment[lt]=Tvarkyti klaviatūros nustatymus ir klavišų derinius Comment[lt_LT]=Tvarkyti klaviatūros nustatymus ir klavišų derinius Comment[lv]=Pārvaldīt tastatūras iestatījumus un saīsnes Comment[lv_LV]=Pārvaldīt tastatūras iestatījumus un saīsnes Comment[ms]=Mengurus tetapan papan kekunci dan pintasan Comment[nb]=Håndter tastaturinnstillinger og hurtigtaster Comment[nb_NO]=Håndter tastaturinnstillinger og hurtigtaster Comment[nds]=Tastatureinstellungen und Verknüpfungen verwalten Comment[nl]=Toetsenbordinstellingen en sneltoetsen beheren Comment[nl_NL]=Toetsenbordinstellingen en sneltoetsen beheren Comment[pl]=Zarządzaj ustawieniami klawiatury i skrótami Comment[pl_PL]=Zarządzaj ustawieniami klawiatury i skrótami Comment[pt]=Geir as definições de teclado e atalhos Comment[pt_BR]=Gerenciar as configurações de teclado e atalhos Comment[pt_PT]=Geir as definições de teclado e atalhos Comment[ro]=Gestionează configurările tastaturii și scurtăturilor Comment[ro_RO]=Gestionează configurările tastaturii și scurtăturilor Comment[ru]=Управление настройками клавиатуры и горячими клавишами Comment[sk]=Spravovať nastavenia klávesnice a klávesových skratiek Comment[sk_SK]=Spravovať nastavenia klávesnice a klávesových skratiek Comment[sl]=Upravljajte z nastavitvami in bližnjicami tipkovnice Comment[sl_SI]=Upravljajte z nastavitvami in bližnjicami tipkovnice Comment[sr]=Управљање поставкама тастатуре и пречицама Comment[sr@Latn]=Управљање поставкама тастатуре и пречицама Comment[sr@ije]=Управљање поставкама тастатуре и пречицама Comment[sr@ijekavianlatin]=Управљање поставкама тастатуре и пречицама Comment[sr@latin]=Управљање поставкама тастатуре и пречицама Comment[sr_RS]=Управљање поставкама тастатуре и пречицама Comment[sv]=Hantera tangentbordsinställningar och genvägar Comment[sv_SE]=Hantera tangentbordsinställningar och genvägar Comment[tg]=Идоракунии миёнбурҳо ва танзимоти клавиатура Comment[th]=จัดการการตั้งค่าแป้นพิมพ์และทางลัด Comment[th_TH]=จัดการการตั้งค่าแป้นพิมพ์และทางลัด Comment[tr]=Klavye ayarlarını ve kısayollarını yönet Comment[uk]=Керування параметрами роботи клавіатури та клавіатурними скороченнями Comment[uk_UA]=Керування параметрами роботи клавіатури та клавіатурними скороченнями Comment[ur]=کیبورڈ کی ترتیبات اور شارٹ کٹ Comment[ur_PK]=کیبورڈ کی ترتیبات اور شارٹ کٹ Comment[vi]=Quản lý thiết đặt bàn phím và phím tắt Comment[vi_VN]=Quản lý thiết đặt bàn phím và phím tắt Comment[zh_CN]=键盘和快捷键设置 Comment[zh_HK]=管理鍵盤設定與快速鍵 Comment[zh_TW]=管理鍵盤設定與快捷鍵組合 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings.desktop0000664000175000017500000002535112625666430024773 0ustar fabiofabio[Desktop Entry] Exec=cinnamon-settings Icon=preferences-system Terminal=false Type=Application Categories=Settings; StartupNotify=false OnlyShowIn=X-Cinnamon; Keywords=Preferences;Settings; Name=System Settings Name[af]=Stelsel instellings Name[am]=ስርአት ማሰናጃ Name[ar]=إعدادات النظام Name[ast]=Axustes del sistema Name[az]=Sistem Tənzimləmələri Name[az_IR]=Sistem Tənzimləmələri Name[be]=Сістэмныя налады Name[be@latin]=Сістэмныя налады Name[bg]=Системни настройки Name[bg_BG]=Системни настройки Name[bn]=সিস্টেম পরিচালনা Name[bn_BD]=সিস্টেম পরিচালনা Name[bn_IN]=সিস্টেম পরিচালনা Name[bs]=Sistemske postavke Name[ca]=Configuració del sistema Name[ca@valencia]=Configuració del sistema Name[ca_ES@valencia]=Configuració del sistema Name[cs]=Nastavení systému Name[cs_CZ]=Nastavení systému Name[cy]=Gosodiadau System Name[da]=Systemindstillinger Name[da_DK]=Systemindstillinger Name[de]=Systemeinstellungen Name[de_DE]=Systemeinstellungen Name[el]=Ρυθμίσεις Συστήματος Name[eo]=Sistemaj agordoj Name[es]=Configuración del sistema Name[es_AR]=Configuración del sistema Name[es_CL]=Configuración del sistema Name[es_CO]=Configuración del sistema Name[es_CR]=Configuración del sistema Name[es_DO]=Configuración del sistema Name[es_EC]=Configuración del sistema Name[es_ES]=Configuración del sistema Name[es_GT]=Configuración del sistema Name[es_HN]=Configuración del sistema Name[es_MX]=Configuración del sistema Name[es_NI]=Configuración del sistema Name[es_PA]=Configuración del sistema Name[es_PE]=Configuración del sistema Name[es_PR]=Configuración del sistema Name[es_SV]=Configuración del sistema Name[es_UY]=Configuración del sistema Name[es_VE]=Configuración del sistema Name[et]=Süsteemi seaded Name[et_EE]=Süsteemi seaded Name[eu]=Sistemaren ezarpenak Name[eu_ES]=Sistemaren ezarpenak Name[fa]=تنظیمات سیستم Name[fa_AF]=تنظیمات سیستم Name[fa_IR]=تنظیمات سیستم Name[fi]=Järjestelmän asetukset Name[fi_FI]=Järjestelmän asetukset Name[fil]=Mga Setting ng System Name[fr]=Paramètres système Name[fr_CA]=Paramètres système Name[fr_FR]=Paramètres système Name[fy]=Systeem ynstellingen Name[ga]=Socruithe Córais Name[gd]=Roghainnean an t-siostaim Name[gl]=Axustes do sistema Name[gl_ES]=Axustes do sistema Name[he]=הגדרות מערכת Name[he_IL]=הגדרות מערכת Name[hi]=सिस्टम सेटिंग्स Name[hr]=Postavke sustava Name[hu]=Rendszerbeállítások Name[hu_HU]=Rendszerbeállítások Name[hy]=Համակարգի կարգավորումները Name[ia]=Preferentias de systema Name[id]=Pengaturan Sistem Name[id_ID]=Pengaturan Sistem Name[is]=Kerfisstillingar Name[it]=Impostazioni di sistema Name[ja]=システム設定 Name[ja_JP]=システム設定 Name[jv]=seting sistem Name[ka]=სისტემის პარამეტრები Name[km]=ការ​កំណត់​ប្រព័ន្ធ Name[km_KH]=ការ​កំណត់​ប្រព័ន្ធ Name[ko]=시스템 설정 Name[ko_KR]=시스템 설정 Name[ku]=Mîhengên pergalê Name[ku_IQ]=Mîhengên pergalê Name[la]=computator commuta Name[li]=Systeeminsjtellinge Name[lo]=ການຕັ້ງຄ່າລະບົບ Name[lt]=Sistemos nustatymai Name[lt_LT]=Sistemos nustatymai Name[lv]=Sistēmas iestatījumi Name[lv_LV]=Sistēmas iestatījumi Name[mr]=सिस्टम सेटिंग्स् Name[ms]=Tetapan Sistem Name[my]=စနစ် ကနောက်ခံ Name[nb]=Systeminnstillinger Name[nb_NO]=Systeminnstillinger Name[nds]=Systemeinstellungen Name[nl]=Systeeminstellingen Name[nl_NL]=Systeeminstellingen Name[oc]=Paramètres del sistèma Name[om]=Qindaainoota sirnaa Name[pa]=ਸਿਸਟਮ ਸੈਟਿੰਗ Name[pl]=Ustawienia systemowe Name[pl_PL]=Ustawienia systemowe Name[pt]=Definições do sistema Name[pt_BR]=Configurações do sistema Name[pt_PT]=Definições do sistema Name[ro]=Configurări sistem Name[ro_RO]=Configurări sistem Name[ru]=Параметры системы Name[si]=පද්ධති සැකසුම් Name[sk]=Systémové nastavenia Name[sk_SK]=Systémové nastavenia Name[sl]=Sistemske nastavitve Name[sl_SI]=Sistemske nastavitve Name[sq]=Parametrat e sistemit Name[sr]=Поставке система Name[sr@Latn]=Поставке система Name[sr@ije]=Поставке система Name[sr@ijekavianlatin]=Поставке система Name[sr@latin]=Поставке система Name[sr_RS]=Поставке система Name[sv]=Systeminställningar Name[sv_SE]=Systeminställningar Name[ta]=கணினி அமைப்புகள் Name[ta_LK]=கணினி அமைப்புகள் Name[tg]=Танзимоти система Name[th]=ค่าตั้งระบบ Name[th_TH]=ค่าตั้งระบบ Name[tl]=Mga Settings ukol sa Sistema Name[tl_PH]=Mga Settings ukol sa Sistema Name[tr]=Sistem Ayarları Name[ug]=سىستېما تەڭشىكى Name[uk]=Системні налаштування Name[uk_UA]=Системні налаштування Name[ur]=سسٹم کی ترتیبات Name[ur_PK]=سسٹم کی ترتیبات Name[uz]=Тизим созламалари Name[uz@Latn]=Тизим созламалари Name[uz@cyrillic]=Тизим созламалари Name[vi]=Thiết lập Hệ thống Name[vi_VN]=Thiết lập Hệ thống Name[zh_CN]=系统设置 Name[zh_HK]=系統設定 Name[zh_TW]=系統設定 Comment=Control Center Comment[af]=Kontrole sentrum Comment[am]=መቆጣጠሪያ ማእከል Comment[ar]=مركز التحكم Comment[ast]=Centru de control Comment[az]=İdarə Mərkəzi Comment[az_IR]=İdarə Mərkəzi Comment[be]=Цэнтр кіравання Comment[be@latin]=Цэнтр кіравання Comment[bg]=Контролен център Comment[bg_BG]=Контролен център Comment[bn]=নিয়ন্ত্রণ কেন্দ্র Comment[bn_BD]=নিয়ন্ত্রণ কেন্দ্র Comment[bn_IN]=নিয়ন্ত্রণ কেন্দ্র Comment[bs]=Upravljački centar Comment[ca]=Centre de Control Comment[ca@valencia]=Centre de control Comment[ca_ES@valencia]=Centre de control Comment[cs]=Ovládací centrum Comment[cs_CZ]=Ovládací centrum Comment[cy]=Canolfan Rheoli Comment[da]=Kontrolcenter Comment[da_DK]=Kontrolcenter Comment[de]=Steuerzentrale Comment[de_DE]=Steuerzentrale Comment[el]=Κέντρο Ελέγχου Comment[en_CA]=Control Centre Comment[en_GB]=Control Centre Comment[eo]=Stircentro Comment[es]=Centro de control Comment[es_AR]=Centro de control Comment[es_CL]=Centro de control Comment[es_CO]=Centro de control Comment[es_CR]=Centro de control Comment[es_DO]=Centro de control Comment[es_EC]=Centro de control Comment[es_ES]=Centro de control Comment[es_GT]=Centro de control Comment[es_HN]=Centro de control Comment[es_MX]=Centro de control Comment[es_NI]=Centro de control Comment[es_PA]=Centro de control Comment[es_PE]=Centro de control Comment[es_PR]=Centro de control Comment[es_SV]=Centro de control Comment[es_UY]=Centro de control Comment[es_VE]=Centro de control Comment[et]=Juhtimiskeskus Comment[et_EE]=Juhtimiskeskus Comment[eu]=Kontrolgunea Comment[eu_ES]=Kontrolgunea Comment[fa]=مرکز کنترل Comment[fa_AF]=مرکز کنترل Comment[fa_IR]=مرکز کنترل Comment[fi]=Ohjauskeskus Comment[fi_FI]=Ohjauskeskus Comment[fil]=Kontrol Senter Comment[fo]=Stýris-miðdepil Comment[fr]=Centre de Contrôle Comment[fr_CA]=Centre de Contrôle Comment[fr_FR]=Centre de Contrôle Comment[fy]=Behear Sintrum Comment[ga]=Lárionad Rialaithe Comment[gd]=An t-ionad smachd Comment[gl]=Centro de control Comment[gl_ES]=Centro de control Comment[he]=מרכז הבקרה Comment[he_IL]=מרכז הבקרה Comment[hi]=कन्ट्रोल सेन्टर Comment[hr]=Upravljačko središte Comment[hu]=Vezérlőpult Comment[hu_HU]=Vezérlőpult Comment[hy]=Կառավարման կենտրոն Comment[id]=Pusat Kendali Comment[id_ID]=Pusat Kendali Comment[is]=Stjórnborð Comment[it]=Centro di controllo Comment[ja]=コントロールセンター Comment[ja_JP]=コントロールセンター Comment[jv]=Pusate ngengatur Comment[ka]=მართვის ცენტრი Comment[km]=មជ្ឈមណ្ឌល​បញ្ជា Comment[km_KH]=មជ្ឈមណ្ឌល​បញ្ជា Comment[ko]=제어판 Comment[ko_KR]=제어판 Comment[ku]=Navenda Kontrolê Comment[ku_IQ]=Navenda Kontrolê Comment[li]=Configurasiesjerm Comment[lo]=ສູນຄວບຄຸມ Comment[lt]=Valdymo centras Comment[lt_LT]=Valdymo centras Comment[lv]=Vadības centrs Comment[lv_LV]=Vadības centrs Comment[mr]=नियंत्रण केंद्र Comment[ms]=Pusat Kawalan Comment[my]=ထိန်းချုပ်ရေး စင်တာ Comment[nb]=Kontrollsenter Comment[nb_NO]=Kontrollsenter Comment[nds]=Kontrollzentrum Comment[nl]=Configuratiecentrum Comment[nl_NL]=Configuratiecentrum Comment[oc]=Centre de contraròtle Comment[om]=Wiirtuu to'annaa Comment[pa]=ਕੰਟਰੋਲ ਸੈਂਟਰ Comment[pl]=Centrum sterowania Comment[pl_PL]=Centrum sterowania Comment[pt]=Centro de controlo Comment[pt_BR]=Centro de Controle Comment[pt_PT]=Centro de controlo Comment[ro]=Centru de control Comment[ro_RO]=Centru de control Comment[ru]=Центр управления Comment[si]=පාලන මධ්‍යස්ථානය Comment[sk]=Centrum ovládania Comment[sk_SK]=Centrum ovládania Comment[sl]=Nadzorno središče Comment[sl_SI]=Nadzorno središče Comment[sq]=Qendra e kontrollit Comment[sr]=Управљачки центар Comment[sr@Latn]=Управљачки центар Comment[sr@ije]=Управљачки центар Comment[sr@ijekavianlatin]=Управљачки центар Comment[sr@latin]=Управљачки центар Comment[sr_RS]=Управљачки центар Comment[sv]=Kontrollcenter Comment[sv_SE]=Kontrollcenter Comment[ta]=கட்டுப்பாட்டு மையம் Comment[ta_LK]=கட்டுப்பாட்டு மையம் Comment[tg]=Маркази идоракунӣ Comment[th]=ศูนย์ควบคุม Comment[th_TH]=ศูนย์ควบคุม Comment[tl]=Sentro ng Kontrol Comment[tl_PH]=Sentro ng Kontrol Comment[tr]=Cinnamon Denetim Merkezi Comment[ug]=باشقۇرۇش مەركىزى Comment[uk]=Центр керування Comment[uk_UA]=Центр керування Comment[ur]=کنڑول مرکز Comment[ur_PK]=کنڑول مرکز Comment[uz]=Назорат маркази Comment[uz@Latn]=Назорат маркази Comment[uz@cyrillic]=Назорат маркази Comment[vi]=Trung tâm Điều khiển Comment[vi_VN]=Trung tâm Điều khiển Comment[zh_CN]=控制中心 Comment[zh_HK]=控制中心 Comment[zh_TW]=控制中心 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-menu-editor.desktop0000664000175000017500000000042012625666430025351 0ustar fabiofabio[Desktop Entry] Name=Cinnamon Menu Editor Comment=Edit the entries of the Cinnamon menu applet Exec=cinnamon-menu-editor Icon=alacarte OnlyShowIn=X-Cinnamon; Terminal=false Type=Application NoDisplay=true Categories=GNOME;GTK;Settings;DesktopSettings; StartupNotify=false Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-general.desktop0000664000175000017500000001563412625666430026411 0ustar fabiofabio[Desktop Entry] Icon=cs-general Exec=cinnamon-settings general Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=General Name[am]=ባጠቃላይ Name[ar]=عام Name[ast]=Xeneral Name[be]=Агульны Name[be@latin]=Агульны Name[bg]=Общи Name[bg_BG]=Общи Name[bs]=Opšte Name[cs]=Obecné Name[cs_CZ]=Obecné Name[cy]=Cyffredinol Name[da]=Generelt Name[da_DK]=Generelt Name[de]=Allgemein Name[de_DE]=Allgemein Name[el]=Γενικές Ρυθμίσεις Name[eo]=Ĝenerala Name[et]=Üldine Name[et_EE]=Üldine Name[eu]=Orokorra Name[eu_ES]=Orokorra Name[fi]=Yleinen Name[fi_FI]=Yleinen Name[fr]=Général Name[fr_CA]=Général Name[fr_FR]=Général Name[ga]=Ginearálta Name[gd]=Coitcheann Name[gl]=Xeral Name[gl_ES]=Xeral Name[hr]=Općenito Name[hu]=Általános Name[hu_HU]=Általános Name[id]=Umum Name[id_ID]=Umum Name[is]=Almennt Name[it]=Generali Name[ja]=一般 Name[ja_JP]=一般 Name[ko]=일반 Name[ko_KR]=일반 Name[lo]=ທົ່ວໄປ Name[lt]=Bendra Name[lt_LT]=Bendra Name[lv]=Vispārīgi Name[lv_LV]=Vispārīgi Name[ms]=Am Name[nb]=Generelt Name[nb_NO]=Generelt Name[nds]=Allgemein Name[nl]=Algemeen Name[nl_NL]=Algemeen Name[om]=Waliigala Name[pa]=ਸਧਾਰਨ Name[pl]=Ogólne Name[pl_PL]=Ogólne Name[pt]=Geral Name[pt_BR]=Geral Name[pt_PT]=Geral Name[ru]=Общие Name[sk]=Všeobecné Name[sk_SK]=Všeobecné Name[sl]=Splošno Name[sl_SI]=Splošno Name[sq]=Të përgjithshme Name[sr]=Опште Name[sr@Latn]=Опште Name[sr@ije]=Опште Name[sr@ijekavianlatin]=Опште Name[sr@latin]=Опште Name[sr_RS]=Опште Name[sv]=Allmänt Name[sv_SE]=Allmänt Name[ta]=பொது Name[ta_LK]=பொது Name[tg]=Умумӣ Name[th]=ทั่วไป Name[th_TH]=ทั่วไป Name[tr]=Genel Name[uk]=Загальні Name[uk_UA]=Загальні Name[ur]=عمومی Name[ur_PK]=عمومی Name[vi]=Tổng quát Name[vi_VN]=Tổng quát Name[zh_CN]=常规 Name[zh_HK]=一般 Name[zh_TW]=一般 Comment=Miscellaneous Cinnamon preferences Comment[am]=የተለያዩ የ ሲናሞን ምርጫዎች Comment[ar]=تفضيلات أخرى لـ: سينامُن Comment[be]=Розныя налады Cinnamon Comment[be@latin]=Розныя налады Cinnamon Comment[bg]=Други настройки на Cinnamon Comment[bg_BG]=Други настройки на Cinnamon Comment[bs]=Razne Cinnamon postavke Comment[ca]=Preferències diverses de Cinnamon Comment[ca@valencia]=Preferències diverses de Cinnamon Comment[ca_ES@valencia]=Preferències diverses de Cinnamon Comment[cs]=Ostatní nastavení Cinnamonu Comment[cs_CZ]=Ostatní nastavení Cinnamonu Comment[cy]=Dewisiadau amrywiol Cinnamon Comment[da]=Diverse Cinnamon-indstillinger Comment[da_DK]=Diverse Cinnamon-indstillinger Comment[de]=Verschiedene Cinnamon-Einstellungen Comment[de_DE]=Verschiedene Cinnamon-Einstellungen Comment[el]=Διάφορες προτιμήσεις του Cinnamon Comment[es]=Preferencias diversas de Cinnamon Comment[es_AR]=Preferencias diversas de Cinnamon Comment[es_CL]=Preferencias diversas de Cinnamon Comment[es_CO]=Preferencias diversas de Cinnamon Comment[es_CR]=Preferencias diversas de Cinnamon Comment[es_DO]=Preferencias diversas de Cinnamon Comment[es_EC]=Preferencias diversas de Cinnamon Comment[es_ES]=Preferencias diversas de Cinnamon Comment[es_GT]=Preferencias diversas de Cinnamon Comment[es_HN]=Preferencias diversas de Cinnamon Comment[es_MX]=Preferencias diversas de Cinnamon Comment[es_NI]=Preferencias diversas de Cinnamon Comment[es_PA]=Preferencias diversas de Cinnamon Comment[es_PE]=Preferencias diversas de Cinnamon Comment[es_PR]=Preferencias diversas de Cinnamon Comment[es_SV]=Preferencias diversas de Cinnamon Comment[es_UY]=Preferencias diversas de Cinnamon Comment[es_VE]=Preferencias diversas de Cinnamon Comment[eu]=Askotariko Cinnamon hobespenak Comment[eu_ES]=Askotariko Cinnamon hobespenak Comment[fi]=Sekalaisia Cinnamonin asetuksia Comment[fi_FI]=Sekalaisia Cinnamonin asetuksia Comment[fr]=Préférences diverses de Cinnamon Comment[fr_CA]=Préférences diverses de Cinnamon Comment[fr_FR]=Préférences diverses de Cinnamon Comment[gd]=Measgachadh de roghainnean Cinnamon Comment[gl]=Preferencias variadas para Cinnamon Comment[gl_ES]=Preferencias variadas para Cinnamon Comment[hr]=Ostale Cinnamon osobitosti Comment[hu]=Egyéb Cinnamon beállítások Comment[hu_HU]=Egyéb Cinnamon beállítások Comment[id]=Macam-macam preferensi Cinnamon Comment[id_ID]=Macam-macam preferensi Cinnamon Comment[is]=Ýmsar stillingar fyrir Cinnamon Comment[it]=Preferenze varie di Cinnamon Comment[ja]=その他 Cinnamon 設定オプション Comment[ja_JP]=その他 Cinnamon 設定オプション Comment[ko]=그밖의 시나몬설정 Comment[ko_KR]=그밖의 시나몬설정 Comment[lt]=Kitos Cinnamon nuostatos Comment[lt_LT]=Kitos Cinnamon nuostatos Comment[lv]=Dažādi Cinnamon iestatījumi Comment[lv_LV]=Dažādi Cinnamon iestatījumi Comment[ms]=Pilihan Cinnamon pelbagai Comment[nb]=Diverse innstillinger for Cinnamon Comment[nb_NO]=Diverse innstillinger for Cinnamon Comment[nds]=Sonstige Cinnamon-Einstellungen Comment[nl]=Verschillende Cinnamonvoorkeuren Comment[nl_NL]=Verschillende Cinnamonvoorkeuren Comment[pl]=Różne preferencje Cinnamona Comment[pl_PL]=Różne preferencje Cinnamona Comment[pt]=Preferências diversas do Cinnamon Comment[pt_BR]=Preferências diversas do Cinnamon Comment[pt_PT]=Preferências diversas do Cinnamon Comment[ro]=Alte preferințe Cinnamon Comment[ro_RO]=Alte preferințe Cinnamon Comment[ru]=Различные настройки Cinnamon Comment[sk]=Rôzne predvoľby Cinnamon Comment[sk_SK]=Rôzne predvoľby Cinnamon Comment[sl]=Razne možnosti Cinnamona Comment[sl_SI]=Razne možnosti Cinnamona Comment[sq]=Parapëlqime të përziera të Cinnamon Comment[sr]=Несврстане поставке Цимета Comment[sr@Latn]=Несврстане поставке Цимета Comment[sr@ije]=Несврстане поставке Цимета Comment[sr@ijekavianlatin]=Несврстане поставке Цимета Comment[sr@latin]=Несврстане поставке Цимета Comment[sr_RS]=Несврстане поставке Цимета Comment[sv]=Diverse inställningar för Cinnamon Comment[sv_SE]=Diverse inställningar för Cinnamon Comment[tg]=Хусусиятҳои гуногун дар Cinnamon Comment[th]=การตั้งค่าเบ็ดเตล็ด Cinnamon ขั้นสูง Comment[th_TH]=การตั้งค่าเบ็ดเตล็ด Cinnamon ขั้นสูง Comment[tr]=Muhtelif Cinnamon seçenekleri Comment[uk]=Різні параметри Cinnamon Comment[uk_UA]=Різні параметри Cinnamon Comment[ur]=متفرق، سنامون کی ترجیحات Comment[ur_PK]=متفرق، سنامون کی ترجیحات Comment[vi]=Tuỳ thích Cinnamon linh tinh Comment[vi_VN]=Tuỳ thích Cinnamon linh tinh Comment[zh_CN]=其他Cinnamon偏好设置 Comment[zh_HK]=Cinnamon 雜項偏好設定 Comment[zh_TW]=其他 Cinnamon 偏好設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-info.desktop0000664000175000017500000002167612625666430025732 0ustar fabiofabio[Desktop Entry] Icon=cs-details Exec=cinnamon-settings info Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=System Info Name[am]=የስርአት መረጃ Name[ar]=معلومات النظام Name[ast]=Información del sistema Name[be]=Сістэмная інфармацыя Name[be@latin]=Сістэмная інфармацыя Name[bg]=Информация за системата Name[bg_BG]=Информация за системата Name[bs]=Informacije o sistemu Name[ca]=Informació del sistema Name[ca@valencia]=Informació del sistema Name[ca_ES@valencia]=Informació del sistema Name[cs]=Systémové informace Name[cs_CZ]=Systémové informace Name[cy]=Gwybodaeth System Name[da]=Systemoplysninger Name[da_DK]=Systemoplysninger Name[de]=Systeminformation Name[de_DE]=Systeminformation Name[el]=Πληροφορίες συστήματος Name[eo]=Sisteminformoj Name[es]=Información del sistema Name[es_AR]=Información del sistema Name[es_CL]=Información del sistema Name[es_CO]=Información del sistema Name[es_CR]=Información del sistema Name[es_DO]=Información del sistema Name[es_EC]=Información del sistema Name[es_ES]=Información del sistema Name[es_GT]=Información del sistema Name[es_HN]=Información del sistema Name[es_MX]=Información del sistema Name[es_NI]=Información del sistema Name[es_PA]=Información del sistema Name[es_PE]=Información del sistema Name[es_PR]=Información del sistema Name[es_SV]=Información del sistema Name[es_UY]=Información del sistema Name[es_VE]=Información del sistema Name[et]=Süsteemi info Name[et_EE]=Süsteemi info Name[eu]=Sistemaren informazioa Name[eu_ES]=Sistemaren informazioa Name[fi]=Järjestelmätiedot Name[fi_FI]=Järjestelmätiedot Name[fr]=Information système Name[fr_CA]=Information système Name[fr_FR]=Information système Name[ga]=Faisnéis Córais Name[gd]=Fiosrachadh mun t-siostam Name[gl]=Información do sistema Name[gl_ES]=Información do sistema Name[hr]=Informacije sustava Name[hu]=Rendszerinformációk Name[hu_HU]=Rendszerinformációk Name[id]=Informasi Sistem Name[id_ID]=Informasi Sistem Name[is]=Kerfisupplýsingar Name[it]=Informazioni di sistema Name[ja]=システム情報 Name[ja_JP]=システム情報 Name[ko]=시스템정보 Name[ko_KR]=시스템정보 Name[lo]=ຂໍ້ມູນລະບົບ Name[lt]=Sistemos informacija Name[lt_LT]=Sistemos informacija Name[lv]=Sistēmas informācija Name[lv_LV]=Sistēmas informācija Name[ms]=Maklumat Sistem Name[nb]=Systeminformasjon Name[nb_NO]=Systeminformasjon Name[nds]=Systeminformationen Name[nl]=Systeeminformatie Name[nl_NL]=Systeeminformatie Name[oc]=Informacions sul sistèma Name[om]=Odeeffanoo sirnaa Name[pa]=ਸਿਸਟਮ ਜਾਣਕਾਰੀ Name[pl]=Informacje o systemie Name[pl_PL]=Informacje o systemie Name[pt]=Informações do sistema Name[pt_BR]=Informações do sistema Name[pt_PT]=Informações do sistema Name[ro]=Informații sistem Name[ro_RO]=Informații sistem Name[ru]=О системе Name[sk]=Systémové informácie Name[sk_SK]=Systémové informácie Name[sl]=Sistemski podatki Name[sl_SI]=Sistemski podatki Name[sq]=Informacione mbi sistemin Name[sr]=Подаци о систему Name[sr@Latn]=Подаци о систему Name[sr@ije]=Подаци о систему Name[sr@ijekavianlatin]=Подаци о систему Name[sr@latin]=Подаци о систему Name[sr_RS]=Подаци о систему Name[sv]=Systeminformation Name[sv_SE]=Systeminformation Name[ta]=கணினி தகவல் Name[ta_LK]=கணினி தகவல் Name[tg]=Иттилооти система Name[th]=ข้อมูลระบบ Name[th_TH]=ข้อมูลระบบ Name[tr]=Sistem Bilgisi Name[uk]=Інформація про систему Name[uk_UA]=Інформація про систему Name[ur]=سسٹم معلومات Name[ur_PK]=سسٹم معلومات Name[vi]=Thông tin Hệ thống Name[vi_VN]=Thông tin Hệ thống Name[zh_CN]=系统信息 Name[zh_HK]=系統資訊 Name[zh_TW]=系統資訊 Comment=Display system information Comment[am]=የ ስርአቱን መረጃ ማሳያ Comment[ar]=إظهار معلومات النظام Comment[be]=Паказаць сістэмная інфармацыю Comment[be@latin]=Паказаць сістэмная інфармацыю Comment[bg]=Покажи системна информация Comment[bg_BG]=Покажи системна информация Comment[bs]=Prikaži informacije o sistemu Comment[ca]=Mostrar Informació del sistema Comment[ca@valencia]=Mostra la informació del sistema Comment[ca_ES@valencia]=Mostra la informació del sistema Comment[cs]=Zobrazit systémové informace Comment[cs_CZ]=Zobrazit systémové informace Comment[cy]=Dangos gwybodaeth system Comment[da]=Vis systeminformation Comment[da_DK]=Vis systeminformation Comment[de]=Systeminformationen anzeigen Comment[de_DE]=Systeminformationen anzeigen Comment[el]=Προβολή πληροφοριών για τον υπολογιστή Comment[es]=Mostrar información del sistema Comment[es_AR]=Mostrar información del sistema Comment[es_CL]=Mostrar información del sistema Comment[es_CO]=Mostrar información del sistema Comment[es_CR]=Mostrar información del sistema Comment[es_DO]=Mostrar información del sistema Comment[es_EC]=Mostrar información del sistema Comment[es_ES]=Mostrar información del sistema Comment[es_GT]=Mostrar información del sistema Comment[es_HN]=Mostrar información del sistema Comment[es_MX]=Mostrar información del sistema Comment[es_NI]=Mostrar información del sistema Comment[es_PA]=Mostrar información del sistema Comment[es_PE]=Mostrar información del sistema Comment[es_PR]=Mostrar información del sistema Comment[es_SV]=Mostrar información del sistema Comment[es_UY]=Mostrar información del sistema Comment[es_VE]=Mostrar información del sistema Comment[et]=Süsteemi teabe näitamine Comment[et_EE]=Süsteemi teabe näitamine Comment[eu]=Bistaratu sistemaren informazioa Comment[eu_ES]=Bistaratu sistemaren informazioa Comment[fi]=Näytä järjestelmätiedot Comment[fi_FI]=Näytä järjestelmätiedot Comment[fr]=Afficher les informations du système Comment[fr_CA]=Afficher les informations du système Comment[fr_FR]=Afficher les informations du système Comment[gd]=Seall fiosrachadh mun t-siostam Comment[gl]=Amosar a información do sistema Comment[gl_ES]=Amosar a información do sistema Comment[hr]=Prikaži informacije sustava Comment[hu]=Rendszerinformációk megjelenítése Comment[hu_HU]=Rendszerinformációk megjelenítése Comment[id]=Tampilkan informasi sistem Comment[id_ID]=Tampilkan informasi sistem Comment[is]=Birta upplýsingar um kerfið Comment[it]=Visualizza le informazioni di sistema Comment[ja]=システム情報を表示 Comment[ja_JP]=システム情報を表示 Comment[ko]=시스템정보 표시 Comment[ko_KR]=시스템정보 표시 Comment[lo]=ສະແດງຂໍ້ມູນກ່ຽວກັບລະບົບ Comment[lt]=Rodyti sistemos informaciją Comment[lt_LT]=Rodyti sistemos informaciją Comment[lv]=Attēlot sistēmas informāciju Comment[lv_LV]=Attēlot sistēmas informāciju Comment[ms]=Papar maklumat sistem Comment[nb]=Vis systeminformasjon Comment[nb_NO]=Vis systeminformasjon Comment[nds]=Systeminformationen anzeigen Comment[nl]=Systeeminformatie weergeven Comment[nl_NL]=Systeeminformatie weergeven Comment[pl]=Wyświetlaj informacje systemowe Comment[pl_PL]=Wyświetlaj informacje systemowe Comment[pt]=Mostrar a informação do sistema Comment[pt_BR]=Exibir informações do sistema Comment[pt_PT]=Mostrar a informação do sistema Comment[ro]=Arată informații despre sistem Comment[ro_RO]=Arată informații despre sistem Comment[ru]=Отображать информацию о системе Comment[sk]=Zobraziť systémové informácie Comment[sk_SK]=Zobraziť systémové informácie Comment[sl]=Prikaži podrobnosti sistema Comment[sl_SI]=Prikaži podrobnosti sistema Comment[sq]=Shfaq informacione mbi sistemin Comment[sr]=Прикажи податке о систему Comment[sr@Latn]=Прикажи податке о систему Comment[sr@ije]=Прикажи податке о систему Comment[sr@ijekavianlatin]=Прикажи податке о систему Comment[sr@latin]=Прикажи податке о систему Comment[sr_RS]=Прикажи податке о систему Comment[sv]=Visa systeminformation Comment[sv_SE]=Visa systeminformation Comment[tg]=Намоиш додани иттилооти система Comment[th]=แสดงข้อมูลระบบ Comment[th_TH]=แสดงข้อมูลระบบ Comment[tr]=Sistem bilgisini göster Comment[uk]=Показати системну інформацію Comment[uk_UA]=Показати системну інформацію Comment[ur]=سسٹم معلومات دکھائیں Comment[ur_PK]=سسٹم معلومات دکھائیں Comment[vi]=Hiển thị thông tin hệ thống Comment[vi_VN]=Hiển thị thông tin hệ thống Comment[zh_CN]=显示系统信息 Comment[zh_HK]=顯示系統資訊 Comment[zh_TW]=顯示系統資訊 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-windows.desktop0000664000175000017500000001715612625666430026467 0ustar fabiofabio[Desktop Entry] Icon=cs-windows Exec=cinnamon-settings windows Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Windows Name[am]=መስኮቶች Name[ar]=النوافذ Name[ast]=Ventanes Name[be]=Вокны Name[be@latin]=Вокны Name[bg]=Прозорци Name[bg_BG]=Прозорци Name[bs]=Prozori Name[ca]=Finestres Name[ca@valencia]=Finestres Name[ca_ES@valencia]=Finestres Name[cs]=Okna Name[cs_CZ]=Okna Name[cy]=Ffenestri Name[da]=Vinduer Name[da_DK]=Vinduer Name[de]=Fenster Name[de_DE]=Fenster Name[el]=Παράθυρα Name[eo]=Fenestroj Name[es]=Ventanas Name[es_AR]=Ventanas Name[es_CL]=Ventanas Name[es_CO]=Ventanas Name[es_CR]=Ventanas Name[es_DO]=Ventanas Name[es_EC]=Ventanas Name[es_ES]=Ventanas Name[es_GT]=Ventanas Name[es_HN]=Ventanas Name[es_MX]=Ventanas Name[es_NI]=Ventanas Name[es_PA]=Ventanas Name[es_PE]=Ventanas Name[es_PR]=Ventanas Name[es_SV]=Ventanas Name[es_UY]=Ventanas Name[es_VE]=Ventanas Name[eu]=Leihoak Name[eu_ES]=Leihoak Name[fi]=Ikkunat Name[fi_FI]=Ikkunat Name[fil]=Mga dungawan Name[fr]=Fenêtres Name[fr_CA]=Fenêtres Name[fr_FR]=Fenêtres Name[ga]=Fuinneoga Name[gd]=Uinneagan Name[gl]=Xanelas Name[gl_ES]=Xanelas Name[hr]=Prozori Name[hu]=Ablakok Name[hu_HU]=Ablakok Name[id]=Jendela-jendela Name[id_ID]=Jendela-jendela Name[is]=Gluggar Name[it]=Finestre Name[ja]=ウィンドウ Name[ja_JP]=ウィンドウ Name[ko]=창 Name[ko_KR]=창 Name[lo]=ໜ້າຕ່າງ Name[lt]=Langai Name[lt_LT]=Langai Name[lv]=Logi Name[lv_LV]=Logi Name[ms]=Tetingkap Name[nb]=Vinduer Name[nb_NO]=Vinduer Name[nds]=Fenster Name[nl]=Vensters Name[nl_NL]=Vensters Name[oc]=Fenèstras Name[om]=Foddaalee Name[pa]=ਵਿੰਡੋਜ਼ Name[pl]=Okna Name[pl_PL]=Okna Name[pt]=Janelas Name[pt_BR]=Janelas Name[pt_PT]=Janelas Name[ro]=Ferestre Name[ro_RO]=Ferestre Name[ru]=Окна Name[sk]=Okná Name[sk_SK]=Okná Name[sl]=Okna Name[sl_SI]=Okna Name[sq]=Dritaret Name[sr]=Прозори Name[sr@Latn]=Прозори Name[sr@ije]=Прозори Name[sr@ijekavianlatin]=Прозори Name[sr@latin]=Прозори Name[sr_RS]=Прозори Name[sv]=Fönster Name[sv_SE]=Fönster Name[ta]=சாளரங்கள் Name[ta_LK]=சாளரங்கள் Name[th]=หน้าต่าง Name[th_TH]=หน้าต่าง Name[tr]=Pencereler Name[uk]=Вікна Name[uk_UA]=Вікна Name[ur]=ونڈوز Name[ur_PK]=ونڈوز Name[vi]=Cửa sổ Name[vi_VN]=Cửa sổ Name[zh_CN]=窗口 Name[zh_HK]=視窗 Name[zh_TW]=視窗 Comment=Manage window preferences Comment[am]=የመስኮት ምርጫዎች አስተዳዳሪ Comment[ar]=إدارة تفضيلات النافذة Comment[be]=Кіраванне наладамі вокнаў Comment[be@latin]=Кіраванне наладамі вокнаў Comment[bg]=Управление настройките на прозорците Comment[bg_BG]=Управление настройките на прозорците Comment[bs]=Upravljaj postavkama prozora Comment[ca]=Administrar les preferències de finestres Comment[ca@valencia]=Administra les preferències de finestres Comment[ca_ES@valencia]=Administra les preferències de finestres Comment[cs]=Změnit nastavení chování oken Comment[cs_CZ]=Změnit nastavení chování oken Comment[cy]=Rheoli dewisiadau ffenestr Comment[da]=Administrér vinduesindstillinger Comment[da_DK]=Administrér vinduesindstillinger Comment[de]=Fenstereinstellungen verwalten Comment[de_DE]=Fenstereinstellungen verwalten Comment[el]=Ρυθμίστε τις επιλογές παραθύρων Comment[es]=Administrar las preferencias de ventanas Comment[es_AR]=Administrar las preferencias de ventanas Comment[es_CL]=Administrar las preferencias de ventanas Comment[es_CO]=Administrar las preferencias de ventanas Comment[es_CR]=Administrar las preferencias de ventanas Comment[es_DO]=Administrar las preferencias de ventanas Comment[es_EC]=Administrar las preferencias de ventanas Comment[es_ES]=Administrar las preferencias de ventanas Comment[es_GT]=Administrar las preferencias de ventanas Comment[es_HN]=Administrar las preferencias de ventanas Comment[es_MX]=Administrar las preferencias de ventanas Comment[es_NI]=Administrar las preferencias de ventanas Comment[es_PA]=Administrar las preferencias de ventanas Comment[es_PE]=Administrar las preferencias de ventanas Comment[es_PR]=Administrar las preferencias de ventanas Comment[es_SV]=Administrar las preferencias de ventanas Comment[es_UY]=Administrar las preferencias de ventanas Comment[es_VE]=Administrar las preferencias de ventanas Comment[eu]=Kudeatu leiho-hobespenak Comment[eu_ES]=Kudeatu leiho-hobespenak Comment[fi]=Muokkaa ikkunoiden asetuksia Comment[fi_FI]=Muokkaa ikkunoiden asetuksia Comment[fr]=Gérer les préférences de la fenêtre Comment[fr_CA]=Gérer les préférences de la fenêtre Comment[fr_FR]=Gérer les préférences de la fenêtre Comment[gd]=Stiùirich roghainnean nan uinneagan Comment[gl]=Administra as preferencias da xanela Comment[gl_ES]=Administra as preferencias da xanela Comment[hr]=Upravljajte osobitostima prozora Comment[hu]=Ablak beállítások kezelése Comment[hu_HU]=Ablak beállítások kezelése Comment[id]=Atur preferensi jendela Comment[id_ID]=Atur preferensi jendela Comment[is]=Sýsla með kjörstillingar glugga Comment[it]=Gestisci preferenze finesta Comment[ja]=ウインドウ管理の設定 Comment[ja_JP]=ウインドウ管理の設定 Comment[ko]=창 설정을 관리합니다 Comment[ko_KR]=창 설정을 관리합니다 Comment[lt]=Tvarkyti lango nuostatas Comment[lt_LT]=Tvarkyti lango nuostatas Comment[lv]=Pārvaldīt logu preferences Comment[lv_LV]=Pārvaldīt logu preferences Comment[ms]=Mengurus pilihan tetingkap Comment[nb]=Håndter vindusinnstillinger Comment[nb_NO]=Håndter vindusinnstillinger Comment[nds]=Fenster-Einstellungen bearbeiten Comment[nl]=Venstervoorkeuren beheren Comment[nl_NL]=Venstervoorkeuren beheren Comment[pl]=Zarządzaj preferencjami okien Comment[pl_PL]=Zarządzaj preferencjami okien Comment[pt]=Gerir preferências de janelas Comment[pt_BR]=Gerenciar preferências de janelas Comment[pt_PT]=Gerir preferências de janelas Comment[ro]=Gestionează preferințele ferestrelor Comment[ro_RO]=Gestionează preferințele ferestrelor Comment[ru]=Управление настройками окон Comment[sk]=Spravovať predvoľby okien Comment[sk_SK]=Spravovať predvoľby okien Comment[sl]=Upravljajte z možnostmi oken Comment[sl_SI]=Upravljajte z možnostmi oken Comment[sq]=Menaxhoni parapëlqimet e dritares Comment[sr]=Управљање поставкама прозора Comment[sr@Latn]=Управљање поставкама прозора Comment[sr@ije]=Управљање поставкама прозора Comment[sr@ijekavianlatin]=Управљање поставкама прозора Comment[sr@latin]=Управљање поставкама прозора Comment[sr_RS]=Управљање поставкама прозора Comment[sv]=Hantera fönsterinställningar Comment[sv_SE]=Hantera fönsterinställningar Comment[tg]=Идоракунии хусусиятҳои равзана Comment[th]=จัดการการตั้งค่าหน้าต่างขั้นสูง Comment[th_TH]=จัดการการตั้งค่าหน้าต่างขั้นสูง Comment[tr]=Pencere tercihlerini yönet Comment[uk]=Керувати параметрами вікон Comment[uk_UA]=Керувати параметрами вікон Comment[ur]=ونڈو کی ترجیحات Comment[ur_PK]=ونڈو کی ترجیحات Comment[vi]=Quản lý tuỳ thích cửa sổ Comment[vi_VN]=Quản lý tuỳ thích cửa sổ Comment[zh_CN]=管理窗口样式 Comment[zh_HK]=管理視窗偏好設定 Comment[zh_TW]=管理視窗偏好設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-users.desktop0000664000175000017500000003534112625666430026132 0ustar fabiofabio[Desktop Entry] Exec=cinnamon-settings-users Icon=system-users Terminal=false Type=Application Categories=System;Settings; StartupNotify=false OnlyShowIn=X-Cinnamon; Keywords=Preferences;Settings; Name=Users and Groups Name[af]=Gebruikers en Groepe Name[am]=ተጠቃሚዎች እና ቡድኖች Name[ar]=المستخدمين والمجموعات Name[ast]=Usuarios y grupos Name[az]=İstifadəçilər və Qruplar Name[az_IR]=İstifadəçilər və Qruplar Name[be]=Карыстальнікі і групы Name[be@latin]=Карыстальнікі і групы Name[bg]=Потребители и Групи Name[bg_BG]=Потребители и Групи Name[bn]=ব্যবহারকারীগণ এবং দলসমূহ Name[bn_BD]=ব্যবহারকারীগণ এবং দলসমূহ Name[bn_IN]=ব্যবহারকারীগণ এবং দলসমূহ Name[bs]=Korisnici i grupe Name[ca]=Usuaris i grups Name[ca@valencia]=Usuaris i grups Name[ca_ES@valencia]=Usuaris i grups Name[cs]=Uživatelé a skupiny Name[cs_CZ]=Uživatelé a skupiny Name[cy]=Defnyddwyr a Grwpiau Name[da]=Brugere og grupper Name[da_DK]=Brugere og grupper Name[de]=Benutzer und Gruppen Name[de_DE]=Benutzer und Gruppen Name[el]=Χρήστες και Ομάδες Name[eo]=Uzantoj kaj grupoj Name[es]=Usuarios y grupos Name[es_AR]=Usuarios y Grupos Name[es_CL]=Usuarios y grupos Name[es_CO]=Usuarios y grupos Name[es_CR]=Usuarios y grupos Name[es_DO]=Usuarios y grupos Name[es_EC]=Usuarios y grupos Name[es_ES]=Usuarios y grupos Name[es_GT]=Usuarios y grupos Name[es_HN]=Usuarios y grupos Name[es_MX]=Usuarios y grupos Name[es_NI]=Usuarios y grupos Name[es_PA]=Usuarios y grupos Name[es_PE]=Usuarios y grupos Name[es_PR]=Usuarios y grupos Name[es_SV]=Usuarios y grupos Name[es_UY]=Usuarios y grupos Name[es_VE]=Usuarios y grupos Name[et]=Kasutajad ja grupid Name[et_EE]=Kasutajad ja grupid Name[eu]=Erabiltzaileak eta taldeak Name[eu_ES]=Erabiltzaileak eta taldeak Name[fi]=Käyttäjät ja ryhmät Name[fi_FI]=Käyttäjät ja ryhmät Name[fil]=Ang mga gumagamit at mga Grupo Name[fr]=Utilisateurs et Groupes Name[fr_CA]=Utilisateurs et groupes Name[fr_FR]=Utilisateurs et Groupes Name[fy]=Brûkers en Ploechen Name[gd]=Cleachdaichean agus buidhnean Name[gl]=Usuarios e grupos Name[gl_ES]=Usuarios e grupos Name[he]=משתמשים וקבוצות Name[he_IL]=משתמשים וקבוצות Name[hi]=यूजर एवं ग्रुप Name[hr]=Korisnici i grupe Name[hu]=Felhasználók és csoportok Name[hu_HU]=Felhasználók és csoportok Name[hy]=Օգտվողներ և խմբեր Name[id]=Pengguna dan Grup Name[id_ID]=Pengguna dan Grup Name[is]=Notendur og hópar Name[it]=Utenti e gruppi Name[ja]=ユーザとグループの管理 Name[ja_JP]=ユーザとグループの管理 Name[ka]=მომხმარებლები და ჯგუფები Name[km]=អ្នក​ប្រើ និង​ក្រុម Name[km_KH]=អ្នក​ប្រើ និង​ក្រុម Name[ko]=사용자와그룹 Name[ko_KR]=사용자와그룹 Name[ku]=Bikarhêner û Kom Name[ku_IQ]=Bikarhêner û Kom Name[la]=Homines annuntiaturi et circli Name[li]=Gebroekers en groepe Name[lo]=ຜູ້ໃຊ້ແລະກຸ່ມ Name[lt]=Naudotojų ir grupių nustatymai Name[lt_LT]=Naudotojų ir grupių nustatymai Name[lv]=Lietotāji un Grupas Name[lv_LV]=Lietotāji un Grupas Name[mr]=वापरकर्ते व गट Name[ms]=Pengguna dan Kumpulan Name[nb]=Brukere og grupper Name[nb_NO]=Brukere og grupper Name[nds]=Benutzer und Gruppen Name[nl]=Gebruikers en groepen Name[nl_NL]=Gebruikers en groepen Name[oc]=Utilizaires e gropes Name[om]=Fayyadamtootaa fi gareewwan Name[pa]=ਯੂਜ਼ਰ ਅਤੇ ਗਰੁੱਪ Name[pl]=Użytkownicy i grupy Name[pl_PL]=Użytkownicy i grupy Name[pt]=Utilizadores e grupos Name[pt_BR]=Usuários e grupos Name[pt_PT]=Utilizadores e grupos Name[ro]=Utilizatori și grupuri Name[ro_RO]=Utilizatori și grupuri Name[ru]=Пользователи и группы Name[si]=භාවිතා කරන්නන් සහ සමුහ Name[sk]=Používatelia a skupiny Name[sk_SK]=Používatelia a skupiny Name[sl]=Uporabniki in skupine Name[sl_SI]=Uporabniki in skupine Name[sq]=Përdoruesit dhe Grupet Name[sr]=Корисници и групе Name[sr@Latn]=Корисници и групе Name[sr@ije]=Корисници и групе Name[sr@ijekavianlatin]=Корисници и групе Name[sr@latin]=Корисници и групе Name[sr_RS]=Корисници и групе Name[sv]=Användare och grupper Name[sv_SE]=Användare och grupper Name[ta]=பயனர்கள் மற்றும் குழுக்கள் Name[ta_LK]=பயனர்கள் மற்றும் குழுக்கள் Name[tg]=Корбарон ва гурӯҳҳо Name[th]=ผู้ใช้และกลุ่มผู้ใช้ Name[th_TH]=ผู้ใช้และกลุ่มผู้ใช้ Name[tl]=Mga Gumagamit at Pangkat Name[tl_PH]=Mga Gumagamit at Pangkat Name[tr]=Kullanıcılar ve Gruplar Name[ug]=ئىشلەتكۈچىلەر ۋە گۇرۇپپىلار Name[uk]=Користувачі і групи Name[uk_UA]=Користувачі і групи Name[ur]=صارفین اور گروہ Name[ur_PK]=صارفین اور گروہ Name[uz]=Фойдаланувчилар ва гуруҳлар Name[uz@Latn]=Фойдаланувчилар ва гуруҳлар Name[uz@cyrillic]=Фойдаланувчилар ва гуруҳлар Name[vi]=Người dùng và Nhóm Name[vi_VN]=Người dùng và Nhóm Name[zh_CN]=用户和用户组 Name[zh_HK]=使用者與羣組 Name[zh_TW]=使用者與群組 Comment=Add or remove users and groups Comment[af]=Voeg of verwyder gebruikers en groepe Comment[am]=ተጠቃሚዎች እና ቡድኖች መጨመሪያ ወይንም ማስወገጃ Comment[ar]=اضافة أو ازالة المستخدمين والمجموعات Comment[ast]=Amiesta o desanicia usuarios y grupos Comment[az]=İstifadəçi və qrupları əlavə et və ya sil Comment[az_IR]=İstifadəçi və qrupları əlavə et və ya sil Comment[be]=Дадаць або выдаліць карыстальнікаў і групы Comment[be@latin]=Дадаць або выдаліць карыстальнікаў і групы Comment[bg]=Добавяне или премахване на потребители и групи Comment[bg_BG]=Добавяне или премахване на потребители и групи Comment[bn]=যোগ করুন অথবা মুছে ফেলুন ব্যবহারকারী এবং দল Comment[bn_BD]=যোগ করুন অথবা মুছে ফেলুন ব্যবহারকারী এবং দল Comment[bn_IN]=যোগ করুন অথবা মুছে ফেলুন ব্যবহারকারী এবং দল Comment[bs]=Dodajte ili uklonite korisnike i grupe Comment[ca]=Afegiu o suprimiu usuaris i grups Comment[ca@valencia]=Afegir o eliminar usuaris i grups Comment[ca_ES@valencia]=Afegir o eliminar usuaris i grups Comment[cs]=Přidat nebo odstranit uživatele a skupiny Comment[cs_CZ]=Přidat nebo odstranit uživatele a skupiny Comment[cy]=Ychwanegu neu dynnu defnyddwyr a grwpiau Comment[da]=Tilføj eller fjern brugere og grupper Comment[da_DK]=Tilføj eller fjern brugere og grupper Comment[de]=Benutzer und Gruppen hinzufügen oder entfernen Comment[de_DE]=Benutzer und Gruppen hinzufügen oder entfernen Comment[el]=Προσθήκη ή αφαίρεση χρηστών και ομάδων Comment[eo]=Aldoni aŭ forigi uzantojn kaj grupojn Comment[es]=Añadir o eliminar usuarios y grupos Comment[es_AR]=Agregar o quitar usuarios y grupos Comment[es_CL]=Añadir o eliminar usuarios y grupos Comment[es_CO]=Añadir o eliminar usuarios y grupos Comment[es_CR]=Añadir o eliminar usuarios y grupos Comment[es_DO]=Añadir o eliminar usuarios y grupos Comment[es_EC]=Añadir o eliminar usuarios y grupos Comment[es_ES]=Añadir o eliminar usuarios y grupos Comment[es_GT]=Añadir o eliminar usuarios y grupos Comment[es_HN]=Añadir o eliminar usuarios y grupos Comment[es_MX]=Añadir o eliminar usuarios y grupos Comment[es_NI]=Añadir o eliminar usuarios y grupos Comment[es_PA]=Añadir o eliminar usuarios y grupos Comment[es_PE]=Añadir o eliminar usuarios y grupos Comment[es_PR]=Añadir o eliminar usuarios y grupos Comment[es_SV]=Añadir o eliminar usuarios y grupos Comment[es_UY]=Añadir o eliminar usuarios y grupos Comment[es_VE]=Añadir o eliminar usuarios y grupos Comment[et]=Lisa või eemalda kasutajaid ja gruppe Comment[et_EE]=Lisa või eemalda kasutajaid ja gruppe Comment[eu]=Gehitu edo ezabatu erabiltzaileak eta taldeak Comment[eu_ES]=Gehitu edo ezabatu erabiltzaileak eta taldeak Comment[fi]=Lisää tai poista käyttäjiä ja ryhmiä Comment[fi_FI]=Lisää tai poista käyttäjiä ja ryhmiä Comment[fil]=Magdagdag o mag-alis ng mga user at mga pangkat Comment[fr]=Ajouter ou supprimer des utilisateurs et des groupes Comment[fr_CA]=Ajouter ou supprimer des utilisateurs et des groupes Comment[fr_FR]=Ajouter ou supprimer des utilisateurs et des groupes Comment[fy]=Brûkers en ploechen taheakje of ferwiderje Comment[gd]=Cuir ris no thoir air falbh cleachdaichean is buidhnean Comment[gl]=Engada ou retire usuarios e grupos Comment[gl_ES]=Engada ou retire usuarios e grupos Comment[he]=הוספה או הסרה של משתמשים וקבוצות Comment[he_IL]=הוספה או הסרה של משתמשים וקבוצות Comment[hi]=यूजर एवं ग्रुप जोड़े अथवा हटायें Comment[hr]=Dodaj ili ukloni korisnike i grupe Comment[hu]=Felhasználók és csoportok hozzáadása vagy eltávolítása Comment[hu_HU]=Felhasználók és csoportok hozzáadása vagy eltávolítása Comment[hy]=Ավելացնել կամ ջնջել օգտվողներ և խմբեր Comment[id]=Tambah atau hapus pemakai dan kelompoknya Comment[id_ID]=Tambah atau hapus pemakai dan kelompoknya Comment[is]=Bæta við eða fjarlægja notendur og hópa Comment[it]=Aggiunge o rimuove utenti e gruppi Comment[ja]=ユーザ及びグループの追加や削除 Comment[ja_JP]=ユーザ及びグループの追加や削除 Comment[ka]=მომხმარებლების და ჯგუფების დამატება ან ამოშლა Comment[ko]=사용자와그룹 추가 또는 제거 Comment[ko_KR]=사용자와그룹 추가 또는 제거 Comment[ku]=Bikarhêner û koman lê zêde bike an jî jê bibe Comment[ku_IQ]=Bikarhêner û koman lê zêde bike an jî jê bibe Comment[la]=Addere aut delere homines annuntiaturi et circli Comment[li]=Gebroekers en groepe toevoege of verwijdere Comment[lo]=ຕື່ມຫຼືລຶບຜູ້ໃຊ້ແລະກູ່ມ Comment[lt]=Pridėti ar šalinti naudotojus bei grupes Comment[lt_LT]=Pridėti ar šalinti naudotojus bei grupes Comment[lv]=Pievienot vai dzēst lietotājus un grupas Comment[lv_LV]=Pievienot vai dzēst lietotājus un grupas Comment[mr]=वापरकर्ते व गट जोडा किंवा काढून टाका Comment[ms]=Tambah atau buang pengguna dan kumpulan Comment[nb]=Legg til eller fjern brukere og grupper Comment[nb_NO]=Legg til eller fjern brukere og grupper Comment[nds]=Benutzer oder Gruppen hinzufügen bzw. löschen Comment[nl]=Gebruikers en groepen toevoegen of verwijderen Comment[nl_NL]=Gebruikers en groepen toevoegen of verwijderen Comment[oc]=Apondre o suprimir d'utilizaires o de gropes Comment[om]=fayyadamtootaa fi gareewwan dabaluu fi haquu Comment[pa]=ਯੂਜ਼ਰ ਤੇ ਗਰੁੱਪ ਸ਼ਾਮਲ ਕਰੋ ਜਾਂ ਹਟਾਓ Comment[pl]=Dodawanie lub usuwanie użytkowników i grup Comment[pl_PL]=Dodawanie lub usuwanie użytkowników i grup Comment[pt]=Adicionar ou remover utilizadores e grupos Comment[pt_BR]=Adicionar ou remover usuários e grupos Comment[pt_PT]=Adicionar ou remover utilizadores e grupos Comment[ro]=Adăugă sau elimină utilizatori și grupuri Comment[ro_RO]=Adăugă sau elimină utilizatori și grupuri Comment[ru]=Добавить или удалить пользователей или группы Comment[si]=පරිශීලකයන් සහ සමුහ එක් කිරීම හෝ ඉවත් කිරීම Comment[sk]=Pridanie alebo odstránenie používateľov a skupín Comment[sk_SK]=Pridanie alebo odstránenie používateľov a skupín Comment[sl]=Dodajte ali odstranite uporabnike in skupine Comment[sl_SI]=Dodajte ali odstranite uporabnike in skupine Comment[sq]=Shto ose hiq përdorues dhe grupe Comment[sr]=Додајте или уклоните кориснике и групе Comment[sr@Latn]=Додајте или уклоните кориснике и групе Comment[sr@ije]=Додајте или уклоните кориснике и групе Comment[sr@ijekavianlatin]=Додајте или уклоните кориснике и групе Comment[sr@latin]=Додајте или уклоните кориснике и групе Comment[sr_RS]=Додајте или уклоните кориснике и групе Comment[sv]=Lägg till eller ta bort användare och grupper Comment[sv_SE]=Lägg till eller ta bort användare och grupper Comment[ta]=பயனர் மற்றும் குழுக்களை சேர் அல்லது நீக்கு Comment[ta_LK]=பயனர் மற்றும் குழுக்களை சேர் அல்லது நீக்கு Comment[tg]=Илова ё тоза кардани корбарон ва гурӯҳҳо Comment[th]=เพิ่มหรือลบผู้ใช้และกลุ่ม Comment[th_TH]=เพิ่มหรือลบผู้ใช้และกลุ่ม Comment[tl]=Magdagdag o magbawas ng mga user at pangkat Comment[tl_PH]=Magdagdag o magbawas ng mga user at pangkat Comment[tr]=Kullanıcıları ve grupları ekle ya da sil Comment[ug]=ئىشلەتكۈچى ۋە گۇرۇپپا قوش ياكى ئۆچۈر Comment[uk]=Додавання або вилучення записів користувачів та груп Comment[uk_UA]=Додавання або вилучення записів користувачів та груп Comment[ur]=صارفین اور گروہ شامل کریں یا مٹائیں Comment[ur_PK]=صارفین اور گروہ شامل کریں یا مٹائیں Comment[uz]=Фойдаланувчилар ва гуруҳларни қўшиш ёки олиб ташлаш Comment[uz@Latn]=Фойдаланувчилар ва гуруҳларни қўшиш ёки олиб ташлаш Comment[uz@cyrillic]=Фойдаланувчилар ва гуруҳларни қўшиш ёки олиб ташлаш Comment[vi]=Thêm hay loại bỏ người dùng và nhóm Comment[vi_VN]=Thêm hay loại bỏ người dùng và nhóm Comment[zh_CN]=添加或删除用户和组 Comment[zh_HK]=新增或移除使用者與羣組 Comment[zh_TW]=加入或移除使用者與群組 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-desklets.desktop0000664000175000017500000002023712625666430026605 0ustar fabiofabio[Desktop Entry] Icon=cs-desklets Exec=cinnamon-settings desklets Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Desklets Name[am]=ዴስክሌቶች Name[ar]=معلّقات Name[be]=Дэсклеты Name[be@latin]=Дэсклеты Name[bg]=Десклети Name[bg_BG]=Десклети Name[bs]=Deskleti Name[cs]=Desklety Name[cs_CZ]=Desklety Name[cy]=Desgledi Name[da]=Skrivebordsprogrammer Name[da_DK]=Skrivebordsprogrammer Name[el]=Εφαρμογές Οθόνης Name[eo]=Deskletoj Name[eu]=Desklet-ak Name[eu_ES]=Desklet-ak Name[fi]=Työpöytäsovelmat Name[fi_FI]=Työpöytäsovelmat Name[ga]=Deiscíní Name[gl]=Trebellos Name[gl_ES]=Trebellos Name[hr]=Deskleti Name[hu]=Asztalkalmazások Name[hu_HU]=Asztalkalmazások Name[id]=Desklet Name[id_ID]=Desklet Name[is]=Skjáborðsgræjur Name[it]=Desklet Name[ja]=デスクレット Name[ja_JP]=デスクレット Name[ko]=데스크릿 Name[ko_KR]=데스크릿 Name[lo]=ເດສເລັດ Name[lt]=Ekrano programėlės Name[lt_LT]=Ekrano programėlės Name[lv]=Sīkrīki Name[lv_LV]=Sīkrīki Name[ms]=Desklet Name[nb]=Skrivebordsprogrammer Name[nb_NO]=Skrivebordsprogrammer Name[nl]=Minitoepassingen Name[nl_NL]=Minitoepassingen Name[pa]=ਡੈਸਕਲੇਟਸ Name[pl]=Desklety Name[pl_PL]=Desklety Name[ro]=Deskleturi Name[ro_RO]=Deskleturi Name[ru]=Десклеты Name[sk]=Desklety Name[sk_SK]=Desklety Name[sl]=Apleti namizja Name[sl_SI]=Apleti namizja Name[sq]=Desklet-ët Name[sr]=Справице површи Name[sr@Latn]=Справице површи Name[sr@ije]=Справице површи Name[sr@ijekavianlatin]=Справице површи Name[sr@latin]=Справице површи Name[sr_RS]=Справице површи Name[sv]=Skrivbordsprogram Name[sv_SE]=Skrivbordsprogram Name[ta]=திரை நிரல்கள் Name[ta_LK]=திரை நிரல்கள் Name[tg]=Барномаҳои мизи корӣ Name[th]=เดสก์เล็ต Name[th_TH]=เดสก์เล็ต Name[tr]=Masaüstü Uygulamacıkları Name[uk]=Десклети Name[uk_UA]=Десклети Name[ur]=ڈیسکلیٹ Name[ur_PK]=ڈیسکلیٹ Name[uz]=Десклетлар Name[uz@Latn]=Десклетлар Name[uz@cyrillic]=Десклетлар Name[vi]=Tiểu trình màn hình Name[vi_VN]=Tiểu trình màn hình Name[zh_CN]=桌面小工具 Name[zh_HK]=桌面小工具 Name[zh_TW]=桌面小程式 Comment=Manage your Cinnamon desklets Comment[am]=የሲናሞን ዴስክሌትስ አስተዳዳሪ Comment[ar]=إدارة معلقات سطح المكتب Comment[ast]=Alministra los tos desklets de Cinnamon Comment[be]=Кiраванне дэсклетамi Cinnamon Comment[be@latin]=Кiраванне дэсклетамi Cinnamon Comment[bg]=Управлявайте Вашите Cinnamon десклети Comment[bg_BG]=Управлявайте Вашите Cinnamon десклети Comment[bs]=Upravljajte vašim Cinnamon deskletima Comment[ca]=Administrar desklets de Cinnamon Comment[ca@valencia]=Administreu els desklets de Cinnamon Comment[ca_ES@valencia]=Administreu els desklets de Cinnamon Comment[cs]=Správa Cinnamon deskletů Comment[cs_CZ]=Správa Cinnamon deskletů Comment[cy]=Rheolwch eich desglenni Cinnamon chi Comment[da]=Administrér dine Cinnamon-skrivebordsprogrammer Comment[da_DK]=Administrér dine Cinnamon-skrivebordsprogrammer Comment[de]=Cinnamon-Desklets verwalten Comment[de_DE]=Cinnamon-Desklets verwalten Comment[el]=Διαχείριση των εφαρμογών οθόνης του Cinnamon Comment[es]=Administrar desklets de Cinnamon Comment[es_AR]=Administrar desklets de Cinnamon Comment[es_CL]=Administrar desklets de Cinnamon Comment[es_CO]=Administrar desklets de Cinnamon Comment[es_CR]=Administrar desklets de Cinnamon Comment[es_DO]=Administrar desklets de Cinnamon Comment[es_EC]=Administrar desklets de Cinnamon Comment[es_ES]=Administrar desklets de Cinnamon Comment[es_GT]=Administrar desklets de Cinnamon Comment[es_HN]=Administrar desklets de Cinnamon Comment[es_MX]=Administrar desklets de Cinnamon Comment[es_NI]=Administrar desklets de Cinnamon Comment[es_PA]=Administrar desklets de Cinnamon Comment[es_PE]=Administrar desklets de Cinnamon Comment[es_PR]=Administrar desklets de Cinnamon Comment[es_SV]=Administrar desklets de Cinnamon Comment[es_UY]=Administrar desklets de Cinnamon Comment[es_VE]=Administrar desklets de Cinnamon Comment[eu]=Kudeatu zure Cinnamon desklet-ak Comment[eu_ES]=Kudeatu zure Cinnamon desklet-ak Comment[fi]=Hallitse Cinnamon-sovelmia Comment[fi_FI]=Hallitse Cinnamon-sovelmia Comment[fr]=Gérez vos desklets Cinnamon Comment[fr_CA]=Gérer vos desklets Cinnamon Comment[fr_FR]=Gérez vos desklets Cinnamon Comment[gd]=Stiùirich na desklets Cinnamon agad Comment[gl]=Administra os trebellos do Cinnamon Comment[gl_ES]=Administra os trebellos do Cinnamon Comment[hr]=Upravljajte Cinnamon deskletima Comment[hu]=Cinnamon asztalkalmazások kezelése Comment[hu_HU]=Cinnamon asztalkalmazások kezelése Comment[id]=Atur desklet Cinnamon Comment[id_ID]=Atur desklet Cinnamon Comment[is]=Sýsla með stillingar fyrir Cinnamon skjáborðsgræjur (desklets) Comment[it]=Gestione dei desklets di Cinnamon Comment[ja]=Cinnamon デスクレット管理 Comment[ja_JP]=Cinnamon デスクレット管理 Comment[ko]=시나몬 데스크릿을 관리합니다 Comment[ko_KR]=시나몬 데스크릿을 관리합니다 Comment[lo]=ຈັດການເດສເລັດ Cinnamon ຂອງເຈົ້າ Comment[lt]=Tvarkyti Cinnamon ekrano programėles Comment[lt_LT]=Tvarkyti Cinnamon ekrano programėles Comment[lv]=Pārvaldīt savus Cinnamon sīkrīkus Comment[lv_LV]=Pārvaldīt savus Cinnamon sīkrīkus Comment[ms]=Mengurus desklet Cinnamon anda Comment[nb]=Behandle Cinnamon-skrivebordsprogrammene dine Comment[nb_NO]=Behandle Cinnamon-skrivebordsprogrammene dine Comment[nds]=Cinnamon Desklets bearbeiten Comment[nl]=Beheer uw minitoepassingen voor Cinnamon Comment[nl_NL]=Beheer uw minitoepassingen voor Cinnamon Comment[pl]=Zarządaj swoimi deskletami Cinnamona Comment[pl_PL]=Zarządaj swoimi deskletami Cinnamona Comment[pt]=Gerir os seus desklets do Cinnamon Comment[pt_BR]=Gerenciar os desklets do Cinnamon Comment[pt_PT]=Gerir os seus desklets do Cinnamon Comment[ro]=Gestionează deskleturile Cinnamon Comment[ro_RO]=Gestionează deskleturile Cinnamon Comment[ru]=Управление десклетами Cinnamon Comment[sk]=Spravovať desklety Cinnamon Comment[sk_SK]=Spravovať desklety Cinnamon Comment[sl]=Upravljajte z apleti namizja Cinnamona Comment[sl_SI]=Upravljajte z apleti namizja Cinnamona Comment[sq]=Menaxhoni desklet-ët tuaj në Cinnamon Comment[sr]=Управљање справицама површи Цимета Comment[sr@Latn]=Управљање справицама површи Цимета Comment[sr@ije]=Управљање справицама површи Цимета Comment[sr@ijekavianlatin]=Управљање справицама површи Цимета Comment[sr@latin]=Управљање справицама површи Цимета Comment[sr_RS]=Управљање справицама површи Цимета Comment[sv]=Hantera Cinnamons skrivbordsprogram Comment[sv_SE]=Hantera Cinnamons skrivbordsprogram Comment[tg]=Идоракунии барномаҳои мизи кории Cinnamon Comment[th]=จัดการเดสก์เล็ต Cinnamon ของคุณ Comment[th_TH]=จัดการเดสก์เล็ต Cinnamon ของคุณ Comment[tr]=Cinnamon masaüstü uygulamacıklarını yönet Comment[uk]=Керування десклетами Cinnamon Comment[uk_UA]=Керування десклетами Cinnamon Comment[ur]=اپنے سنامون کے ڈیسکلیٹ کا انتظام کریں Comment[ur_PK]=اپنے سنامون کے ڈیسکلیٹ کا انتظام کریں Comment[uz]=Синнамон десклетларни бошқариш Comment[uz@Latn]=Синнамон десклетларни бошқариш Comment[uz@cyrillic]=Синнамон десклетларни бошқариш Comment[vi]=Quản lý tiểu dụng màn hình Cinnamon Comment[vi_VN]=Quản lý tiểu dụng màn hình Cinnamon Comment[zh_CN]=管理桌面小工具 Comment[zh_HK]=管理你的 Cinnamon 桌面小工具 Comment[zh_TW]=管理您的 Cinnamon 桌面小程式 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-notifications.desktop0000664000175000017500000001736112625666430027644 0ustar fabiofabio[Desktop Entry] Icon=cs-notifications Exec=cinnamon-settings notifications Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Notifications Name[af]=Kennisgewings Name[am]=ማስታወቂያዎች Name[ar]=الإشعارات Name[ast]=Notificaciones Name[be]=Паведамленні Name[be@latin]=Паведамленні Name[bg]=Известявания Name[bg_BG]=Известявания Name[bn]=বিজ্ঞপ্তিসমূহ Name[bn_BD]=বিজ্ঞপ্তিসমূহ Name[bn_IN]=বিজ্ঞপ্তিসমূহ Name[bs]=Obavještenja Name[ca]=Notificacions Name[ca@valencia]=Notificacions Name[ca_ES@valencia]=Notificacions Name[cs]=Oznámení Name[cs_CZ]=Oznámení Name[cy]=Hysbysiadau Name[da]=Meddelelser Name[da_DK]=Meddelelser Name[de]=Meldungen Name[de_DE]=Meldungen Name[el]=Ειδοποιήσεις Name[eo]=Atentigoj Name[es]=Notificaciones Name[es_AR]=Notificaciones Name[es_CL]=Notificaciones Name[es_CO]=Notificaciones Name[es_CR]=Notificaciones Name[es_DO]=Notificaciones Name[es_EC]=Notificaciones Name[es_ES]=Notificaciones Name[es_GT]=Notificaciones Name[es_HN]=Notificaciones Name[es_MX]=Notificaciones Name[es_NI]=Notificaciones Name[es_PA]=Notificaciones Name[es_PE]=Notificaciones Name[es_PR]=Notificaciones Name[es_SV]=Notificaciones Name[es_UY]=Notificaciones Name[es_VE]=Notificaciones Name[et]=Teavitused Name[et_EE]=Teavitused Name[eu]=Jakinarazpenak Name[eu_ES]=Jakinarazpenak Name[fa]=هشدارها Name[fa_AF]=هشدارها Name[fa_IR]=هشدارها Name[fi]=Ilmoitukset Name[fi_FI]=Ilmoitukset Name[fil]=Mga Abiso Name[fr_CA]=Avis Name[fy]=Notifikaasjes Name[ga]=Fógraí Name[gd]=Brathan Name[gl]=Notificacións Name[gl_ES]=Notificacións Name[he]=התרעות Name[he_IL]=התרעות Name[hr]=Obavijesti Name[hu]=Értesítések Name[hu_HU]=Értesítések Name[hy]=Ծանուցումներ Name[id]=Pemberitahuan Name[id_ID]=Pemberitahuan Name[is]=Tilkynningar Name[it]=Notifiche Name[ja]=通知メッセージ Name[ja_JP]=通知メッセージ Name[km]=ការ​ជូនដំណឹង​ Name[km_KH]=ការ​ជូនដំណឹង​ Name[ko]=알림 Name[ko_KR]=알림 Name[lo]=ການແຈ້ງເຕືອນ Name[lt]=Pranešimai Name[lt_LT]=Pranešimai Name[lv]=Paziņojumi Name[lv_LV]=Paziņojumi Name[ml]=അറിയിപ്പുകൾ Name[ml_IN]=അറിയിപ്പുകൾ Name[mr]=सूचना Name[ms]=Pemberitahuan Name[nb]=Varslinger Name[nb_NO]=Varslinger Name[nds]=Benachrichtigungen Name[ne]=सूचना Name[nl]=Meldingen Name[nl_NL]=Meldingen Name[oc]=Notificacions Name[pa]=ਸੂਚਨਾਵਾਂ Name[pl]=Powiadomienia Name[pl_PL]=Powiadomienia Name[pt]=Notificações Name[pt_BR]=Notificações Name[pt_PT]=Notificações Name[ro]=Notificări Name[ro_RO]=Notificări Name[ru]=Уведомления Name[sk]=Oznámenia Name[sk_SK]=Oznámenia Name[sl]=Obvestila Name[sl_SI]=Obvestila Name[sq]=Njoftimet Name[sr]=Обавештења Name[sr@Latn]=Obaveštenja Name[sr@ije]=Обавештења Name[sr@ijekavianlatin]=Обавештења Name[sr@latin]=Obaveštenja Name[sr_RS]=Обавештења Name[sv]=Notiser Name[sv_SE]=Notiser Name[sw]=Taarifa Name[ta]=அறிவிப்புகள் Name[ta_LK]=அறிவிப்புகள் Name[te]=గమనింపులు Name[tg]=Огоҳиҳо Name[th]=การแจ้งเตือน Name[th_TH]=การแจ้งเตือน Name[tl]=Mga Abiso Name[tl_PH]=Mga Abiso Name[tr]=Bildirimler Name[uk]=Сповіщення Name[uk_UA]=Сповіщення Name[ur]=اطلاعات Name[ur_PK]=اطلاعات Name[uz]=Эслатмалар Name[uz@Latn]=Эслатмалар Name[uz@cyrillic]=Эслатмалар Name[vi]=Thông báo Name[vi_VN]=Thông báo Name[zh_CN]=通知 Name[zh_HK]=通知 Name[zh_TW]=通知 Comment=Notification preferences Comment[am]=የ ማስታወቂያ ምርጫዎች Comment[ar]=تفضيلات التنبيهات Comment[ast]=Preferencies de les notificaciones Comment[bg]=Настройки за известяване Comment[bg_BG]=Настройки за известяване Comment[ca]=Preferències de notificació Comment[ca@valencia]=Preferències de notificació Comment[ca_ES@valencia]=Preferències de notificació Comment[cs]=Nastavení notifikací Comment[cs_CZ]=Nastavení notifikací Comment[cy]=Dewisiadau hysbysiad Comment[da]=Indstillinger for meddelelser Comment[da_DK]=Indstillinger for meddelelser Comment[de]=Benachrichtigungseinstellungen Comment[de_DE]=Benachrichtigungseinstellungen Comment[el]=Προτιμήσεις ειδοποιήσεων Comment[es]=Preferencias de notificación Comment[es_AR]=Preferencias de notificación Comment[es_CL]=Preferencias de notificación Comment[es_CO]=Preferencias de notificación Comment[es_CR]=Preferencias de notificación Comment[es_DO]=Preferencias de notificación Comment[es_EC]=Preferencias de notificación Comment[es_ES]=Preferencias de notificación Comment[es_GT]=Preferencias de notificación Comment[es_HN]=Preferencias de notificación Comment[es_MX]=Preferencias de notificación Comment[es_NI]=Preferencias de notificación Comment[es_PA]=Preferencias de notificación Comment[es_PE]=Preferencias de notificación Comment[es_PR]=Preferencias de notificación Comment[es_SV]=Preferencias de notificación Comment[es_UY]=Preferencias de notificación Comment[es_VE]=Preferencias de notificación Comment[eu]=Jakinerazpenen hobespenak Comment[eu_ES]=Jakinerazpenen hobespenak Comment[fi]=Ilmoitusten asetukset Comment[fi_FI]=Ilmoitusten asetukset Comment[fr]=Préférences des notifications Comment[fr_CA]=Préférences des notifications Comment[fr_FR]=Préférences des notifications Comment[gd]=Roghainnean nam brathan Comment[gl]=Preferencias da notificación Comment[gl_ES]=Preferencias da notificación Comment[hr]=Osobitosti obavijesti Comment[hu]=Értesítések beállításai Comment[hu_HU]=Értesítések beállításai Comment[id]=Preferensi notifikasi Comment[id_ID]=Preferensi notifikasi Comment[is]=Valkostir kerfistilkynninga Comment[it]=Preferenze di notifica Comment[ko]=알림 설정 Comment[ko_KR]=알림 설정 Comment[lt]=Pranešimų nuostatos Comment[lt_LT]=Pranešimų nuostatos Comment[ms]=Tetapan notifikasi Comment[nb]=Varslingsinnstillinger Comment[nb_NO]=Varslingsinnstillinger Comment[nds]=Benachrichtigungseinstellungen Comment[nl]=Meldingsvoorkeuren Comment[nl_NL]=Meldingsvoorkeuren Comment[pl]=Preferencje powiadamiania Comment[pl_PL]=Preferencje powiadamiania Comment[pt]=Preferências de notificação Comment[pt_BR]=Preferências de notificação Comment[pt_PT]=Preferências de notificação Comment[ro]=Preferințe notificări Comment[ro_RO]=Preferințe notificări Comment[ru]=Настройки уведомлений Comment[sk]=Predvoľby oznámení Comment[sk_SK]=Predvoľby oznámení Comment[sl]=Možnosti obvestil Comment[sl_SI]=Možnosti obvestil Comment[sq]=Parapëlqimet e njoftimeve Comment[sr]=Поставке обавештења Comment[sr@Latn]=Поставке обавештења Comment[sr@ije]=Поставке обавештења Comment[sr@ijekavianlatin]=Поставке обавештења Comment[sr@latin]=Поставке обавештења Comment[sr_RS]=Поставке обавештења Comment[sv]=Inställningar för Notiser Comment[sv_SE]=Inställningar för Notiser Comment[th]=ตั้งค่าการแจ้งเตือน Comment[th_TH]=ตั้งค่าการแจ้งเตือน Comment[tr]=Bildirim tercihleri Comment[uk]=Налаштування сповіщень Comment[uk_UA]=Налаштування сповіщень Comment[ur]=اطلاعیہ ترجیحات Comment[ur_PK]=اطلاعیہ ترجیحات Comment[vi]=Tùy thích thông báo Comment[vi_VN]=Tùy thích thông báo Comment[zh_CN]=通知设置 Comment[zh_HK]=通知偏好設定 Comment[zh_TW]=通知偏好設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-startup.desktop0000664000175000017500000002004012625666430026461 0ustar fabiofabio[Desktop Entry] Icon=cs-startup-programs Exec=cinnamon-settings startup Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Startup Applications Name[am]=መተግበሪያዎች ማስጀመሪያ Name[ar]=برامج بدء التشغيل Name[ast]=Aplicaciones d'aniciu Name[bg]=Автоматично стартирани програми Name[bg_BG]=Автоматично стартирани програми Name[ca]=Aplicacions a l'inici Name[ca@valencia]=Aplicacions a l'inici Name[ca_ES@valencia]=Aplicacions a l'inici Name[cs]=Aplikace spouštěné při přihlášení Name[cs_CZ]=Aplikace spouštěné při přihlášení Name[cy]=Rhaglenni Cychwyn Name[da]=Opstartsprogrammer Name[da_DK]=Opstartsprogrammer Name[de]=Startprogramme Name[de_DE]=Startprogramme Name[el]=Προγράμματα εκκίνησης Name[es]=Aplicaciones al inicio Name[es_AR]=Aplicaciones al inicio Name[es_CL]=Aplicaciones al inicio Name[es_CO]=Aplicaciones al inicio Name[es_CR]=Aplicaciones al inicio Name[es_DO]=Aplicaciones al inicio Name[es_EC]=Aplicaciones al inicio Name[es_ES]=Aplicaciones al inicio Name[es_GT]=Aplicaciones al inicio Name[es_HN]=Aplicaciones al inicio Name[es_MX]=Aplicaciones al inicio Name[es_NI]=Aplicaciones al inicio Name[es_PA]=Aplicaciones al inicio Name[es_PE]=Aplicaciones al inicio Name[es_PR]=Aplicaciones al inicio Name[es_SV]=Aplicaciones al inicio Name[es_UY]=Aplicaciones al inicio Name[es_VE]=Aplicaciones al inicio Name[et]=Käivitatavad rakendused Name[et_EE]=Käivitatavad rakendused Name[eu]=Abioko aplikazioak Name[eu_ES]=Abioko aplikazioak Name[fi]=Käynnistettävät sovellukset Name[fi_FI]=Käynnistettävät sovellukset Name[fr]=Applications au démarrage Name[fr_CA]=Applications au démarrage Name[fr_FR]=Applications au démarrage Name[fy]=Opstart Applikaasjes Name[gd]=Aplacaidean tòiseachaidh Name[gl]=Aplicativos de inicio Name[gl_ES]=Aplicativos de inicio Name[hr]=Pokrenute aplikacije Name[hu]=Indítópult Name[hu_HU]=Indítópult Name[id]=Aplikasi Permulaan Name[id_ID]=Aplikasi Permulaan Name[is]=Ræsiforrit Name[it]=Applicazioni d'avvio Name[ko]=시작프로그램 Name[ko_KR]=시작프로그램 Name[lo]=ໂປຣແກຣມເລີ່ມລະບົບ Name[lt]=Startuojant paleidžiamos programos Name[lt_LT]=Startuojant paleidžiamos programos Name[ms]=Aplikasi Permulaan Name[nb]=Oppstartsprogrammer Name[nb_NO]=Oppstartsprogrammer Name[nds]=Systemstartprogramme Name[nl]=Opstarttoepassingen Name[nl_NL]=Opstarttoepassingen Name[pl]=Programy startowe Name[pl_PL]=Programy startowe Name[pt]=Aplicações de Arranque Name[pt_BR]=Aplicativos de sessão Name[pt_PT]=Aplicações de Arranque Name[ro]=Aplicații pornite automat Name[ro_RO]=Aplicații pornite automat Name[ru]=Автозагрузка Name[sk]=Aplikácie po spustení Name[sk_SK]=Aplikácie po spustení Name[sl]=Začetni programi Name[sl_SI]=Začetni programi Name[sq]=Programet në nisje Name[sr]=Почетни програми Name[sr@Latn]=Почетни програми Name[sr@ije]=Почетни програми Name[sr@ijekavianlatin]=Почетни програми Name[sr@latin]=Почетни програми Name[sr_RS]=Почетни програми Name[sv]=Uppstartsprogram Name[sv_SE]=Uppstartsprogram Name[th]=โปรแกรมเริ่มวาระ Name[th_TH]=โปรแกรมเริ่มวาระ Name[tr]=Başlangıç Uygulamaları Name[uk]=Автозапуск програм Name[uk_UA]=Автозапуск програм Name[ur]=کمپیوڑر آغاز کی ایپلی کیشنز Name[ur_PK]=کمپیوڑر آغاز کی ایپلی کیشنز Name[vi]=Ứng dụng Khởi chạy Name[vi_VN]=Ứng dụng Khởi chạy Name[zh_CN]=开机自启动程序 Name[zh_HK]=初始啟動程式 Name[zh_TW]=系統啟動應用程式 Comment=Manage your startup applications Comment[bg]=Управление на стартиращите приложения Comment[bg_BG]=Управление на стартиращите приложения Comment[ca]=Gestiona les aplicacions d'inici Comment[ca@valencia]=Gestiona les aplicacions d'inici Comment[ca_ES@valencia]=Gestiona les aplicacions d'inici Comment[cs]=Spravovat vaše aplikace při spuštění Comment[cs_CZ]=Spravovat vaše aplikace při spuštění Comment[cy]=Rheoli eich rhaglenni cychwyn Comment[da]=Håndtér dine opstartsprogrammer Comment[da_DK]=Håndtér dine opstartsprogrammer Comment[de]=Ihre Startprogramme verwalten Comment[de_DE]=Ihre Startprogramme verwalten Comment[es]=Gestionar las aplicaciones de inicio Comment[es_AR]=Gestionar las aplicaciones de inicio Comment[es_CL]=Gestionar las aplicaciones de inicio Comment[es_CO]=Gestionar las aplicaciones de inicio Comment[es_CR]=Gestionar las aplicaciones de inicio Comment[es_DO]=Gestionar las aplicaciones de inicio Comment[es_EC]=Gestionar las aplicaciones de inicio Comment[es_ES]=Gestionar las aplicaciones de inicio Comment[es_GT]=Gestionar las aplicaciones de inicio Comment[es_HN]=Gestionar las aplicaciones de inicio Comment[es_MX]=Gestionar las aplicaciones de inicio Comment[es_NI]=Gestionar las aplicaciones de inicio Comment[es_PA]=Gestionar las aplicaciones de inicio Comment[es_PE]=Gestionar las aplicaciones de inicio Comment[es_PR]=Gestionar las aplicaciones de inicio Comment[es_SV]=Gestionar las aplicaciones de inicio Comment[es_UY]=Gestionar las aplicaciones de inicio Comment[es_VE]=Gestionar las aplicaciones de inicio Comment[eu]=Kudeatu zure abioko aplikazioak Comment[eu_ES]=Kudeatu zure abioko aplikazioak Comment[fi]=Hallinnoi automaattisesti käynnistyviä sovelluksia Comment[fi_FI]=Hallinnoi automaattisesti käynnistyviä sovelluksia Comment[fr]=Gérez vos applications au démarrage Comment[fr_CA]=Gérez vos applications au démarrage Comment[fr_FR]=Gérez vos applications au démarrage Comment[gd]=Stiùirich na h-aplacaidean tòiseachaidh agad Comment[gl]=Administrar os seus aplicativos do inicio Comment[gl_ES]=Administrar os seus aplicativos do inicio Comment[hr]=Upravljajte vašim pokrenutim aplikacijama Comment[hu]=Bejelentkezéskor elindítandó alkalmazások Comment[hu_HU]=Bejelentkezéskor elindítandó alkalmazások Comment[is]=Sýsla með forrit virkjuð í ræsingu Comment[it]=Gestisci le tue applicazioni di avvio Comment[ko]=여러분의 시작 프로그램을 관리하세요 Comment[ko_KR]=여러분의 시작 프로그램을 관리하세요 Comment[nb]=Håndter dine oppstartsprogrammer Comment[nb_NO]=Håndter dine oppstartsprogrammer Comment[nds]=Startanwendungen verwalten Comment[nl]=Beheer uw opstarttoepassingen Comment[nl_NL]=Beheer uw opstarttoepassingen Comment[pl]=Zarządzaj aplikacjami startowymi Comment[pl_PL]=Zarządzaj aplikacjami startowymi Comment[pt]=Gerir as suas aplicações de arranque Comment[pt_BR]=Gerencie seus aplicativos de inicialização Comment[pt_PT]=Gerir as suas aplicações de arranque Comment[ro]=Gestionează aplicațiile pornite automat Comment[ro_RO]=Gestionează aplicațiile pornite automat Comment[ru]=Управление автозапускаемыми приложениями Comment[sk]=Spravovať aplikácie spúšťané automaticky po spustení pc Comment[sk_SK]=Spravovať aplikácie spúšťané automaticky po spustení pc Comment[sq]=Menaxhoni programet tuaja të nisjes Comment[sv]=Hantera dina uppstartsprogram Comment[sv_SE]=Hantera dina uppstartsprogram Comment[th]=จัดการโปรแกรมเริ่มวาระของคุณ Comment[th_TH]=จัดการโปรแกรมเริ่มวาระของคุณ Comment[tr]=Başlangıç uygulamalarınızı yönetin Comment[uk]=Керування запуском Ваших додатків Comment[uk_UA]=Керування запуском Ваших додатків Comment[ur]=کمپیوڑر آغاز کی ایپلی کیشنز کا انتطام کریں Comment[ur_PK]=کمپیوڑر آغاز کی ایپلی کیشنز کا انتطام کریں Comment[vi]=Quản lý ứng dụng khởi chạy của bạn Comment[vi_VN]=Quản lý ứng dụng khởi chạy của bạn Comment[zh_CN]=管理启动程序 Comment[zh_HK]=管理你的始動應用程式 Comment[zh_TW]=管理您的系統啟動應用程式 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-fonts.desktop0000664000175000017500000001750612625666430026125 0ustar fabiofabio[Desktop Entry] Icon=cs-fonts Exec=cinnamon-settings fonts Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Fonts Name[am]=ፊደሎች Name[ar]=الخطوط Name[ast]=Fontes Name[be]=Шрыфты Name[be@latin]=Шрыфты Name[bg]=Шрифтове Name[bg_BG]=Шрифтове Name[bs]=Fontovi Name[ca]=Lletres Name[ca@valencia]=Tipus de lletra Name[ca_ES@valencia]=Tipus de lletra Name[cs]=Písma Name[cs_CZ]=Písma Name[cy]=Ffontiau Name[da]=Skrifttyper Name[da_DK]=Skrifttyper Name[de]=Schriften Name[de_DE]=Schriften Name[el]=Γραμματοσειρές Name[eo]=Tiparoj Name[es]=Tipos de letra Name[es_AR]=Tipos de letra Name[es_CL]=Tipos de letra Name[es_CO]=Tipos de letra Name[es_CR]=Tipos de letra Name[es_DO]=Tipos de letra Name[es_EC]=Tipos de letra Name[es_ES]=Tipos de letra Name[es_GT]=Tipos de letra Name[es_HN]=Tipos de letra Name[es_MX]=Tipos de letra Name[es_NI]=Tipos de letra Name[es_PA]=Tipos de letra Name[es_PE]=Tipos de letra Name[es_PR]=Tipos de letra Name[es_SV]=Tipos de letra Name[es_UY]=Tipos de letra Name[es_VE]=Tipos de letra Name[et]=Kirjatüübid Name[et_EE]=Kirjatüübid Name[eu]=Letra-tipoak Name[eu_ES]=Letra-tipoak Name[fi]=Fontit Name[fi_FI]=Fontit Name[fil]=Pagmumulan ng Name[fr]=Polices de caractères Name[fr_CA]=Polices de caractères Name[fr_FR]=Polices de caractères Name[ga]=Clófhoirne Name[gd]=Cruthan-clò Name[gl]=Tipos de letra Name[gl_ES]=Tipos de letra Name[hr]=Slova Name[hu]=Betűtípusok Name[hu_HU]=Betűtípusok Name[id]=Huruf Name[id_ID]=Huruf Name[is]=Letur Name[it]=Caratteri Name[ja]=フォント Name[ja_JP]=フォント Name[ko]=글꼴 Name[ko_KR]=글꼴 Name[lo]=ຟອນ Name[lt]=Šriftai Name[lt_LT]=Šriftai Name[lv]=Fonti Name[lv_LV]=Fonti Name[ms]=Fon Name[nb]=Skrifttyper Name[nb_NO]=Skrifttyper Name[nds]=Schriftarten Name[nl]=Lettertypen Name[nl_NL]=Lettertypen Name[oc]=Poliças Name[pa]=ਫੋਂਟ Name[pl]=Czcionki Name[pl_PL]=Czcionki Name[pt]=Tipo de letra Name[pt_BR]=Fontes Name[pt_PT]=Tipo de letra Name[ro]=Fonturi Name[ro_RO]=Fonturi Name[ru]=Шрифты Name[sk]=Písmo Name[sk_SK]=Písmo Name[sl]=Pisave Name[sl_SI]=Pisave Name[sq]=Shkronjat Name[sr]=Фонтови Name[sr@Latn]=Фонтови Name[sr@ije]=Фонтови Name[sr@ijekavianlatin]=Фонтови Name[sr@latin]=Фонтови Name[sr_RS]=Фонтови Name[sv]=Teckensnitt Name[sv_SE]=Teckensnitt Name[ta]=எழுத்துருக்கள் Name[ta_LK]=எழுத்துருக்கள் Name[tg]=Шрифтҳо Name[th]=แบบอักษร Name[th_TH]=แบบอักษร Name[tr]=Yazı Tipleri Name[uk]=Шрифти Name[uk_UA]=Шрифти Name[ur]=فونٹ Name[ur_PK]=فونٹ Name[vi]=Phông chữ Name[vi_VN]=Phông chữ Name[zh_CN]=字体 Name[zh_HK]=字型 Name[zh_TW]=字型 Comment=Configure system fonts Comment[am]=የ ስርአቱን ፊደል ማሰናጃ Comment[ar]=إعداد خطوط النظام Comment[ast]=Configura les fontes del sistema Comment[be]=Наладзіць сістэмныя шрыфты Comment[be@latin]=Наладзіць сістэмныя шрыфты Comment[bg]=Конфигуриране на системни шрифтове Comment[bg_BG]=Конфигуриране на системни шрифтове Comment[bs]=Konfigurišite sistemke fontove Comment[ca]=Administrar fonts del sistema Comment[ca@valencia]=Administra fonts del sistema Comment[ca_ES@valencia]=Administra fonts del sistema Comment[cs]=Nastavení systémových písem Comment[cs_CZ]=Nastavení systémových písem Comment[cy]=Ffurfweddwch ffontiau'r system Comment[da]=Konfigurér systemskrifttyper Comment[da_DK]=Konfigurér systemskrifttyper Comment[de]=Systemschriften konfigurieren Comment[de_DE]=Systemschriften konfigurieren Comment[el]=Ρύθμιση γραμματοσειράς συστήματος Comment[es]=Administrar fuentes del sistema Comment[es_AR]=Administrar fuentes del sistema Comment[es_CL]=Administrar fuentes del sistema Comment[es_CO]=Administrar fuentes del sistema Comment[es_CR]=Administrar fuentes del sistema Comment[es_DO]=Administrar fuentes del sistema Comment[es_EC]=Administrar fuentes del sistema Comment[es_ES]=Administrar fuentes del sistema Comment[es_GT]=Administrar fuentes del sistema Comment[es_HN]=Administrar fuentes del sistema Comment[es_MX]=Administrar fuentes del sistema Comment[es_NI]=Administrar fuentes del sistema Comment[es_PA]=Administrar fuentes del sistema Comment[es_PE]=Administrar fuentes del sistema Comment[es_PR]=Administrar fuentes del sistema Comment[es_SV]=Administrar fuentes del sistema Comment[es_UY]=Administrar fuentes del sistema Comment[es_VE]=Administrar fuentes del sistema Comment[eu]=Konfiguratu sistemaren letra-tipoak Comment[eu_ES]=Konfiguratu sistemaren letra-tipoak Comment[fi]=Hallitse järjestelmän fontteja Comment[fi_FI]=Hallitse järjestelmän fontteja Comment[fr]=Configurer les polices du système Comment[fr_CA]=Configurer les polices du système Comment[fr_FR]=Configurer les polices du système Comment[gd]=Rèitich cruthan-clò an t-siostaim Comment[gl]=Configura os tipos de letra do sistema Comment[gl_ES]=Configura os tipos de letra do sistema Comment[hr]=Prilagodite slova sustava Comment[hu]=Rendszer betűtípusok beállítása Comment[hu_HU]=Rendszer betűtípusok beállítása Comment[id]=Pengaturan font Comment[id_ID]=Pengaturan font Comment[is]=Stilla kerfisletur Comment[it]=Configura il carattere di sistema Comment[ja]=システムフォントの管理 Comment[ja_JP]=システムフォントの管理 Comment[ko]=시스템 글꼴 설정 Comment[ko_KR]=시스템 글꼴 설정 Comment[lo]=ປັບແຕ່ງຟອນຂອງລະບົບ Comment[lt]=Nustatyti sistemos šriftus Comment[lt_LT]=Nustatyti sistemos šriftus Comment[lv]=Konfigurēt sistēmas fontus Comment[lv_LV]=Konfigurēt sistēmas fontus Comment[ms]=Konfigur sistem Font Comment[nb]=Konfigurer systemskrifter Comment[nb_NO]=Konfigurer systemskrifter Comment[nds]=Systemschriftarten konfigurieren Comment[nl]=Systeemlettertypen instellen Comment[nl_NL]=Systeemlettertypen instellen Comment[pl]=Konfiguracja czcionek systemu Comment[pl_PL]=Konfiguracja czcionek systemu Comment[pt]=Configurar fontes do sistema Comment[pt_BR]=Configurar fontes do sistema Comment[pt_PT]=Configurar fontes do sistema Comment[ro]=Configurează fonturile sistemului Comment[ro_RO]=Configurează fonturile sistemului Comment[ru]=Настроить системные шрифты Comment[sk]=Nastaviť systémové písma Comment[sk_SK]=Nastaviť systémové písma Comment[sl]=Nastavljajte sistemske pisave Comment[sl_SI]=Nastavljajte sistemske pisave Comment[sq]=Konfiguro shkronjat e sistemit Comment[sr]=Подешавање словних ликова система Comment[sr@Latn]=Подешавање словних ликова система Comment[sr@ije]=Подешавање словних ликова система Comment[sr@ijekavianlatin]=Подешавање словних ликова система Comment[sr@latin]=Подешавање словних ликова система Comment[sr_RS]=Подешавање словних ликова система Comment[sv]=Konfigurera systemets teckensnitt Comment[sv_SE]=Konfigurera systemets teckensnitt Comment[tg]=Танзимоти шрифтҳои системавӣ Comment[th]=กำหนดแบบอักษรของระบบ Comment[th_TH]=กำหนดแบบอักษรของระบบ Comment[tr]=Sistem yazı tiplerini yapılandır Comment[uk]=Налаштування системних шрифтів Comment[uk_UA]=Налаштування системних шрифтів Comment[ur]=نظام کے فونٹ کو تشکیل دیں Comment[ur_PK]=نظام کے فونٹ کو تشکیل دیں Comment[vi]=Cấu hình kiểu chữ hệ thống Comment[vi_VN]=Cấu hình kiểu chữ hệ thống Comment[zh_CN]=配置系统字体 Comment[zh_HK]=配置系統字型 Comment[zh_TW]=設定系統字型組態 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-tiling.desktop0000664000175000017500000002107412625666430026255 0ustar fabiofabio[Desktop Entry] Icon=cs-tiling Exec=cinnamon-settings tiling Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Window Tiling Name[bg]=Показвай придвижването Name[bg_BG]=Показвай придвижването Name[ca]=Ajust de finestres Name[ca@valencia]=Ajust de finestres Name[ca_ES@valencia]=Ajust de finestres Name[cs]=Dláždění oken Name[cs_CZ]=Dláždění oken Name[cy]=Teilsio Ffenestri Name[da]=Side om side-udlægning Name[da_DK]=Side om side-udlægning Name[de]=Fensterkacheln Name[de_DE]=Fensterkacheln Name[es]=Ajuste de ventanas Name[es_AR]=Ajuste de ventanas Name[es_CL]=Ajuste de ventanas Name[es_CO]=Ajuste de ventanas Name[es_CR]=Ajuste de ventanas Name[es_DO]=Ajuste de ventanas Name[es_EC]=Ajuste de ventanas Name[es_ES]=Ajuste de ventanas Name[es_GT]=Ajuste de ventanas Name[es_HN]=Ajuste de ventanas Name[es_MX]=Ajuste de ventanas Name[es_NI]=Ajuste de ventanas Name[es_PA]=Ajuste de ventanas Name[es_PE]=Ajuste de ventanas Name[es_PR]=Ajuste de ventanas Name[es_SV]=Ajuste de ventanas Name[es_UY]=Ajuste de ventanas Name[es_VE]=Ajuste de ventanas Name[eu]=Leihoa lauzatzea Name[eu_ES]=Leihoa lauzatzea Name[fi]=Ikkunoiden ruudutus Name[fi_FI]=Ikkunoiden ruudutus Name[fr]=Alignement de fenêtres Name[fr_CA]=Alignement de fenêtres Name[fr_FR]=Alignement de fenêtres Name[gd]=Leacadh nan uinneagan Name[gl]=Xanelas en mosaico Name[gl_ES]=Xanelas en mosaico Name[hr]=Slaganje prozora Name[hu]=Ablak mozaik Name[hu_HU]=Ablak mozaik Name[is]=Flísalagning glugga Name[it]=Affiancamento finestre Name[ko]=창 타일링 Name[ko_KR]=창 타일링 Name[lt]=Langų išdėliojimas Name[lt_LT]=Langų išdėliojimas Name[nb]=Vindusorganisering Name[nb_NO]=Vindusorganisering Name[nl]=Tegelen van vensters Name[nl_NL]=Tegelen van vensters Name[pl]=Ułożenie Okna Name[pl_PL]=Ułożenie Okna Name[pt]=Tiling de Janelas Name[pt_BR]=Revestimento da janela Name[pt_PT]=Tiling de Janelas Name[ro]=Tiling fereastră Name[ro_RO]=Tiling fereastră Name[ru]=Оконная кладка Name[sk]=Dláždenie okien Name[sk_SK]=Dláždenie okien Name[sv]=Ordna Fönster Name[sv_SE]=Ordna Fönster Name[tr]=Pencere Döşeme Name[uk]=Віконна кладка Name[uk_UA]=Віконна кладка Name[ur]=ونڈو ٹائلنگ Name[ur_PK]=ونڈو ٹائلنگ Name[vi]=Lát Cửa sổ Name[vi_VN]=Lát Cửa sổ Name[zh_CN]=窗口平铺 Name[zh_HK]=視窗平舖 Name[zh_TW]=視窗平舖 Comment=Manage window tiling preferences Comment[am]=የ መስኮት መደርደሪያ ምርጫዎች አስተዳዳሪ Comment[be]=Кіраванне параметрамі мазаічнага размяшчэння вокнаў Comment[be@latin]=Кіраванне параметрамі мазаічнага размяшчэння вокнаў Comment[bg]=Управление подредбата на прозорците Comment[bg_BG]=Управление подредбата на прозорците Comment[bs]=Upravljaj postavkama pločastog prikaza prozora Comment[ca]=Administrar les preferències de mosaic de finestres Comment[ca@valencia]=Administra les preferències de mosaic de finestres Comment[ca_ES@valencia]=Administra les preferències de mosaic de finestres Comment[cs]=Nastavení dláždění oken Comment[cs_CZ]=Nastavení dláždění oken Comment[cy]=Rheoli dewisiadau teilsio ffenestri Comment[da]=Administrér indstillinger for side om side-udlægning Comment[da_DK]=Administrér indstillinger for side om side-udlægning Comment[de]=Kacheleinstellungen der Fenster verwalten Comment[de_DE]=Kacheleinstellungen der Fenster verwalten Comment[el]=Διαχείριση προτιμήσεων τοποθέτησης παραθύρων ως πλακίδια Comment[es]=Administrar las preferencias de ajuste de ventanas Comment[es_AR]=Administrar las preferencias de ajuste de ventanas Comment[es_CL]=Administrar las preferencias de ajuste de ventanas Comment[es_CO]=Administrar las preferencias de ajuste de ventanas Comment[es_CR]=Administrar las preferencias de ajuste de ventanas Comment[es_DO]=Administrar las preferencias de ajuste de ventanas Comment[es_EC]=Administrar las preferencias de ajuste de ventanas Comment[es_ES]=Administrar las preferencias de ajuste de ventanas Comment[es_GT]=Administrar las preferencias de ajuste de ventanas Comment[es_HN]=Administrar las preferencias de ajuste de ventanas Comment[es_MX]=Administrar las preferencias de ajuste de ventanas Comment[es_NI]=Administrar las preferencias de ajuste de ventanas Comment[es_PA]=Administrar las preferencias de ajuste de ventanas Comment[es_PE]=Administrar las preferencias de ajuste de ventanas Comment[es_PR]=Administrar las preferencias de ajuste de ventanas Comment[es_SV]=Administrar las preferencias de ajuste de ventanas Comment[es_UY]=Administrar las preferencias de ajuste de ventanas Comment[es_VE]=Administrar las preferencias de ajuste de ventanas Comment[eu]=Kudeatu leihoak lauzatzeko hobespenak Comment[eu_ES]=Kudeatu leihoak lauzatzeko hobespenak Comment[fi]=Muokkaa ikkunoiden ruudutuksen asetuksia Comment[fi_FI]=Muokkaa ikkunoiden ruudutuksen asetuksia Comment[fr]=Gérer les préférences de juxtaposition des fenêtres Comment[fr_CA]=Gérer les préférences de juxtaposition des fenêtres Comment[fr_FR]=Gérer les préférences de juxtaposition des fenêtres Comment[gd]=Stiùirich roghainnean leacadh nan uinneagan Comment[gl]=Administra as preferencias do mosaixo de xanelas Comment[gl_ES]=Administra as preferencias do mosaixo de xanelas Comment[hr]=Upravljajte osobitostima slaganja prozora Comment[hu]=Ablak mozaik beállítások kezelése Comment[hu_HU]=Ablak mozaik beállítások kezelése Comment[id]=Atur preferensi pengubinan jendela Comment[id_ID]=Atur preferensi pengubinan jendela Comment[is]=Sýsla með kjörstillingar fyrir uppröðun glugga Comment[it]=Gestisci le preferenze di affiancamento delle finestre Comment[ko]=창 타일링 설정 관리하기 Comment[ko_KR]=창 타일링 설정 관리하기 Comment[lt]=Tvarkyti langų išdėliojimo nuostatas Comment[lt_LT]=Tvarkyti langų išdėliojimo nuostatas Comment[ms]=Mengurus pilihan jubin tetingkap Comment[nb]=Håndter innstillinger for vindusorganisering Comment[nb_NO]=Håndter innstillinger for vindusorganisering Comment[nds]=Fensteranordnungseinstellungen verwalten Comment[nl]=Voorkeuren beheren voor het tegelen van vensters Comment[nl_NL]=Voorkeuren beheren voor het tegelen van vensters Comment[pl]=Zarządzaj preferencjami kafelkowania okien Comment[pl_PL]=Zarządzaj preferencjami kafelkowania okien Comment[pt]=Gerir as definições de arrumação de janelas Comment[pt_BR]=Gerenciar as definições de disposição de janelas Comment[pt_PT]=Gerir as definições de arrumação de janelas Comment[ro]=Gestionează preferințele pentru tilingul ferestrelor Comment[ro_RO]=Gestionează preferințele pentru tilingul ferestrelor Comment[ru]=Управление параметрами мозаичного размещения окон Comment[sk]=Spravovať predvoľby dláždenia okien Comment[sk_SK]=Spravovať predvoľby dláždenia okien Comment[sl]=Upravljajte z možnostmi razpostavljanja oken Comment[sl_SI]=Upravljajte z možnostmi razpostavljanja oken Comment[sr]=Управљање поставкама поплочавања прозора Comment[sr@Latn]=Управљање поставкама поплочавања прозора Comment[sr@ije]=Управљање поставкама поплочавања прозора Comment[sr@ijekavianlatin]=Управљање поставкама поплочавања прозора Comment[sr@latin]=Управљање поставкама поплочавања прозора Comment[sr_RS]=Управљање поставкама поплочавања прозора Comment[sv]=Hantera inställningar för radning av fönster Comment[sv_SE]=Hantera inställningar för radning av fönster Comment[tg]=Идоракунии хусусиятҳои лавҳабандии равзанаҳо Comment[th]=จัดการการตั้งค่าการจัดหน้าต่างแบบกระเบื้องขั้นสูง Comment[th_TH]=จัดการการตั้งค่าการจัดหน้าต่างแบบกระเบื้องขั้นสูง Comment[tr]=Pencere döşeme seçeneklerini yönet Comment[uk]=Керування прилипанням вікон Comment[uk_UA]=Керування прилипанням вікон Comment[vi]=Quản lý tuỳ thích lát cửa sổ Comment[vi_VN]=Quản lý tuỳ thích lát cửa sổ Comment[zh_CN]=管理窗口平铺样式 Comment[zh_HK]=管理視窗平舖偏好設定 Comment[zh_TW]=管理視窗平舖偏好設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-themes.desktop0000664000175000017500000002550712625666430026261 0ustar fabiofabio[Desktop Entry] Icon=cs-themes Exec=cinnamon-settings themes Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Themes Name[af]=Temas Name[am]=ገፅታዎች Name[ar]=السِمات Name[ast]=Temes Name[be]=Тэмы Name[be@latin]=Тэмы Name[bg]=Теми Name[bg_BG]=Теми Name[bn]=থিমসমূহ Name[bn_BD]=থিমসমূহ Name[bn_IN]=থিমসমূহ Name[bs]=Teme Name[ca]=Temes Name[ca@valencia]=Temes Name[ca_ES@valencia]=Temes Name[cs]=Motivy Name[cs_CZ]=Motivy Name[csb]=Mòtiwë Name[cy]=Themâu Name[da]=Temaer Name[da_DK]=Temaer Name[de]=Themen Name[de_DE]=Themen Name[el]=Θέματα Name[eo]=Etosoj Name[es]=Temas Name[es_AR]=Temas Name[es_CL]=Temas Name[es_CO]=Temas Name[es_CR]=Temas Name[es_DO]=Temas Name[es_EC]=Temas Name[es_ES]=Temas Name[es_GT]=Temas Name[es_HN]=Temas Name[es_MX]=Temas Name[es_NI]=Temas Name[es_PA]=Temas Name[es_PE]=Temas Name[es_PR]=Temas Name[es_SV]=Temas Name[es_UY]=Temas Name[es_VE]=Temas Name[et]=Teemad Name[et_EE]=Teemad Name[eu]=Gaiak Name[eu_ES]=Gaiak Name[fa]=قالب‌ها Name[fa_AF]=قالب‌ها Name[fa_IR]=قالب‌ها Name[fi]=Teemat Name[fi_FI]=Teemat Name[fil]=Mga Tema Name[fr]=Thèmes Name[fr_CA]=Thèmes Name[fr_FR]=Thèmes Name[fy]=Tema's Name[ga]=Téamaí Name[gd]=Ùrlaran Name[gl]=Temas Name[gl_ES]=Temas Name[he]=ערכות Name[he_IL]=ערכות Name[hi]=थीम्स Name[hr]=Teme Name[hu]=Témák Name[hu_HU]=Témák Name[hy]=Թեմաներ Name[id]=Tema Name[id_ID]=Tema Name[is]=Þemu Name[it]=Temi Name[ja]=テーマ Name[ja_JP]=テーマ Name[km]=ស្បែក Name[km_KH]=ស្បែក Name[ko]=테마 Name[ko_KR]=테마 Name[li]=Thema's Name[lo]=ລັກສະນະ Name[lt]=Stiliai Name[lt_LT]=Stiliai Name[lv]=Motīvi Name[lv_LV]=Motīvi Name[ml]=രംഗവിധാനങ്ങൾ Name[ml_IN]=രംഗവിധാനങ്ങൾ Name[mr]=थीम्स Name[ms]=Tema Name[nb]=Temaer Name[nb_NO]=Temaer Name[nds]=Themen Name[ne]=थेमहरु Name[nl]=Thema's Name[nl_NL]=Thema's Name[nn]=Drakter Name[nn_NO]=Drakter Name[oc]=Tèmas Name[om]=Bifoota Name[pa]=ਥੀਮ Name[pl]=Motywy Name[pl_PL]=Motywy Name[pt]=Temas Name[pt_BR]=Temas Name[pt_PT]=Temas Name[ro]=Teme Name[ro_RO]=Teme Name[ru]=Темы Name[rue]=Теми Name[si]=තේමාවන් Name[sk]=Motívy vzhľadu Name[sk_SK]=Motívy vzhľadu Name[sl]=Teme Name[sl_SI]=Teme Name[so]=Siimyadda Name[sq]=Temat Name[sr]=Теме Name[sr@Latn]=Teme Name[sr@ije]=Теме Name[sr@ijekavianlatin]=Теме Name[sr@latin]=Teme Name[sr_RS]=Теме Name[sv]=Teman Name[sv_SE]=Teman Name[ta]=தள வடிவங்கள் Name[ta_LK]=தள வடிவங்கள் Name[te]=అలంకారాలు Name[tg]=Мавзӯҳо Name[th]=ชุดตกแต่ง Name[th_TH]=ชุดตกแต่ง Name[tl]=Mga Tema Name[tl_PH]=Mga Tema Name[tr]=Temalar Name[ts]=Nkongomelo Name[uk]=Теми Name[uk_UA]=Теми Name[ur]=تھیمز Name[ur_PK]=تھیمز Name[uz]=Ташқи кўринишлар Name[uz@Latn]=Ташқи кўринишлар Name[uz@cyrillic]=Ташқи кўринишлар Name[vi]=Giao diện Name[vi_VN]=Giao diện Name[zh_CN]=主题 Name[zh_HK]=佈景主題 Name[zh_TW]=佈景主題 Comment=Manage themes to change how your desktop looks Comment[am]=የ እርስዎ ዴስክቶፕ ገጽታ ምን እንደሚመስል መቆጣጠሪያ Comment[ar]=إدارة السمات اتغيير مظهر سطح المكتب Comment[ast]=Alministra los temes y camuda l'aspeutu del to escritoriu Comment[be]=Кіраванне тэмамі знешняга выгляду працоўнага стала Comment[be@latin]=Кіраванне тэмамі знешняга выгляду працоўнага стала Comment[bg]=Управлявайте темите, за да промените вида на вашия десктоп Comment[bg_BG]=Управлявайте темите, за да промените вида на вашия десктоп Comment[bs]=Upravljaj temama da promijenite kako vaš desktop izgleda Comment[ca]=Administrar temes per canviar l'aparença de l'escriptori Comment[ca@valencia]=Administra els temes per a canviar l'aspecte de l'escriptori Comment[ca_ES@valencia]=Administra els temes per a canviar l'aspecte de l'escriptori Comment[cs]=Nastavení témat, která mění vzhled vašeho prostředí Comment[cs_CZ]=Nastavení témat, která mění vzhled vašeho prostředí Comment[cy]=Rheoli themâu er mwyn newid sut olwg sydd ar eich sgrin Comment[da]=Administrér temaer for at ændre, hvordan dit skrivebord ser ud Comment[da_DK]=Administrér temaer for at ændre, hvordan dit skrivebord ser ud Comment[de]=Themen verwalten, um das Aussehen des Schreibtischs zu verändern Comment[de_DE]=Themen verwalten, um das Aussehen des Schreibtischs zu verändern Comment[el]=Ρυθμίστε τα θέματα και αλλάξτε την εμφάνιση του υπολογιστή Comment[es]=Administrar temas para cambiar el aspecto del escritorio Comment[es_AR]=Administrar temas para cambiar el aspecto del escritorio Comment[es_CL]=Administrar temas para cambiar el aspecto del escritorio Comment[es_CO]=Administrar temas para cambiar el aspecto del escritorio Comment[es_CR]=Administrar temas para cambiar el aspecto del escritorio Comment[es_DO]=Administrar temas para cambiar el aspecto del escritorio Comment[es_EC]=Administrar temas para cambiar el aspecto del escritorio Comment[es_ES]=Administrar temas para cambiar el aspecto del escritorio Comment[es_GT]=Administrar temas para cambiar el aspecto del escritorio Comment[es_HN]=Administrar temas para cambiar el aspecto del escritorio Comment[es_MX]=Administrar temas para cambiar el aspecto del escritorio Comment[es_NI]=Administrar temas para cambiar el aspecto del escritorio Comment[es_PA]=Administrar temas para cambiar el aspecto del escritorio Comment[es_PE]=Administrar temas para cambiar el aspecto del escritorio Comment[es_PR]=Administrar temas para cambiar el aspecto del escritorio Comment[es_SV]=Administrar temas para cambiar el aspecto del escritorio Comment[es_UY]=Administrar temas para cambiar el aspecto del escritorio Comment[es_VE]=Administrar temas para cambiar el aspecto del escritorio Comment[eu]=Kudeatu gaiak zure mahaigainaren itxura aldatzeko Comment[eu_ES]=Kudeatu gaiak zure mahaigainaren itxura aldatzeko Comment[fi]=Muuta työpöydän ulkonäköä teemojen avulla Comment[fi_FI]=Muuta työpöydän ulkonäköä teemojen avulla Comment[fr]=Gérer les thèmes pour changer l'apparence du bureau Comment[fr_CA]=Gérer les thèmes pour changer l'apparence du bureau Comment[fr_FR]=Gérer les thèmes pour changer l'apparence du bureau Comment[gd]=Stiùirich ùrlaran gus an coltas a tha air an deasg agad atharrachadh Comment[gl]=Administra os temas do escritorio Comment[gl_ES]=Administra os temas do escritorio Comment[hr]=Upravljajte temama za promjenu izgleda vaše radne površine Comment[hu]=Témák kezelése az asztal kinézetének megváltoztatásához Comment[hu_HU]=Témák kezelése az asztal kinézetének megváltoztatásához Comment[id]=Mengatur tema untuk mengubah tampilan desktop anda Comment[id_ID]=Mengatur tema untuk mengubah tampilan desktop anda Comment[is]=Sýsla með þemu til að breyta útliti skjáborðsins Comment[it]=Gestisci i temi per cambiare l'aspetto dek desktop Comment[ko]=테마 관리는 여러분의 컴퓨터를 달라 보이게 할 겁니다 Comment[ko_KR]=테마 관리는 여러분의 컴퓨터를 달라 보이게 할 겁니다 Comment[lt]=Tvarkykite stilius darbalaukio vaizdui pakeisti Comment[lt_LT]=Tvarkykite stilius darbalaukio vaizdui pakeisti Comment[lv]=Pārvaldīt motīvus, lai mainītu jūsu darbvirsmas izskatu Comment[lv_LV]=Pārvaldīt motīvus, lai mainītu jūsu darbvirsmas izskatu Comment[ms]=Mengurus tema untuk tukar bagaimana rupa desktop anda Comment[nds]=Themen verwalten, um den Desktop auf Ihre Bedürfnisse einzurichten Comment[nl]=Thema's beheren om te veranderen hoe uw bureaublad eruit ziet Comment[nl_NL]=Thema's beheren om te veranderen hoe uw bureaublad eruit ziet Comment[pl]=Zarządzaj motywami, aby zmienić wygląd pulpitu Comment[pl_PL]=Zarządzaj motywami, aby zmienić wygląd pulpitu Comment[pt]=Gerir temas para mudar como o seu ambiente de trabalho aparenta Comment[pt_BR]=Gerenciar temas para mudar aparência em sua área de trabalho Comment[pt_PT]=Gerir temas para mudar como o seu ambiente de trabalho aparenta Comment[ro]=Gestionează temele pentru a schimba aspectul desktopului Comment[ro_RO]=Gestionează temele pentru a schimba aspectul desktopului Comment[ru]=Управление темами внешнего вида рабочего стола Comment[sk]=Spravovať témy na úpravu výzoru prostredia Comment[sk_SK]=Spravovať témy na úpravu výzoru prostredia Comment[sl]=Upravljajte s temami za spreminjanje izgleda vašega namizja Comment[sl_SI]=Upravljajte s temami za spreminjanje izgleda vašega namizja Comment[sq]=Menaxhoni temat për të ndryshuar si duket desktopi juaj Comment[sr]=Управљање темама ради измене изгледа радног простора Comment[sr@Latn]=Управљање темама ради измене изгледа радног простора Comment[sr@ije]=Управљање темама ради измене изгледа радног простора Comment[sr@ijekavianlatin]=Управљање темама ради измене изгледа радног простора Comment[sr@latin]=Управљање темама ради измене изгледа радног простора Comment[sr_RS]=Управљање темама ради измене изгледа радног простора Comment[sv]=Hantera teman för att ändra hur ditt skrivbord ser ut Comment[sv_SE]=Hantera teman för att ändra hur ditt skrivbord ser ut Comment[tg]=Идоракунии мавзӯҳо ва намуди зоҳирии мизи корӣ Comment[th]=จัดการชุดตกแต่งเพื่อเปลี่ยนว่าจะให้พื้นโต๊ะของคุณมีมุมมองอย่างไรบ้าง Comment[th_TH]=จัดการชุดตกแต่งเพื่อเปลี่ยนว่าจะให้พื้นโต๊ะของคุณมีมุมมองอย่างไรบ้าง Comment[tr]=Masaüstü görüntünüzü özelleştirmek için temaları yönetin Comment[uk]=Керуйте темами, щоб змінити вигляд Вашої стільниці Comment[uk_UA]=Керуйте темами, щоб змінити вигляд Вашої стільниці Comment[vi]=Quản lý chủ đề để thay đổi dáng vẻ màn hình làm việc Comment[vi_VN]=Quản lý chủ đề để thay đổi dáng vẻ màn hình làm việc Comment[zh_CN]=管理主体以改变您桌面的外貌 Comment[zh_HK]=管理佈景主題以變更你的桌面外觀 Comment[zh_TW]=管理佈景主題以變更您的桌面外觀 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-default.desktop0000664000175000017500000002063712625666430026417 0ustar fabiofabio[Desktop Entry] Icon=cs-default-applications Exec=cinnamon-settings default Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Preferred Applications Name[am]=የመረጥኩዋቸው መተግበሪያዎች Name[ar]=التطبيقات المفضّلة Name[ast]=Aplicaciones preferíes Name[be]=Пераважныя дастасаванні Name[be@latin]=Пераважныя дастасаванні Name[bg]=Предпочитани програми Name[bg_BG]=Предпочитани програми Name[bs]=Preferirane aplikacije Name[ca]=Aplicacions preferides Name[ca@valencia]=Aplicacions preferides Name[ca_ES@valencia]=Aplicacions preferides Name[cs]=Preferované aplikace Name[cs_CZ]=Preferované aplikace Name[cy]=Rhaglenni Dewisol Name[da]=Foretrukne programmer Name[da_DK]=Foretrukne programmer Name[de]=Bevorzugte Anwendungen Name[de_DE]=Bevorzugte Anwendungen Name[el]=Προτιμώμενες Εφαρμογές Name[es]=Aplicaciones preferidas Name[es_AR]=Aplicaciones preferidas Name[es_CL]=Aplicaciones preferidas Name[es_CO]=Aplicaciones preferidas Name[es_CR]=Aplicaciones preferidas Name[es_DO]=Aplicaciones preferidas Name[es_EC]=Aplicaciones preferidas Name[es_ES]=Aplicaciones preferidas Name[es_GT]=Aplicaciones preferidas Name[es_HN]=Aplicaciones preferidas Name[es_MX]=Aplicaciones preferidas Name[es_NI]=Aplicaciones preferidas Name[es_PA]=Aplicaciones preferidas Name[es_PE]=Aplicaciones preferidas Name[es_PR]=Aplicaciones preferidas Name[es_SV]=Aplicaciones preferidas Name[es_UY]=Aplicaciones preferidas Name[es_VE]=Aplicaciones preferidas Name[et]=Eelistatud rakendused Name[et_EE]=Eelistatud rakendused Name[eu]=Hobetsitako aplikazioak Name[eu_ES]=Hobetsitako aplikazioak Name[fi]=Oletussovellukset Name[fi_FI]=Oletussovellukset Name[fr]=Applications préférées Name[fr_CA]=Applications préférées Name[fr_FR]=Applications préférées Name[gd]=Aplacaidean as fhearr leat Name[gl]=Aplicativos preferidos Name[gl_ES]=Aplicativos preferidos Name[hr]=Željene aplikacije Name[hu]=Alapértelmezett alkalmazások Name[hu_HU]=Alapértelmezett alkalmazások Name[id]=Aplikasi Pilihan Name[id_ID]=Aplikasi Pilihan Name[is]=Sjálfgefin forrit Name[it]=Applicazioni preferite Name[ja]=お気に入りのアプリケーション Name[ja_JP]=お気に入りのアプリケーション Name[ko]=기본프로그램 Name[ko_KR]=기본프로그램 Name[lo]=ໂປຣແກຣມທີ່ເລືອກ Name[lt]=Pageidaujamos programos Name[lt_LT]=Pageidaujamos programos Name[lv]=Noklusētās programmas Name[lv_LV]=Noklusētās programmas Name[ms]=Aplikasi Digemari Name[nb]=Foretrukne programmer Name[nb_NO]=Foretrukne programmer Name[nds]=Bevorzugte Anwendungen Name[nl]=Voorkeurstoepassingen Name[nl_NL]=Voorkeurstoepassingen Name[pl]=Preferowane programy Name[pl_PL]=Preferowane programy Name[pt]=Aplicações Preferidas Name[pt_BR]=Aplicativos preferenciais Name[pt_PT]=Aplicações Preferidas Name[ro]=Aplicații preferate Name[ro_RO]=Aplicații preferate Name[ru]=Предпочитаемые приложения Name[sk]=Predvolené aplikácie Name[sk_SK]=Predvolené aplikácie Name[sl]=Prednostni programi Name[sl_SI]=Prednostni programi Name[sq]=Programet e preferuar Name[sr]=Омиљени програми Name[sr@Latn]=Омиљени програми Name[sr@ije]=Омиљени програми Name[sr@ijekavianlatin]=Омиљени програми Name[sr@latin]=Омиљени програми Name[sr_RS]=Омиљени програми Name[sv]=Förvalda program Name[sv_SE]=Förvalda program Name[th]=โปรแกรมที่ชอบ Name[th_TH]=โปรแกรมที่ชอบ Name[tr]=Tercih Edilen Uygulamalar Name[uk]=Стандартні програми Name[uk_UA]=Стандартні програми Name[ur]=ترجیحی ایپلی کیشنز Name[ur_PK]=ترجیحی ایپلی کیشنز Name[uz]=Танланган дастурлар Name[uz@Latn]=Танланган дастурлар Name[uz@cyrillic]=Танланган дастурлар Name[vi]=Ứng dụng Ưa thích Name[vi_VN]=Ứng dụng Ưa thích Name[zh_CN]=首选应用程序 Name[zh_HK]=偏好的應用程式 Name[zh_TW]=偏好的應用程式 Comment=Preferred Applications Comment[am]=የመረጥኩዋቸው መተግበሪያዎች Comment[ar]=التطبيقات المفضّلة Comment[ast]=Aplicaciones preferíes Comment[be]=Пераважныя дастасаванні Comment[be@latin]=Пераважныя дастасаванні Comment[bg]=Предпочитани програми Comment[bg_BG]=Предпочитани програми Comment[bs]=Preferirane aplikacije Comment[ca]=Aplicacions preferides Comment[ca@valencia]=Aplicacions preferides Comment[ca_ES@valencia]=Aplicacions preferides Comment[cs]=Preferované aplikace Comment[cs_CZ]=Preferované aplikace Comment[cy]=Rhaglenni Dewisol Comment[da]=Foretrukne programmer Comment[da_DK]=Foretrukne programmer Comment[de]=Bevorzugte Anwendungen Comment[de_DE]=Bevorzugte Anwendungen Comment[el]=Προτιμώμενες Εφαρμογές Comment[es]=Aplicaciones preferidas Comment[es_AR]=Aplicaciones preferidas Comment[es_CL]=Aplicaciones preferidas Comment[es_CO]=Aplicaciones preferidas Comment[es_CR]=Aplicaciones preferidas Comment[es_DO]=Aplicaciones preferidas Comment[es_EC]=Aplicaciones preferidas Comment[es_ES]=Aplicaciones preferidas Comment[es_GT]=Aplicaciones preferidas Comment[es_HN]=Aplicaciones preferidas Comment[es_MX]=Aplicaciones preferidas Comment[es_NI]=Aplicaciones preferidas Comment[es_PA]=Aplicaciones preferidas Comment[es_PE]=Aplicaciones preferidas Comment[es_PR]=Aplicaciones preferidas Comment[es_SV]=Aplicaciones preferidas Comment[es_UY]=Aplicaciones preferidas Comment[es_VE]=Aplicaciones preferidas Comment[et]=Eelistatud rakendused Comment[et_EE]=Eelistatud rakendused Comment[eu]=Hobetsitako aplikazioak Comment[eu_ES]=Hobetsitako aplikazioak Comment[fi]=Oletussovellukset Comment[fi_FI]=Oletussovellukset Comment[fr]=Applications préférées Comment[fr_CA]=Applications préférées Comment[fr_FR]=Applications préférées Comment[gd]=Aplacaidean as fhearr leat Comment[gl]=Aplicativos preferidos Comment[gl_ES]=Aplicativos preferidos Comment[hr]=Željene aplikacije Comment[hu]=Alapértelmezett alkalmazások Comment[hu_HU]=Alapértelmezett alkalmazások Comment[id]=Aplikasi Pilihan Comment[id_ID]=Aplikasi Pilihan Comment[is]=Sjálfgefin forrit Comment[it]=Applicazioni preferite Comment[ja]=お気に入りのアプリケーション Comment[ja_JP]=お気に入りのアプリケーション Comment[ko]=기본프로그램 Comment[ko_KR]=기본프로그램 Comment[lo]=ໂປຣແກຣມທີ່ເລືອກ Comment[lt]=Pageidaujamos programos Comment[lt_LT]=Pageidaujamos programos Comment[lv]=Noklusētās programmas Comment[lv_LV]=Noklusētās programmas Comment[ms]=Aplikasi Digemari Comment[nb]=Foretrukne programmer Comment[nb_NO]=Foretrukne programmer Comment[nds]=Bevorzugte Anwendungen Comment[nl]=Voorkeurstoepassingen Comment[nl_NL]=Voorkeurstoepassingen Comment[pl]=Preferowane programy Comment[pl_PL]=Preferowane programy Comment[pt]=Aplicações Preferidas Comment[pt_BR]=Aplicativos preferenciais Comment[pt_PT]=Aplicações Preferidas Comment[ro]=Aplicații preferate Comment[ro_RO]=Aplicații preferate Comment[ru]=Предпочитаемые приложения Comment[sk]=Predvolené aplikácie Comment[sk_SK]=Predvolené aplikácie Comment[sl]=Prednostni programi Comment[sl_SI]=Prednostni programi Comment[sq]=Programet e preferuar Comment[sr]=Омиљени програми Comment[sr@Latn]=Омиљени програми Comment[sr@ije]=Омиљени програми Comment[sr@ijekavianlatin]=Омиљени програми Comment[sr@latin]=Омиљени програми Comment[sr_RS]=Омиљени програми Comment[sv]=Förvalda program Comment[sv_SE]=Förvalda program Comment[th]=โปรแกรมที่ชอบ Comment[th_TH]=โปรแกรมที่ชอบ Comment[tr]=Tercih Edilen Uygulamalar Comment[uk]=Стандартні програми Comment[uk_UA]=Стандартні програми Comment[ur]=ترجیحی ایپلی کیشنز Comment[ur_PK]=ترجیحی ایپلی کیشنز Comment[uz]=Танланган дастурлар Comment[uz@Latn]=Танланган дастурлар Comment[uz@cyrillic]=Танланган дастурлар Comment[vi]=Ứng dụng Ưa thích Comment[vi_VN]=Ứng dụng Ưa thích Comment[zh_CN]=首选应用程序 Comment[zh_HK]=偏好的應用程式 Comment[zh_TW]=偏好的應用程式 Cinnamon-2.8.6/files/usr/share/applications/cinnamon2d.desktop0000664000175000017500000000055312625666430023360 0ustar fabiofabio[Desktop Entry] Type=Application Name=Cinnamon 2D Comment=Window management and application launching Exec=/usr/bin/cinnamon2d Categories=GNOME;GTK;System;Core; OnlyShowIn=X-Cinnamon; NoDisplay=true X-GNOME-Autostart-Phase=WindowManager X-GNOME-Provides=panel;windowmanager; X-GNOME-Autostart-Notify=true X-GNOME-AutoRestart=true X-Ubuntu-Gettext-Domain=cinnamon Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-calendar.desktop0000664000175000017500000002301112625666430026531 0ustar fabiofabio[Desktop Entry] Icon=cs-date-time Exec=cinnamon-settings calendar Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Date & Time Name[am]=ቀን & ሰአት Name[ar]=التاريخ والوقت Name[ast]=Data y hora Name[be]=Дата і час Name[be@latin]=Дата і час Name[bg]=Дата и час Name[bg_BG]=Дата и час Name[bs]=Datum i vrijeme Name[ca]=Data i hora Name[ca@valencia]=Data i hora Name[ca_ES@valencia]=Data i hora Name[cs]=Datum a čas Name[cs_CZ]=Datum a čas Name[cy]=Dyddiad ac Amser Name[da]=Dato & tid Name[da_DK]=Dato & tid Name[de]=Datum & Zeit Name[de_DE]=Datum & Zeit Name[el]=Ημερομηνία & Ώρα Name[eo]=Dato kaj tempo Name[es]=Fecha y hora Name[es_AR]=Fecha y hora Name[es_CL]=Fecha y hora Name[es_CO]=Fecha y hora Name[es_CR]=Fecha y hora Name[es_DO]=Fecha y hora Name[es_EC]=Fecha y hora Name[es_ES]=Fecha y hora Name[es_GT]=Fecha y hora Name[es_HN]=Fecha y hora Name[es_MX]=Fecha y hora Name[es_NI]=Fecha y hora Name[es_PA]=Fecha y hora Name[es_PE]=Fecha y hora Name[es_PR]=Fecha y hora Name[es_SV]=Fecha y hora Name[es_UY]=Fecha y hora Name[es_VE]=Fecha y hora Name[et]=Kuupäev & kellaaeg Name[et_EE]=Kuupäev & kellaaeg Name[eu]=Data eta ordua Name[eu_ES]=Data eta ordua Name[fi]=Päivä ja aika Name[fi_FI]=Päivä ja aika Name[fr]=Date & Heure Name[fr_CA]=Date et heure Name[fr_FR]=Date & Heure Name[fy]=Datum en Tiid Name[gd]=Ceann-là ⁊ Àm Name[gl]=Data e hora Name[gl_ES]=Data e hora Name[hr]=Datum i Vrijeme Name[hu]=Dátum és idő Name[hu_HU]=Dátum és idő Name[id]=Tanggal & Waktu Name[id_ID]=Tanggal & Waktu Name[is]=Dagsetning og tími Name[it]=Data & Ora Name[ja]=日付と時刻 Name[ja_JP]=日付と時刻 Name[ko]=날짜 & 시간 Name[ko_KR]=날짜 & 시간 Name[lo]=ວັນແລະເວລາ Name[lt]=Data ir laikas Name[lt_LT]=Data ir laikas Name[lv]=Datums un laiks Name[lv_LV]=Datums un laiks Name[ms]=Tarikh & Masa Name[nb]=Dato og klokkeslett Name[nb_NO]=Dato og klokkeslett Name[nds]=Datum & Zeit Name[nl]=Datum en tijd Name[nl_NL]=Datum en tijd Name[oc]=Data & ora Name[pa]=ਮਿਤੀ ਤੇ ਸਮਾਂ Name[pl]=Data i godzina Name[pl_PL]=Data i godzina Name[pt]=Data e Hora Name[pt_BR]=Data & Hora Name[pt_PT]=Data e Hora Name[ro]=Dată și oră Name[ro_RO]=Dată și oră Name[ru]=Дата и время Name[sk]=Dátum a čas Name[sk_SK]=Dátum a čas Name[sl]=Datum in čas Name[sl_SI]=Datum in čas Name[sq]=Data dhe Ora Name[sr]=Датум и време Name[sr@Latn]=Датум и време Name[sr@ije]=Датум и време Name[sr@ijekavianlatin]=Датум и време Name[sr@latin]=Датум и време Name[sr_RS]=Датум и време Name[sv]=Datum och tid Name[sv_SE]=Datum och tid Name[ta]=தேதியும் நேரமும் Name[ta_LK]=தேதியும் நேரமும் Name[tg]=Сана ва вақт: Name[th]=วันที่และเวลา Name[th_TH]=วันที่และเวลา Name[tr]=Tarih & Zaman Name[uk]=Дата і час Name[uk_UA]=Дата і час Name[ur]=وقت اور تاريخ Name[ur_PK]=وقت اور تاريخ Name[uz]=Сана & Вақт Name[uz@Latn]=Сана & Вақт Name[uz@cyrillic]=Сана & Вақт Name[vi]=Ngày giờ Name[vi_VN]=Ngày giờ Name[zh_CN]=日期和时间 Name[zh_HK]=日期與時間 Name[zh_TW]=日期與時間 Comment=Manage date and time settings Comment[am]=የ ቀን እና ሰአት ማሰናጃ አስተዳዳሪ Comment[ar]=إدارة إعدادات التاريخ والوقت Comment[ast]=Alministra los axustes de data y tiempu Comment[be]=Кіраванне наладамi даты і часу Comment[be@latin]=Кіраванне наладамi даты і часу Comment[bg]=Управление на часа и датата Comment[bg_BG]=Управление на часа и датата Comment[bs]=Upravljajte postavkama datuma i vremena Comment[ca]=Administrar la configuració de data i hora Comment[ca@valencia]=Administra la configuració de data i hora Comment[ca_ES@valencia]=Administra la configuració de data i hora Comment[cs]=Nastavení data a času Comment[cs_CZ]=Nastavení data a času Comment[cy]=Rheolwch osodiadau dyddiad ac amser Comment[da]=Administrér dato- og tidsindstillinger Comment[da_DK]=Administrér dato- og tidsindstillinger Comment[de]=Datum- und Zeiteinstellungen verwalten Comment[de_DE]=Datum- und Zeiteinstellungen verwalten Comment[el]=Διαχειρίσου τις ρυθμίσεις ημερομηνίας και ώρας Comment[es]=Administrar la configuración de fecha y hora Comment[es_AR]=Administrar la configuración de fecha y hora Comment[es_CL]=Administrar la configuración de fecha y hora Comment[es_CO]=Administrar la configuración de fecha y hora Comment[es_CR]=Administrar la configuración de fecha y hora Comment[es_DO]=Administrar la configuración de fecha y hora Comment[es_EC]=Administrar la configuración de fecha y hora Comment[es_ES]=Administrar la configuración de fecha y hora Comment[es_GT]=Administrar la configuración de fecha y hora Comment[es_HN]=Administrar la configuración de fecha y hora Comment[es_MX]=Administrar la configuración de fecha y hora Comment[es_NI]=Administrar la configuración de fecha y hora Comment[es_PA]=Administrar la configuración de fecha y hora Comment[es_PE]=Administrar la configuración de fecha y hora Comment[es_PR]=Administrar la configuración de fecha y hora Comment[es_SV]=Administrar la configuración de fecha y hora Comment[es_UY]=Administrar la configuración de fecha y hora Comment[es_VE]=Administrar la configuración de fecha y hora Comment[eu]=Kudeatu data eta orduaren ezarpenak Comment[eu_ES]=Kudeatu data eta orduaren ezarpenak Comment[fi]=Muuta päivä- ja aika-asetuksia Comment[fi_FI]=Muuta päivä- ja aika-asetuksia Comment[fr]=Gérer les paramètres de date et d'heure Comment[fr_CA]=Gérer les paramètres de date et d'heure Comment[fr_FR]=Gérer les paramètres de date et d'heure Comment[gd]=Stiùirich roghainnean a' chinn-là 's an ama Comment[gl]=Administrar a configuración de data e hora Comment[gl_ES]=Administrar a configuración de data e hora Comment[hr]=Upravljajte postavkama datuma i vremena Comment[hu]=Dátum és idő beállítások kezelése Comment[hu_HU]=Dátum és idő beállítások kezelése Comment[id]=Atur pengaturan tanggal dan waktu Comment[id_ID]=Atur pengaturan tanggal dan waktu Comment[is]=Sýsla með stillingar fyrir dagsetningu og tíma Comment[it]=Impostazioni data e ora Comment[ja]=日付時間の変更 Comment[ja_JP]=日付時間の変更 Comment[ko]=날짜 및 시간 설정 관리 Comment[ko_KR]=날짜 및 시간 설정 관리 Comment[lo]=ຈັດການການຕັ້ງຄ່າວັນແລະເວລາ Comment[lt]=Keisti datos ir laiko nustatymus Comment[lt_LT]=Keisti datos ir laiko nustatymus Comment[lv]=Pārvaldīt datuma un laika iestatījumus Comment[lv_LV]=Pārvaldīt datuma un laika iestatījumus Comment[ms]=Mengurus tetapan tarikh dan masa Comment[nb]=Behandle innstillinger for dato og tid Comment[nb_NO]=Behandle innstillinger for dato og tid Comment[nds]=Datums- und Zeiteinstellung bearbeiten Comment[nl]=Instellingen voor datum en tijd beheren Comment[nl_NL]=Instellingen voor datum en tijd beheren Comment[pl]=Zarządzaj ustawieniami daty i czasu Comment[pl_PL]=Zarządzaj ustawieniami daty i czasu Comment[pt]=Gerir as definições de data e hora Comment[pt_BR]=Gerenciar configurações de data e hora Comment[pt_PT]=Gerir as definições de data e hora Comment[ro]=Gestionează configurările datei și orei Comment[ro_RO]=Gestionează configurările datei și orei Comment[ru]=Настройки даты и времени Comment[sk]=Spravovať nastavenia dátumu a času Comment[sk_SK]=Spravovať nastavenia dátumu a času Comment[sl]=Upravljajte z nastavitvami datuma in časa Comment[sl_SI]=Upravljajte z nastavitvami datuma in časa Comment[sq]=Menaxho parametrat e datës dhe orës Comment[sr]=Управљање поставкама датума и времена Comment[sr@Latn]=Управљање поставкама датума и времена Comment[sr@ije]=Управљање поставкама датума и времена Comment[sr@ijekavianlatin]=Управљање поставкама датума и времена Comment[sr@latin]=Управљање поставкама датума и времена Comment[sr_RS]=Управљање поставкама датума и времена Comment[sv]=Hantera datum och tidsinställningar Comment[sv_SE]=Hantera datum och tidsinställningar Comment[tg]=Идоракунии танзимоти вақт ва сана Comment[th]=จัดการการตั้งค่าวันและเวลา Comment[th_TH]=จัดการการตั้งค่าวันและเวลา Comment[tr]=Tarih ve zaman ayarlarını yönet Comment[uk]=Керування налаштуваннями дати та часу Comment[uk_UA]=Керування налаштуваннями дати та часу Comment[ur]=وقت اور تاریخ کی ترتیبات کا انتظام کریں Comment[ur_PK]=وقت اور تاریخ کی ترتیبات کا انتظام کریں Comment[uz]=Сана ва вақт созламаларини бошқариш Comment[uz@Latn]=Сана ва вақт созламаларини бошқариш Comment[uz@cyrillic]=Сана ва вақт созламаларини бошқариш Comment[vi]=Quản lý thiết đặt ngày tháng Comment[vi_VN]=Quản lý thiết đặt ngày tháng Comment[zh_CN]=管理时间/日期设置 Comment[zh_HK]=管理日期與時間設定 Comment[zh_TW]=管理日期與時間設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-panel.desktop0000664000175000017500000002013012625666430026056 0ustar fabiofabio[Desktop Entry] Icon=cs-panel Exec=cinnamon-settings panel Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Panel Name[af]=Paneel Name[am]=ክፍል Name[ar]=الشريط Name[be]=Панэль Name[be@latin]=Панэль Name[bg]=Панел Name[bg_BG]=Панел Name[bn]=প্যানেল Name[bn_BD]=প্যানেল Name[bn_IN]=প্যানেল Name[ca]=Tauler Name[ca@valencia]=Panell Name[ca_ES@valencia]=Panell Name[de]=Leiste Name[de_DE]=Leiste Name[el]=Γραμμή εργασιών Name[eo]=Panelo Name[et]=Paneel Name[et_EE]=Paneel Name[eu]=Panela Name[eu_ES]=Panela Name[fa]=صفحه‌ی‌ کنترل Name[fa_AF]=صفحه‌ی‌ کنترل Name[fa_IR]=صفحه‌ی‌ کنترل Name[fi]=Paneeli Name[fi_FI]=Paneeli Name[fr]=Tableau de bord Name[fr_CA]=Tableau de bord Name[fr_FR]=Tableau de bord Name[ga]=Painéal Name[gd]=Panail Name[he]=פאנל Name[he_IL]=פאנל Name[hi]=पट्टिका Name[is]=Skjástika Name[it]=Pannello Name[ja]=パネル Name[ja_JP]=パネル Name[km]=បន្ទះ Name[km_KH]=បន្ទះ Name[ko]=패널 Name[ko_KR]=패널 Name[li]=Paneel Name[lt]=Skydelis Name[lt_LT]=Skydelis Name[lv]=Panelis Name[lv_LV]=Panelis Name[ml]=പാളി Name[ml_IN]=പാളി Name[mr]=पटल Name[nb]=Oppgavelinje Name[nb_NO]=Oppgavelinje Name[nds]=Leiste Name[ne]=प्यानल Name[nl]=Paneel Name[nl_NL]=Paneel Name[oc]=Tablèu de bòrd Name[om]=Moggaargii Name[pa]=ਪੈਨਲ Name[pt]=Painel Name[pt_BR]=Painel Name[pt_PT]=Painel Name[ro]=Panou Name[ro_RO]=Panou Name[ru]=Панель Name[rue]=Панель Name[si]=පැනලය Name[sl]=Pult Name[sl_SI]=Pult Name[so]=Baanelka Name[sq]=Paneli Name[sr]=Полица Name[sr@Latn]=Tabla Name[sr@ije]=Полица Name[sr@ijekavianlatin]=Полица Name[sr@latin]=Tabla Name[sr_RS]=Полица Name[ta]=பலகை Name[ta_LK]=பலகை Name[te]=ప్యానల్ Name[tg]=Лавҳа Name[th]=แผง Name[th_TH]=แผง Name[ts]=Phenele Name[uk]=Панель Name[uk_UA]=Панель Name[ur]=پینل Name[ur_PK]=پینل Name[uz]=Панел Name[uz@Latn]=Панел Name[uz@cyrillic]=Панел Name[vi]=Bảng Name[vi_VN]=Bảng Name[zh_CN]=面板 Name[zh_HK]=面板 Name[zh_TW]=面板 Comment=Manage Cinnamon panel settings Comment[am]=የሲናሞን ክፍል ማሰናጃዎች አስተዳዳሪ Comment[ar]=إدارة خصائص شريط سينامُن Comment[ast]=Axustes del alministrador del panel Cinnamon Comment[be]=Кіраванне наладамі панэлі Cinnamon Comment[be@latin]=Кіраванне наладамі панэлі Cinnamon Comment[bg]=Управление на панела на Cinnamon Comment[bg_BG]=Управление на панела на Cinnamon Comment[bs]=Upravljanje Cinnamon ploča Comment[ca]=Administrar configuració de panell de Cinnamon Comment[ca@valencia]=Administra la configuració del panell de Cinnamon Comment[ca_ES@valencia]=Administra la configuració del panell de Cinnamon Comment[cs]=Správa Cinnamon panelu Comment[cs_CZ]=Správa Cinnamon panelu Comment[cy]=Rheoli gosodiadau panel Cinnamon Comment[da]=Administrér Cinnamon-panelindstillinger Comment[da_DK]=Administrér Cinnamon-panelindstillinger Comment[de]=Cinnamons Leisteneinstellungen verwalten Comment[de_DE]=Cinnamons Leisteneinstellungen verwalten Comment[el]=Διαχείριση ρυθμίσεων γραμμής εργασιών Cinnamon Comment[es]=Administrar configuración del panel de Cinnamon Comment[es_AR]=Administrar configuración del panel de Cinnamon Comment[es_CL]=Administrar configuración del panel de Cinnamon Comment[es_CO]=Administrar configuración del panel de Cinnamon Comment[es_CR]=Administrar configuración del panel de Cinnamon Comment[es_DO]=Administrar configuración del panel de Cinnamon Comment[es_EC]=Administrar configuración del panel de Cinnamon Comment[es_ES]=Administrar configuración del panel de Cinnamon Comment[es_GT]=Administrar configuración del panel de Cinnamon Comment[es_HN]=Administrar configuración del panel de Cinnamon Comment[es_MX]=Administrar configuración del panel de Cinnamon Comment[es_NI]=Administrar configuración del panel de Cinnamon Comment[es_PA]=Administrar configuración del panel de Cinnamon Comment[es_PE]=Administrar configuración del panel de Cinnamon Comment[es_PR]=Administrar configuración del panel de Cinnamon Comment[es_SV]=Administrar configuración del panel de Cinnamon Comment[es_UY]=Administrar configuración del panel de Cinnamon Comment[es_VE]=Administrar configuración del panel de Cinnamon Comment[eu]=Kudeatu Cinnamon panel-ezarpenak Comment[eu_ES]=Kudeatu Cinnamon panel-ezarpenak Comment[fi]=Muokkaa Cinnamonin paneelin asetuksia Comment[fi_FI]=Muokkaa Cinnamonin paneelin asetuksia Comment[fr]=Gérer les paramètres du tableau de bord de Cinnamon Comment[fr_CA]=Gérer les paramètres du tableau de bord de Cinnamon Comment[fr_FR]=Gérer les paramètres du tableau de bord de Cinnamon Comment[gd]=Stiùirich na roghainnean panail aig Cinnamon Comment[gl]=Administra os axustes do panel de Cinnamon Comment[gl_ES]=Administra os axustes do panel de Cinnamon Comment[hr]=Upravljajte postavkama Cinnamon panela Comment[hu]=Cinnamon panel beállítások Comment[hu_HU]=Cinnamon panel beállítások Comment[id]=Atur pengaturan panel Cinnamon Comment[id_ID]=Atur pengaturan panel Cinnamon Comment[is]=Sýsla með stillingar Cinnamon skjástika Comment[it]=Gestisci impostazioni del pannello Cinnamon Comment[ja]=Cinnamon パネル設定管理 Comment[ja_JP]=Cinnamon パネル設定管理 Comment[ko]=시나몬 패널설정 관리 Comment[ko_KR]=시나몬 패널설정 관리 Comment[lt]=Tvarkyti Cinnamon skydelio nustatymus Comment[lt_LT]=Tvarkyti Cinnamon skydelio nustatymus Comment[lv]=Pārvaldīt Cinnamon paneļa iestatījumus Comment[lv_LV]=Pārvaldīt Cinnamon paneļa iestatījumus Comment[ms]=Mengurus tetapan panel Cinnamon Comment[nb]=Håndter innstillinger for oppgavelinjen i Cinnamon Comment[nb_NO]=Håndter innstillinger for oppgavelinjen i Cinnamon Comment[nds]=Cinnamon Panel-Einstellungen verwalten Comment[nl]=Instellingen van Cinnamonwerkbalk beheren Comment[nl_NL]=Instellingen van Cinnamonwerkbalk beheren Comment[pl]=Zarządzaj ustawieniami panelu Cinnamona Comment[pl_PL]=Zarządzaj ustawieniami panelu Cinnamona Comment[pt]=Gerir as definições do painel do Cinnamon Comment[pt_BR]=Gerenciar as configurações do painel do Cinnamon Comment[pt_PT]=Gerir as definições do painel do Cinnamon Comment[ro]=Gestionează configurările panoului Cinnamon Comment[ro_RO]=Gestionează configurările panoului Cinnamon Comment[ru]=Управление настройками панели Cinnamon Comment[sk]=Spravovať nastavenia panelov Cinnamon Comment[sk_SK]=Spravovať nastavenia panelov Cinnamon Comment[sl]=Upravljajte z nastavitvami pulta Cinnamona Comment[sl_SI]=Upravljajte z nastavitvami pulta Cinnamona Comment[sq]=Menaxhoni parametrat e panelit të Cinnamon Comment[sr]=Управљање поставкама полице Цимета Comment[sr@Latn]=Управљање поставкама полице Цимета Comment[sr@ije]=Управљање поставкама полице Цимета Comment[sr@ijekavianlatin]=Управљање поставкама полице Цимета Comment[sr@latin]=Управљање поставкама полице Цимета Comment[sr_RS]=Управљање поставкама полице Цимета Comment[sv]=Hantera Cinnamons panelinställningar Comment[sv_SE]=Hantera Cinnamons panelinställningar Comment[tg]=Идоракунии танзимоти лавҳаи Cinnamon Comment[th]=จัดการการตั้งค่าแผง Cinnamon Comment[th_TH]=จัดการการตั้งค่าแผง Cinnamon Comment[tr]=Cinnamon panel ayarlarını yönet Comment[uk]=Керування налаштуваннями панелі Cinnamon Comment[uk_UA]=Керування налаштуваннями панелі Cinnamon Comment[vi]=Quản lý thiết đặt bảng Cinnamon Comment[vi_VN]=Quản lý thiết đặt bảng Cinnamon Comment[zh_CN]=管理Cinnamon面板设置 Comment[zh_HK]=管理 Cinnamon 面板設定 Comment[zh_TW]=管理 Cinnamon 面板設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-desktop.desktop0000664000175000017500000002020512625666430026433 0ustar fabiofabio[Desktop Entry] Icon=cs-desktop Exec=cinnamon-settings desktop Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Desktop Name[am]=ዴስክቶፕ Name[ar]=سطح المكتب Name[ast]=Escritoriu Name[be]=Працоўны стол Name[be@latin]=Працоўны стол Name[bg]=Работен плот Name[bg_BG]=Работен плот Name[ca]=Escriptori Name[ca@valencia]=Escriptori Name[ca_ES@valencia]=Escriptori Name[cs]=Pracovní plocha Name[cs_CZ]=Pracovní plocha Name[cy]=Bwrdd Gwaith Name[da]=Skrivebord Name[da_DK]=Skrivebord Name[de]=Schreibtisch Name[de_DE]=Schreibtisch Name[el]=Επιφάνεια εργασίας Name[eo]=Labortablo Name[es]=Escritorio Name[es_AR]=Escritorio Name[es_CL]=Escritorio Name[es_CO]=Escritorio Name[es_CR]=Escritorio Name[es_DO]=Escritorio Name[es_EC]=Escritorio Name[es_ES]=Escritorio Name[es_GT]=Escritorio Name[es_HN]=Escritorio Name[es_MX]=Escritorio Name[es_NI]=Escritorio Name[es_PA]=Escritorio Name[es_PE]=Escritorio Name[es_PR]=Escritorio Name[es_SV]=Escritorio Name[es_UY]=Escritorio Name[es_VE]=Escritorio Name[et]=Töölaud Name[et_EE]=Töölaud Name[eu]=Mahaigaina Name[eu_ES]=Mahaigaina Name[fi]=Työpöytä Name[fi_FI]=Työpöytä Name[fil]=Pook ng Likha Name[fr]=Bureau Name[fr_CA]=Bureau Name[fr_FR]=Bureau Name[ga]=Deasc Name[gl]=Escritorio Name[gl_ES]=Escritorio Name[hr]=Radna površina Name[hu]=Asztal Name[hu_HU]=Asztal Name[is]=Skjáborð Name[ja]=デスクトップ Name[ja_JP]=デスクトップ Name[ko]=바탕화면 Name[ko_KR]=바탕화면 Name[lo]=ພື້ນທີ່ໜ້າຈໍ Name[lt]=Darbalaukis Name[lt_LT]=Darbalaukis Name[lv]=Darbvirsma Name[lv_LV]=Darbvirsma Name[nb]=Skrivebord Name[nb_NO]=Skrivebord Name[nl]=Bureaublad Name[nl_NL]=Bureaublad Name[oc]=Burèu Name[pa]=ਡੈਸਕਟਾਪ Name[pl]=Pulpit Name[pl_PL]=Pulpit Name[pt]=Ambiente de trabalho Name[pt_BR]=Área de trabalho Name[pt_PT]=Ambiente de trabalho Name[ru]=Рабочий стол Name[sk]=Pracovná plocha Name[sk_SK]=Pracovná plocha Name[sl]=Namizje Name[sl_SI]=Namizje Name[sq]=Desktopi Name[sr]=Радна површ Name[sr@Latn]=Радна површ Name[sr@ije]=Радна површ Name[sr@ijekavianlatin]=Радна површ Name[sr@latin]=Радна површ Name[sr_RS]=Радна површ Name[sv]=Skrivbord Name[sv_SE]=Skrivbord Name[ta]=பணிமேடை Name[ta_LK]=பணிமேடை Name[tg]=Мизи корӣ Name[th]=พื้นโต๊ะ Name[th_TH]=พื้นโต๊ะ Name[tl]=Pook ng likha Name[tl_PH]=Pook ng likha Name[tr]=Masaüstü Name[uk]=Стільниця Name[uk_UA]=Стільниця Name[ur]=ڈیسک ٹاپ Name[ur_PK]=ڈیسک ٹاپ Name[vi]=Màn hình làm việc Name[vi_VN]=Màn hình làm việc Name[zh_CN]=桌面 Name[zh_HK]=桌面 Name[zh_TW]=桌面 Comment=Manage your desktop icons Comment[am]=የ ዴስክቶፕ ምልክቶች አስተዳዳሪ Comment[ar]=إدارة أيقونات سطح المكتب Comment[be]=Наладзіць значкі на працоўным стале Comment[be@latin]=Наладзіць значкі на працоўным стале Comment[bg]=Управлявайте Вашите иконки на работния плот Comment[bg_BG]=Управлявайте Вашите иконки на работния плот Comment[bs]=Upravljajte vašim desktop ikonama Comment[ca]=Administrar icones de l'escriptori Comment[ca@valencia]=Administrar icones de l'escriptori Comment[ca_ES@valencia]=Administrar icones de l'escriptori Comment[cs]=Správa ikon na ploše Comment[cs_CZ]=Správa ikon na ploše Comment[cy]=Rheolwch eiconau'ch bwrdd gwaith Comment[da]=Administrér dine skrivebordsikoner Comment[da_DK]=Administrér dine skrivebordsikoner Comment[de]=Schreibtischsymbole verwalten Comment[de_DE]=Schreibtischsymbole verwalten Comment[el]=Διαχείριση εικονιδίων επιφάνειας εργασίας Comment[es]=Administrar iconos del escritorio Comment[es_AR]=Administrar iconos del escritorio Comment[es_CL]=Administrar iconos del escritorio Comment[es_CO]=Administrar iconos del escritorio Comment[es_CR]=Administrar iconos del escritorio Comment[es_DO]=Administrar iconos del escritorio Comment[es_EC]=Administrar iconos del escritorio Comment[es_ES]=Administrar iconos del escritorio Comment[es_GT]=Administrar iconos del escritorio Comment[es_HN]=Administrar iconos del escritorio Comment[es_MX]=Administrar iconos del escritorio Comment[es_NI]=Administrar iconos del escritorio Comment[es_PA]=Administrar iconos del escritorio Comment[es_PE]=Administrar iconos del escritorio Comment[es_PR]=Administrar iconos del escritorio Comment[es_SV]=Administrar iconos del escritorio Comment[es_UY]=Administrar iconos del escritorio Comment[es_VE]=Administrar iconos del escritorio Comment[eu]=Kudeatu mahaigaineko ikonoak Comment[eu_ES]=Kudeatu mahaigaineko ikonoak Comment[fi]=Hallitse työpöydän kuvakkeita Comment[fi_FI]=Hallitse työpöydän kuvakkeita Comment[fr]=Gérez vos icones de bureaux Comment[fr_CA]=Gérer vos icônes du bureau Comment[fr_FR]=Gérez vos icones de bureaux Comment[gd]=Stiùirich ìomhaigheagan an deasga agad Comment[gl]=Xestiona as iconas do escritorio Comment[gl_ES]=Xestiona as iconas do escritorio Comment[hr]=Upravljajte svojim ikonama radne površine Comment[hu]=Asztal ikonjainak kezelése Comment[hu_HU]=Asztal ikonjainak kezelése Comment[id]=Atur icon di desktop Comment[id_ID]=Atur icon di desktop Comment[is]=Sýsla með táknmyndir á skjáborði Comment[it]=Gestione delle icone sul desktop Comment[ja]=デスクトップアイコン管理 Comment[ja_JP]=デスクトップアイコン管理 Comment[ko]=바탕화면 아이콘을 관리합니다 Comment[ko_KR]=바탕화면 아이콘을 관리합니다 Comment[lo]=ຈັດການໄອຄອນພື້ນທີ່ໜ້າຈໍຂອງເຈົ້າ Comment[lt]=Tvarkyti darbalaukio piktogramas Comment[lt_LT]=Tvarkyti darbalaukio piktogramas Comment[lv]=Pārvaldīt savas darbvirsmas ikonas Comment[lv_LV]=Pārvaldīt savas darbvirsmas ikonas Comment[ms]=Mengurus ikon desktop anda Comment[nb]=Behandle skrivebordsikonene Comment[nb_NO]=Behandle skrivebordsikonene Comment[nds]=Desktopsymbole bearbeiten Comment[nl]=Uw bureaubladpictogrammen beheren Comment[nl_NL]=Uw bureaubladpictogrammen beheren Comment[pl]=Zarządzaj swoimi ikonami pulpitu Comment[pl_PL]=Zarządzaj swoimi ikonami pulpitu Comment[pt]=Gerir os seus ícones Comment[pt_BR]=Gerenciar seus ícones da área de trabalho Comment[pt_PT]=Gerir os seus ícones Comment[ro]=Gestionează pictogramele de pe desktop Comment[ro_RO]=Gestionează pictogramele de pe desktop Comment[ru]=Настроить значки на рабочем столе Comment[sk]=Spravovať ikony plochy Comment[sk_SK]=Spravovať ikony plochy Comment[sl]=Upravljajte z ikonami namizja Comment[sl_SI]=Upravljajte z ikonami namizja Comment[sq]=Menaxhoni ikonat e desktopit tuaj Comment[sr]=Управљање иконицама радне површи Comment[sr@Latn]=Управљање иконицама радне површи Comment[sr@ije]=Управљање иконицама радне површи Comment[sr@ijekavianlatin]=Управљање иконицама радне површи Comment[sr@latin]=Управљање иконицама радне површи Comment[sr_RS]=Управљање иконицама радне површи Comment[sv]=Hantera dina skrivbordsikoner Comment[sv_SE]=Hantera dina skrivbordsikoner Comment[tg]=Идоракунии нишонаҳои мизи корӣ Comment[th]=จัดการไอคอนพื้นโต๊ะของคุณ Comment[th_TH]=จัดการไอคอนพื้นโต๊ะของคุณ Comment[tr]=Masaüstü simgelerini yönet Comment[uk]=Керування піктограмами стільниці Comment[uk_UA]=Керування піктограмами стільниці Comment[ur]=ڈیسک ٹاپ کے آئکن کا انتظام کریں Comment[ur_PK]=ڈیسک ٹاپ کے آئکن کا انتظام کریں Comment[vi]=Quản lý biểu tượng màn hình của bạn Comment[vi_VN]=Quản lý biểu tượng màn hình của bạn Comment[zh_CN]=管理桌面图标 Comment[zh_HK]=管理你的桌面圖示 Comment[zh_TW]=管理您的桌面圖示 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-hotcorner.desktop0000664000175000017500000002132412625666430026770 0ustar fabiofabio[Desktop Entry] Icon=cs-overview Exec=cinnamon-settings hotcorner Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Hot Corners Name[ar]=أركان ساخنة Name[be]=Гарачы Кут Name[be@latin]=Гарачы Кут Name[bg]=Активни ъгли Name[bg_BG]=Активни ъгли Name[bs]=Uglovi ekrana Name[ca]=Cantonades actives Name[ca@valencia]=Cantons actius Name[ca_ES@valencia]=Cantons actius Name[cs]=Aktivní rohy Name[cs_CZ]=Aktivní rohy Name[cy]=Corneli Poeth Name[da]=Aktive hjørner Name[da_DK]=Aktive hjørner Name[de]=Aktive Ecken Name[de_DE]=Aktive Ecken Name[el]=Ενεργές γωνίες Name[es]=Esquinas activas Name[es_AR]=Esquinas activas Name[es_CL]=Esquinas activas Name[es_CO]=Esquinas activas Name[es_CR]=Esquinas activas Name[es_DO]=Esquinas activas Name[es_EC]=Esquinas activas Name[es_ES]=Esquinas activas Name[es_GT]=Esquinas activas Name[es_HN]=Esquinas activas Name[es_MX]=Esquinas activas Name[es_NI]=Esquinas activas Name[es_PA]=Esquinas activas Name[es_PE]=Esquinas activas Name[es_PR]=Esquinas activas Name[es_SV]=Esquinas activas Name[es_UY]=Esquinas activas Name[es_VE]=Esquinas activas Name[et]=Abistavad nurgad Name[et_EE]=Abistavad nurgad Name[eu]=Izkina beroak Name[eu_ES]=Izkina beroak Name[fi]=Toimintokulmat Name[fi_FI]=Toimintokulmat Name[fr]=Coins intelligents Name[fr_CA]=Coins intelligents Name[fr_FR]=Coins intelligents Name[ga]=Cúinní teo Name[gd]=Oiseanan teth Name[gl]=Cantos activos Name[gl_ES]=Cantos activos Name[hr]=Kutovi zaslona Name[hu]=Érzékeny sarkok Name[hu_HU]=Érzékeny sarkok Name[id]=Pojok Interaktif Name[id_ID]=Pojok Interaktif Name[is]=Virk skjáhorn Name[it]=Angoli attivi Name[ja]=ホットコーナー Name[ja_JP]=ホットコーナー Name[ko]=핫코너 Name[ko_KR]=핫코너 Name[lt]=Karštieji kampai Name[lt_LT]=Karštieji kampai Name[lv]=Aktīvie stūri Name[lv_LV]=Aktīvie stūri Name[ms]=Bucu Panas Name[nb]=Aktive hjørner Name[nb_NO]=Aktive hjørner Name[nds]=Schnellzugriffsecken Name[nl]=Actieve hoeken Name[nl_NL]=Actieve hoeken Name[pl]=Aktywne narożniki Name[pl_PL]=Aktywne narożniki Name[pt]=Local de ativação Name[pt_BR]=Cantos de atalho Name[pt_PT]=Local de ativação Name[ro]=Colțuri sensibile Name[ro_RO]=Colțuri sensibile Name[ru]=Горячие углы Name[sk]=Aktívne rohy Name[sk_SK]=Aktívne rohy Name[sl]=Vroči koti Name[sl_SI]=Vroči koti Name[sq]=Këndet aktivë Name[sr]=Вруће ивице Name[sr@Latn]=Вруће ивице Name[sr@ije]=Вруће ивице Name[sr@ijekavianlatin]=Вруће ивице Name[sr@latin]=Вруће ивице Name[sr_RS]=Вруће ивице Name[sv]=Aktiva hörn Name[sv_SE]=Aktiva hörn Name[tg]=Кунҷҳои тез Name[th]=มุมที่น่าสนใจ Name[th_TH]=มุมที่น่าสนใจ Name[tr]=Etkin Köşeler Name[uk]=Гарячі кути Name[uk_UA]=Гарячі кути Name[ur]=ہاٹ کونے Name[ur_PK]=ہاٹ کونے Name[vi]=Góc nóng Name[vi_VN]=Góc nóng Name[zh_CN]=热区 Name[zh_TW]=熱角 Comment=Manage hotcorner settings Comment[ar]=إدارة إعدادات الركن الساخن Comment[be]=Кіраванне наладамі гарачых кутоў Comment[be@latin]=Кіраванне наладамі гарачых кутоў Comment[bg]=Управление на настройките на активните ъгли Comment[bg_BG]=Управление на настройките на активните ъгли Comment[bs]=Upravljaj postavkama uglova ekrana Comment[ca]=Administrar configuració de cantonades actives Comment[ca@valencia]=Administra configuració de cantons actius Comment[ca_ES@valencia]=Administra configuració de cantons actius Comment[cs]=Nastavení horkých rohů Comment[cs_CZ]=Nastavení horkých rohů Comment[cy]=Rheolwch osodiadau y gornel boeth Comment[da]=Administrér indstillinger for aktive hjørner Comment[da_DK]=Administrér indstillinger for aktive hjørner Comment[de]=Einstellungen der aktiven Ecken verwalten Comment[de_DE]=Einstellungen der aktiven Ecken verwalten Comment[el]=Ρυθμίστε τις ενεργές γωνίες Comment[es]=Administrar configuración de esquinas activas Comment[es_AR]=Administrar configuración de esquinas activas Comment[es_CL]=Administrar configuración de esquinas activas Comment[es_CO]=Administrar configuración de esquinas activas Comment[es_CR]=Administrar configuración de esquinas activas Comment[es_DO]=Administrar configuración de esquinas activas Comment[es_EC]=Administrar configuración de esquinas activas Comment[es_ES]=Administrar configuración de esquinas activas Comment[es_GT]=Administrar configuración de esquinas activas Comment[es_HN]=Administrar configuración de esquinas activas Comment[es_MX]=Administrar configuración de esquinas activas Comment[es_NI]=Administrar configuración de esquinas activas Comment[es_PA]=Administrar configuración de esquinas activas Comment[es_PE]=Administrar configuración de esquinas activas Comment[es_PR]=Administrar configuración de esquinas activas Comment[es_SV]=Administrar configuración de esquinas activas Comment[es_UY]=Administrar configuración de esquinas activas Comment[es_VE]=Administrar configuración de esquinas activas Comment[eu]=Kudeatu izkina beroen ezarpenak Comment[eu_ES]=Kudeatu izkina beroen ezarpenak Comment[fi]=Muuta toimintonurkkien asetuksia Comment[fi_FI]=Muuta toimintonurkkien asetuksia Comment[fr]=Gérez les paramètres des coins intelligents Comment[fr_CA]=Gérez les paramètres des coins intelligents Comment[fr_FR]=Gérez les paramètres des coins intelligents Comment[gd]=Stiùirich roghainnean a' hotcorner Comment[gl]=Administra os axustes dos recantos activos Comment[gl_ES]=Administra os axustes dos recantos activos Comment[hr]=Upravljajte postavkama kutova zaslona Comment[hu]=Érzékeny sarok beállítása Comment[hu_HU]=Érzékeny sarok beállítása Comment[id]=Atur Pengaturan hotcorner Comment[id_ID]=Atur Pengaturan hotcorner Comment[is]=Sýsla með stillingar fyrir virk skjáhorn Comment[it]=Gestisci impostazioni degli angoli attivi Comment[ko]=핫코너 설정 관리하기 Comment[ko_KR]=핫코너 설정 관리하기 Comment[lo]=ຈັດການການຕັ້ງຄ່າ hotcorner Comment[lt]=Tvarkyti karštųjų kampų nustatymus Comment[lt_LT]=Tvarkyti karštųjų kampų nustatymus Comment[lv]=Pērvaldīt aktīvo stūru iestatījumus Comment[lv_LV]=Pērvaldīt aktīvo stūru iestatījumus Comment[ms]=Mengurus tetapan hotcorner Comment[nb]=Håndter aktive hjørner-innstillinger Comment[nb_NO]=Håndter aktive hjørner-innstillinger Comment[nds]=Schnellzugriffsecken-Einstellungen bearbeiten Comment[nl]=Instellingen voor actieve hoeken beheren Comment[nl_NL]=Instellingen voor actieve hoeken beheren Comment[pl]=Zarządzaj ustawieniami aktywnego narożnika Comment[pl_PL]=Zarządzaj ustawieniami aktywnego narożnika Comment[pt]=Gerir as definições dos cantos Comment[pt_BR]=Gerenciar as definições dos cantos Comment[pt_PT]=Gerir as definições dos cantos Comment[ro]=Gestionează configurările colțurilor sensibile Comment[ro_RO]=Gestionează configurările colțurilor sensibile Comment[ru]=Управление настройками горячих углов Comment[sk]=Spravovať nastavenia tzv. hotcornerov Comment[sk_SK]=Spravovať nastavenia tzv. hotcornerov Comment[sl]=Upravljajte z nastavitvami hitrih kotov Comment[sl_SI]=Upravljajte z nastavitvami hitrih kotov Comment[sq]=Menaxhoni parametrat e këndeve aktivë Comment[sr]=Управљање поставкама врућих ивица Comment[sr@Latn]=Управљање поставкама врућих ивица Comment[sr@ije]=Управљање поставкама врућих ивица Comment[sr@ijekavianlatin]=Управљање поставкама врућих ивица Comment[sr@latin]=Управљање поставкама врућих ивица Comment[sr_RS]=Управљање поставкама врућих ивица Comment[sv]=Hantera inställningar för aktiva hörn Comment[sv_SE]=Hantera inställningar för aktiva hörn Comment[tg]=Идоракунии танзимоти кунҷи тезкор Comment[th]=จัดการการตั้งค่ามุมร้อน Comment[th_TH]=จัดการการตั้งค่ามุมร้อน Comment[tr]=Etkin köşe ayarlarını yönet Comment[uk]=Керування налаштуваннями гарячих кутів Comment[uk_UA]=Керування налаштуваннями гарячих кутів Comment[ur]=ہاٹ کونے کی ترتیبات کا انتظام Comment[ur_PK]=ہاٹ کونے کی ترتیبات کا انتظام Comment[vi]=Quản lý thiết đặt góc nóng Comment[vi_VN]=Quản lý thiết đặt góc nóng Comment[zh_CN]=热角设置 Comment[zh_HK]=管理 hotcorner 設定 Comment[zh_TW]=管理熱角設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-extensions.desktop0000664000175000017500000002035212625666430027164 0ustar fabiofabio[Desktop Entry] Icon=cs-extensions Exec=cinnamon-settings extensions Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Extensions Name[am]=ተጨማሪዎች Name[ar]=إضافات Name[ast]=Estensiones Name[be]=Пашырэнні Name[be@latin]=Пашырэнні Name[bg]=Разширения Name[bg_BG]=Разширения Name[bs]=Proširenja Name[cs]=Rozšíření Name[cs_CZ]=Rozšíření Name[cy]=Estyniadau Name[da]=Udvidelser Name[da_DK]=Udvidelser Name[de]=Erweiterungen Name[de_DE]=Erweiterungen Name[el]=Επεκτάσεις Name[eo]=Etendaĵoj Name[es]=Extensiones Name[es_AR]=Extensiones Name[es_CL]=Extensiones Name[es_CO]=Extensiones Name[es_CR]=Extensiones Name[es_DO]=Extensiones Name[es_EC]=Extensiones Name[es_ES]=Extensiones Name[es_GT]=Extensiones Name[es_HN]=Extensiones Name[es_MX]=Extensiones Name[es_NI]=Extensiones Name[es_PA]=Extensiones Name[es_PE]=Extensiones Name[es_PR]=Extensiones Name[es_SV]=Extensiones Name[es_UY]=Extensiones Name[es_VE]=Extensiones Name[et]=Laiendid Name[et_EE]=Laiendid Name[eu]=Hedapenak Name[eu_ES]=Hedapenak Name[fi]=Laajennukset Name[fi_FI]=Laajennukset Name[ga]=Iarmhíreanna Name[gd]=Leudachain Name[gl]=Extensións Name[gl_ES]=Extensións Name[hr]=Proširenja Name[hu]=Kiterjesztések Name[hu_HU]=Kiterjesztések Name[id]=Ekstensi Name[id_ID]=Ekstensi Name[is]=Viðbætur Name[it]=Estensioni Name[ja]=拡張機能 Name[ja_JP]=拡張機能 Name[ko]=확장기능 Name[ko_KR]=확장기능 Name[lo]=ສ່ວນຂະຫຍາຍ Name[lt]=Plėtiniai Name[lt_LT]=Plėtiniai Name[lv]=Papildinājumi Name[lv_LV]=Papildinājumi Name[ms]=Sambungan Name[nb]=Utvidelser Name[nb_NO]=Utvidelser Name[nds]=Erweiterungen Name[nl]=Extensies Name[nl_NL]=Extensies Name[pa]=ਐਕਸਟੈਂਸ਼ਨਾਂ Name[pl]=Rozszerzenia Name[pl_PL]=Rozszerzenia Name[pt]=Extensões Name[pt_BR]=Extensões Name[pt_PT]=Extensões Name[ro]=Extensii Name[ro_RO]=Extensii Name[ru]=Расширения Name[sk]=Rozšírenia Name[sk_SK]=Rozšírenia Name[sl]=Razširitve Name[sl_SI]=Razširitve Name[sq]=Prapashtesat Name[sr]=Проширења Name[sr@Latn]=Проширења Name[sr@ije]=Проширења Name[sr@ijekavianlatin]=Проширења Name[sr@latin]=Проширења Name[sr_RS]=Проширења Name[sv]=Tillägg Name[sv_SE]=Tillägg Name[ta]=நீட்சிகள் Name[ta_LK]=நீட்சிகள் Name[tg]=Пасвандҳо Name[th]=ส่วนขยาย Name[th_TH]=ส่วนขยาย Name[tr]=Eklentiler Name[uk]=Розширення Name[uk_UA]=Розширення Name[ur]=ایکس ٹینشنز Name[ur_PK]=ایکس ٹینشنز Name[vi]=Phần mở rộng Name[vi_VN]=Phần mở rộng Name[zh_CN]=扩展 Name[zh_HK]=擴充套件 Name[zh_TW]=擴充套件 Comment=Manage your Cinnamon extensions Comment[am]=የሲናሞን ተጨማሪዎች አስተዳዳሪ Comment[ar]=إدارة إضافات سينامُن Comment[ast]=Alministra les tos estensiones Cinnamon Comment[be]=Кiраванне пашырэннямi Cinnamon Comment[be@latin]=Кiраванне пашырэннямi Cinnamon Comment[bg]=Управление на Вашите разширения за Cinnamon Comment[bg_BG]=Управление на Вашите разширения за Cinnamon Comment[bs]=Upravljajte vašim Cinnamon proširenjima Comment[ca]=Administrar extensions de Cinnamon Comment[ca@valencia]=Administra extensions de Cinnamon Comment[ca_ES@valencia]=Administra extensions de Cinnamon Comment[cs]=Správa Cinnamon rozšíření Comment[cs_CZ]=Správa Cinnamon rozšíření Comment[cy]=Rheolwch eich estyniadau Cinnamon Comment[da]=Administrér dine Cinnamon-udvidelser Comment[da_DK]=Administrér dine Cinnamon-udvidelser Comment[de]=Cinnamon-Erweiterungen verwalten Comment[de_DE]=Cinnamon-Erweiterungen verwalten Comment[el]=Διαχειρίσου τις επεκτάσεις του Cinnamon Comment[es]=Administrar extensiones de Cinnamon Comment[es_AR]=Administrar extensiones de Cinnamon Comment[es_CL]=Administrar extensiones de Cinnamon Comment[es_CO]=Administrar extensiones de Cinnamon Comment[es_CR]=Administrar extensiones de Cinnamon Comment[es_DO]=Administrar extensiones de Cinnamon Comment[es_EC]=Administrar extensiones de Cinnamon Comment[es_ES]=Administrar extensiones de Cinnamon Comment[es_GT]=Administrar extensiones de Cinnamon Comment[es_HN]=Administrar extensiones de Cinnamon Comment[es_MX]=Administrar extensiones de Cinnamon Comment[es_NI]=Administrar extensiones de Cinnamon Comment[es_PA]=Administrar extensiones de Cinnamon Comment[es_PE]=Administrar extensiones de Cinnamon Comment[es_PR]=Administrar extensiones de Cinnamon Comment[es_SV]=Administrar extensiones de Cinnamon Comment[es_UY]=Administrar extensiones de Cinnamon Comment[es_VE]=Administrar extensiones de Cinnamon Comment[eu]=Kudeatu zure Cinnamon hedapenak Comment[eu_ES]=Kudeatu zure Cinnamon hedapenak Comment[fi]=Hallitse Cinnamon-laajennuksia Comment[fi_FI]=Hallitse Cinnamon-laajennuksia Comment[fr]=Gérez vos extensions Cinnamon Comment[fr_CA]=Gérer les extensions Cinnamon Comment[fr_FR]=Gérez vos extensions Cinnamon Comment[gd]=Stiùirich na leudachain Cinnamon agad Comment[gl]=Administra os engadidos de Cinnamon Comment[gl_ES]=Administra os engadidos de Cinnamon Comment[hr]=Upravljajte svojim Cinnamon proširenjima Comment[hu]=Cinnamon kiterjesztések kezelése Comment[hu_HU]=Cinnamon kiterjesztések kezelése Comment[id]=Pengaturan extensions Cinnamon Comment[id_ID]=Pengaturan extensions Cinnamon Comment[is]=Sýsla með stillingar fyrir Cinnamon viðbætur Comment[it]=Gestisci le estensioni di Cinnamon Comment[ja]=Cinnamon エクステンションの管理 Comment[ja_JP]=Cinnamon エクステンションの管理 Comment[ko]=시나몬 확장기능을 관리합니다 Comment[ko_KR]=시나몬 확장기능을 관리합니다 Comment[lo]=ຈັດການສ່ວນຂະຫຍາຍ Cinnamon ຂອງເຈົ້າ Comment[lt]=Tvarkyti savo Cinnamono plėtinius Comment[lt_LT]=Tvarkyti savo Cinnamono plėtinius Comment[lv]=Pārvaldīt Cinnamon papildinājumus Comment[lv_LV]=Pārvaldīt Cinnamon papildinājumus Comment[ms]=Mengurus lanjutan Cinnamon Comment[nb]=Håndter dine Cinnamon-utvidelser Comment[nb_NO]=Håndter dine Cinnamon-utvidelser Comment[nds]=Cinnamon Erweiterungen bearbeiten Comment[nl]=Uw Cinnamon-extensies beheren Comment[nl_NL]=Uw Cinnamon-extensies beheren Comment[pl]=Zarządzaj swoimi rozszerzeniami Cinnamona Comment[pl_PL]=Zarządzaj swoimi rozszerzeniami Cinnamona Comment[pt]=Gerir as suas extensões do Cinnamon Comment[pt_BR]=Gerenciar suas extensões do Cinnamon Comment[pt_PT]=Gerir as suas extensões do Cinnamon Comment[ro]=Gestionează extensiile tale Cinnamon Comment[ro_RO]=Gestionează extensiile tale Cinnamon Comment[ru]=Управление расширениями Cinnamon Comment[sk]=Spravovať rozšírenia Cinnamon Comment[sk_SK]=Spravovať rozšírenia Cinnamon Comment[sl]=Upravljajte z razširitvami Cinnamona Comment[sl_SI]=Upravljajte z razširitvami Cinnamona Comment[sq]=Menaxhoni prapashtesat e Cinnamon Comment[sr]=Управљање проширењима Цимета Comment[sr@Latn]=Управљање проширењима Цимета Comment[sr@ije]=Управљање проширењима Цимета Comment[sr@ijekavianlatin]=Управљање проширењима Цимета Comment[sr@latin]=Управљање проширењима Цимета Comment[sr_RS]=Управљање проширењима Цимета Comment[sv]=Hantera dina Cinnamon-tillägg Comment[sv_SE]=Hantera dina Cinnamon-tillägg Comment[tg]=Идоракунии пасвандҳои Cinnamon Comment[th]=จัดการส่วนขยาย Cinnamon ของคุณ Comment[th_TH]=จัดการส่วนขยาย Cinnamon ของคุณ Comment[tr]=Cinnamon eklentilerini yönet Comment[uk]=Керування розширеннями Cinnamon Comment[uk_UA]=Керування розширеннями Cinnamon Comment[ur]=اپنے سنامون کی ایکسٹینشن کا انتظام کریں Comment[ur_PK]=اپنے سنامون کی ایکسٹینشن کا انتظام کریں Comment[vi]=Quản lý phần mở rộng Cinnamon Comment[vi_VN]=Quản lý phần mở rộng Cinnamon Comment[zh_CN]=扩展管理 Comment[zh_HK]=管理你的 Cinnamon 擴充套件 Comment[zh_TW]=管理您的 Cinnamon 擴充套件 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-privacy.desktop0000664000175000017500000001665112625666430026451 0ustar fabiofabio[Desktop Entry] Icon=cs-privacy Exec=cinnamon-settings privacy Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Privacy Name[am]=የ ግል Name[ar]=الخصوصية Name[ast]=Privacidá Name[bg]=Поверителност Name[bg_BG]=Поверителност Name[ca]=Privadesa Name[ca@valencia]=Privacitat Name[ca_ES@valencia]=Privacitat Name[cs]=Soukromí Name[cs_CZ]=Soukromí Name[cy]=Preifatwydd Name[da]=Privatliv Name[da_DK]=Privatliv Name[de]=Datenschutz Name[de_DE]=Datenschutz Name[el]=Ιδιωτικότητα Name[es]=Privacidad Name[es_AR]=Privacidad Name[es_CL]=Privacidad Name[es_CO]=Privacidad Name[es_CR]=Privacidad Name[es_DO]=Privacidad Name[es_EC]=Privacidad Name[es_ES]=Privacidad Name[es_GT]=Privacidad Name[es_HN]=Privacidad Name[es_MX]=Privacidad Name[es_NI]=Privacidad Name[es_PA]=Privacidad Name[es_PE]=Privacidad Name[es_PR]=Privacidad Name[es_SV]=Privacidad Name[es_UY]=Privacidad Name[es_VE]=Privacidad Name[et]=Privaatsus Name[et_EE]=Privaatsus Name[eu]=Pribatutasuna Name[eu_ES]=Pribatutasuna Name[fi]=Yksityisyys Name[fi_FI]=Yksityisyys Name[fr]=Confidentialité Name[fr_CA]=Confidentialité Name[fr_FR]=Confidentialité Name[gd]=Prìobhaideachd Name[gl]=Intimidade Name[gl_ES]=Intimidade Name[hr]=Privatnost Name[hu]=Magánszféra Name[hu_HU]=Magánszféra Name[id]=Privasi Name[id_ID]=Privasi Name[is]=Friðhelgi Name[ko]=프라이버시 Name[ko_KR]=프라이버시 Name[lt]=Privatumas Name[lt_LT]=Privatumas Name[ms]=Privasi Name[nb]=Personvern Name[nb_NO]=Personvern Name[nds]=Privatssphäre Name[nl]=Privéleven Name[nl_NL]=Privéleven Name[pl]=Prywatność Name[pl_PL]=Prywatność Name[pt]=Privacidade Name[pt_BR]=Privacidade Name[pt_PT]=Privacidade Name[ro]=Confidențialitate Name[ro_RO]=Confidențialitate Name[ru]=Конфиденциальность Name[sk]=Súkromný Name[sk_SK]=Súkromný Name[sl]=Zasebnost Name[sl_SI]=Zasebnost Name[sq]=Privatësia Name[sr]=Приватност Name[sr@Latn]=Приватност Name[sr@ije]=Приватност Name[sr@ijekavianlatin]=Приватност Name[sr@latin]=Приватност Name[sr_RS]=Приватност Name[sv]=Sekretess Name[sv_SE]=Sekretess Name[th]=ความเป็นส่วนตัว Name[th_TH]=ความเป็นส่วนตัว Name[tr]=Gizlilik Name[uk]=Конфіденційність Name[uk_UA]=Конфіденційність Name[ur]=پرائیویسی Name[ur_PK]=پرائیویسی Name[vi]=Riêng tư Name[vi_VN]=Riêng tư Name[zh_CN]=隐私 Name[zh_HK]=私隱 Name[zh_TW]=隱私 Comment=Cinnamon privacy settings Comment[ar]=خيارات الخصوصية لسينامُن Comment[ast]=Axustes de privacidá Cinnamon Comment[bg]=Настройки за поверителност на Cinnamon Comment[bg_BG]=Настройки за поверителност на Cinnamon Comment[ca]=Configuració de privadesa de Cinnamon Comment[ca@valencia]=Opcions de privacitat de Cinnamon Comment[ca_ES@valencia]=Opcions de privacitat de Cinnamon Comment[cs]=Nastevení soukromí v prostředí Cinnamon Comment[cs_CZ]=Nastevení soukromí v prostředí Cinnamon Comment[cy]=Gosodiadau preifatrwydd Cinnamon Comment[da]=Cinnamon-privatlivsindstillinger Comment[da_DK]=Cinnamon-privatlivsindstillinger Comment[de]=Cinnamon-Datenschutzeinstellungen Comment[de_DE]=Cinnamon-Datenschutzeinstellungen Comment[el]=Ρυθμίσεις ιδιωτικότητας του Cinnamon Comment[es]=Opciones de privacidad de Cinnamon Comment[es_AR]=Opciones de privacidad de Cinnamon Comment[es_CL]=Opciones de privacidad de Cinnamon Comment[es_CO]=Opciones de privacidad de Cinnamon Comment[es_CR]=Opciones de privacidad de Cinnamon Comment[es_DO]=Opciones de privacidad de Cinnamon Comment[es_EC]=Opciones de privacidad de Cinnamon Comment[es_ES]=Opciones de privacidad de Cinnamon Comment[es_GT]=Opciones de privacidad de Cinnamon Comment[es_HN]=Opciones de privacidad de Cinnamon Comment[es_MX]=Opciones de privacidad de Cinnamon Comment[es_NI]=Opciones de privacidad de Cinnamon Comment[es_PA]=Opciones de privacidad de Cinnamon Comment[es_PE]=Opciones de privacidad de Cinnamon Comment[es_PR]=Opciones de privacidad de Cinnamon Comment[es_SV]=Opciones de privacidad de Cinnamon Comment[es_UY]=Opciones de privacidad de Cinnamon Comment[es_VE]=Opciones de privacidad de Cinnamon Comment[eu]=Cinnamon pribatutasunaren ezarpenak Comment[eu_ES]=Cinnamon pribatutasunaren ezarpenak Comment[fi]=Cinnamonin tietosuoja-asetukset Comment[fi_FI]=Cinnamonin tietosuoja-asetukset Comment[fr]=Paramètres de confidentialité de Cinnamon Comment[fr_CA]=Paramètres de confidentialité de Cinnamon Comment[fr_FR]=Paramètres de confidentialité de Cinnamon Comment[gd]=Roghainnean prìobhaideachd Cinnamon Comment[gl]=Opciones de intimidade de Cinnamon Comment[gl_ES]=Opciones de intimidade de Cinnamon Comment[hr]=Cinnamon postavke privatnosti Comment[hu]=Cinnamon magánszféra beállítások Comment[hu_HU]=Cinnamon magánszféra beállítások Comment[id]=Pengaturan privasi cinnamon Comment[id_ID]=Pengaturan privasi cinnamon Comment[is]=Gagnaleyndarstillingar Cinnamon Comment[it]=Impostazioni privacy di Cinnamon Comment[ko]=시나몬 프라이버시 설정 Comment[ko_KR]=시나몬 프라이버시 설정 Comment[lt]=Cinnamon privatumo nustatymai Comment[lt_LT]=Cinnamon privatumo nustatymai Comment[ms]=Tetapan privasi Cinnamon Comment[nb]=Personverninnstillinger for Cinnamon Comment[nb_NO]=Personverninnstillinger for Cinnamon Comment[nds]=Cinnamon Privatsphäre Einstellungen Comment[nl]=Instellingen van Cinnamon inzake privéleven Comment[nl_NL]=Instellingen van Cinnamon inzake privéleven Comment[pl]=Ustawienia prywatności Cinnamon Comment[pl_PL]=Ustawienia prywatności Cinnamon Comment[pt]=Configurações de privacidade do Cinnamon Comment[pt_BR]=Configurações de privacidade do Cinnamon Comment[pt_PT]=Configurações de privacidade do Cinnamon Comment[ro]=Configurări de confidențialitate Cinnamon Comment[ro_RO]=Configurări de confidențialitate Cinnamon Comment[ru]=Настройки конфиденциальности Cinnamon Comment[sk]=Nastavenia súkromia Cinnamon Comment[sk_SK]=Nastavenia súkromia Cinnamon Comment[sl]=Nastavitve zasebnosti Cinnamona Comment[sl_SI]=Nastavitve zasebnosti Cinnamona Comment[sq]=Parametrat e privatësisë të Cinnamon Comment[sr]=Управљање приватношћу Цимета Comment[sr@Latn]=Управљање приватношћу Цимета Comment[sr@ije]=Управљање приватношћу Цимета Comment[sr@ijekavianlatin]=Управљање приватношћу Цимета Comment[sr@latin]=Управљање приватношћу Цимета Comment[sr_RS]=Управљање приватношћу Цимета Comment[sv]=Cinnamon integritetsinställningar Comment[sv_SE]=Cinnamon integritetsinställningar Comment[th]=ค่าตั้งความเป็นส่วนตัวของ Cinnamon Comment[th_TH]=ค่าตั้งความเป็นส่วนตัวของ Cinnamon Comment[tr]=Cinnamon gizlilik ayarları Comment[uk]=Налаштування конфіденційності Cinnamon Comment[uk_UA]=Налаштування конфіденційності Cinnamon Comment[ur]=سنامون پرائیویسی کی ترتیبات Comment[ur_PK]=سنامون پرائیویسی کی ترتیبات Comment[vi]=Thiết đặt riêng tư Cinnamon Comment[vi_VN]=Thiết đặt riêng tư Cinnamon Comment[zh_CN]=Cinnamon隐私设置 Comment[zh_HK]=Cinnamon 私隱設定 Comment[zh_TW]=Cinnamon 隱私設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-mouse.desktop0000664000175000017500000002501612625666430026117 0ustar fabiofabio[Desktop Entry] Icon=cs-mouse Exec=cinnamon-settings mouse Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Mouse and Touchpad Name[am]=አይጥ እና ተችፓድ Name[ar]=الفأرة ولوحة اللمس Name[ast]=Mur y touchpad Name[be]=Мыш i сэнсарная панэль Name[be@latin]=Мыш i сэнсарная панэль Name[bg]=Мишка и сензорен панел Name[bg_BG]=Мишка и сензорен панел Name[bs]=Miš i površina osjetljiva na dodir Name[ca]=Ratolí i ratolí tàctil Name[ca@valencia]=Ratolí i ratolí tàctil Name[ca_ES@valencia]=Ratolí i ratolí tàctil Name[cs]=Myš a touchpad Name[cs_CZ]=Myš a touchpad Name[cy]=Llygoden a Phad Cyffwrdd Name[da]=Mus og touchpad Name[da_DK]=Mus og touchpad Name[de]=Maus und Touchpad Name[de_DE]=Maus und Touchpad Name[el]=Ποντίκι και Επιφάνεια Αφής Name[eo]=Muso kaj tuŝplato Name[es]=Ratón y ratón táctil Name[es_AR]=Ratón y ratón táctil Name[es_CL]=Ratón y ratón táctil Name[es_CO]=Ratón y ratón táctil Name[es_CR]=Ratón y ratón táctil Name[es_DO]=Ratón y ratón táctil Name[es_EC]=Ratón y ratón táctil Name[es_ES]=Ratón y ratón táctil Name[es_GT]=Ratón y ratón táctil Name[es_HN]=Ratón y ratón táctil Name[es_MX]=Ratón y ratón táctil Name[es_NI]=Ratón y ratón táctil Name[es_PA]=Ratón y ratón táctil Name[es_PE]=Ratón y ratón táctil Name[es_PR]=Ratón y ratón táctil Name[es_SV]=Ratón y ratón táctil Name[es_UY]=Ratón y ratón táctil Name[es_VE]=Ratón y ratón táctil Name[et]=Hiir ja puuteplaat Name[et_EE]=Hiir ja puuteplaat Name[eu]=Sagua eta ukipen-teklatua Name[eu_ES]=Sagua eta ukipen-teklatua Name[fi]=Hiiri ja kosketuslevy Name[fi_FI]=Hiiri ja kosketuslevy Name[fr]=Souris et pavé tactile Name[fr_CA]=Souris et pavé tactile Name[fr_FR]=Souris et pavé tactile Name[ga]=Luch agus Ceap Tadhaill Name[gd]=An luchag is am pada-suathaidh Name[gl]=Rato e área táctil Name[gl_ES]=Rato e área táctil Name[hr]=Miš i touchpad Name[hu]=Egér és érintőtábla Name[hu_HU]=Egér és érintőtábla Name[id]=Mouse dan Touchpad Name[id_ID]=Mouse dan Touchpad Name[is]=Mús og snertiplatti Name[it]=Mouse e touchpad Name[ja]=マウスとタッチパッド Name[ja_JP]=マウスとタッチパッド Name[ko]=마우스와 터치패드 Name[ko_KR]=마우스와 터치패드 Name[lt]=Pelė ir jutiklinis kilimėlis Name[lt_LT]=Pelė ir jutiklinis kilimėlis Name[lv]=Pele un skārienpaliktnis Name[lv_LV]=Pele un skārienpaliktnis Name[ms]=Tetikus dan Pad Sentuh Name[nb]=Mus og styreplate Name[nb_NO]=Mus og styreplate Name[nds]=Maus und Touchpad Name[nl]=Muis en touchpad Name[nl_NL]=Muis en touchpad Name[oc]=Mirga e pavat tactil Name[om]=Hantuutee fi touchpad Name[pa]=ਮਾਊਸ ਤੇ ਟੱਚਪੈਚ Name[pl]=Mysz i panel dotykowy Name[pl_PL]=Mysz i panel dotykowy Name[pt]=Rato e painel de toque Name[pt_BR]=Mouse e Touchpad Name[pt_PT]=Rato e painel de toque Name[ro]=Mouse și touchpad Name[ro_RO]=Mouse și touchpad Name[ru]=Мышь и сенсорная панель Name[sk]=Myš a touchpad Name[sk_SK]=Myš a touchpad Name[sl]=Miška in sledilna ploščica Name[sl_SI]=Miška in sledilna ploščica Name[sr]=Миш и додирна табла Name[sr@Latn]=Миш и додирна табла Name[sr@ije]=Миш и додирна табла Name[sr@ijekavianlatin]=Миш и додирна табла Name[sr@latin]=Миш и додирна табла Name[sr_RS]=Миш и додирна табла Name[sv]=Mus och pekplatta Name[sv_SE]=Mus och pekplatta Name[ta]=சொடுக்கி மற்றும் தொடுதிட்டு Name[ta_LK]=சொடுக்கி மற்றும் தொடுதிட்டு Name[tg]=Муш ва панели ламсӣ Name[th]=เมาส์และทัชแพด Name[th_TH]=เมาส์และทัชแพด Name[tr]=Fare ve Dokunmatik Panel Name[uk]=Миша та сенсорна панель Name[uk_UA]=Миша та сенсорна панель Name[ur]=ماؤس اور ٹچ پیڈ Name[ur_PK]=ماؤس اور ٹچ پیڈ Name[vi]=Chuột và Bàn chạm chuột Name[vi_VN]=Chuột và Bàn chạm chuột Name[zh_CN]=鼠标和触摸板 Name[zh_HK]=滑鼠和觸控板 Name[zh_TW]=滑鼠與觸控板 Comment=Control mouse and touchpad settings Comment[am]=የ አይጥ እና የ ተችፓድ ማሰናጃ መቆጣጠሪያ Comment[ar]=التحكم في خصائص الفأرة ولوحة اللمس Comment[be]=Налада мышы і сэнсарнай панэлі Comment[be@latin]=Налада мышы і сэнсарнай панэлі Comment[bg]=Контрол на мишката и настройки на тъчпада Comment[bg_BG]=Контрол на мишката и настройки на тъчпада Comment[bs]=Kontrole miša i površina osjetljiva na dodir Comment[ca]=Configuració de ratolí i ratolí tàctil Comment[ca@valencia]=Configuració de ratolí i ratolí tàctil Comment[ca_ES@valencia]=Configuració de ratolí i ratolí tàctil Comment[cs]=Nastavení myši a touchpadu Comment[cs_CZ]=Nastavení myši a touchpadu Comment[cy]=Gosodiadau rheoli llygoden a phad cyffwrdd Comment[da]=Kontrollér muse- og touchpadindstillinger Comment[da_DK]=Kontrollér muse- og touchpadindstillinger Comment[de]=Maus- und Touchpad-Einstellungen verwalten Comment[de_DE]=Maus- und Touchpad-Einstellungen verwalten Comment[el]=Ρυθμίστε το ποντίκι και την επιφάνεια αφής Comment[es]=Configuración de ratón y ratón táctil Comment[es_AR]=Configuración de ratón y ratón táctil Comment[es_CL]=Configuración de ratón y ratón táctil Comment[es_CO]=Configuración de ratón y ratón táctil Comment[es_CR]=Configuración de ratón y ratón táctil Comment[es_DO]=Configuración de ratón y ratón táctil Comment[es_EC]=Configuración de ratón y ratón táctil Comment[es_ES]=Configuración de ratón y ratón táctil Comment[es_GT]=Configuración de ratón y ratón táctil Comment[es_HN]=Configuración de ratón y ratón táctil Comment[es_MX]=Configuración de ratón y ratón táctil Comment[es_NI]=Configuración de ratón y ratón táctil Comment[es_PA]=Configuración de ratón y ratón táctil Comment[es_PE]=Configuración de ratón y ratón táctil Comment[es_PR]=Configuración de ratón y ratón táctil Comment[es_SV]=Configuración de ratón y ratón táctil Comment[es_UY]=Configuración de ratón y ratón táctil Comment[es_VE]=Configuración de ratón y ratón táctil Comment[eu]=Kontrolatu sagua eta ukipen-teklatuaren ezarpenak Comment[eu_ES]=Kontrolatu sagua eta ukipen-teklatuaren ezarpenak Comment[fi]=Muuta hiiren ja kosketuslevyn asetuksia Comment[fi_FI]=Muuta hiiren ja kosketuslevyn asetuksia Comment[fr]=Paramètres de contrôle de la souris et du pavé tactile Comment[fr_CA]=Paramètres de contrôle de la souris et du pavé tactile Comment[fr_FR]=Paramètres de contrôle de la souris et du pavé tactile Comment[gd]=Stiùirich roghainnean na luchaige 's a' phada-shuathaidh Comment[gl]=Controla os axustes do rato e da área táctil Comment[gl_ES]=Controla os axustes do rato e da área táctil Comment[hr]=Upravljajte mišem i touchpad postavkama Comment[hu]=Egér és érintőtábla beállítások Comment[hu_HU]=Egér és érintőtábla beállítások Comment[id]=Atur pengaturan mouse dan touchpad Comment[id_ID]=Atur pengaturan mouse dan touchpad Comment[is]=Sýsla með stillingar músa og snertiplatta Comment[it]=Impostazioni mouse e touchpad Comment[ja]=マウス・タットパッド設定管理 Comment[ja_JP]=マウス・タットパッド設定管理 Comment[ko]=마우스와 터치패드 설정 Comment[ko_KR]=마우스와 터치패드 설정 Comment[lt]=Valdyti pelės ir jutiklinio kilimėlio nustatymus Comment[lt_LT]=Valdyti pelės ir jutiklinio kilimėlio nustatymus Comment[lv]=Vadīt peles un skārienpalikņa iestatījumus Comment[lv_LV]=Vadīt peles un skārienpalikņa iestatījumus Comment[ms]=Tetapan kawalan tetikus dan pad sesentuh Comment[nb]=Håndter mus- og styreplateinnstillinger Comment[nb_NO]=Håndter mus- og styreplateinnstillinger Comment[nds]=Maus und Touchpad-Einstellungen bearbeiten Comment[nl]=Muis- en touchpadinstellingen beheren Comment[nl_NL]=Muis- en touchpadinstellingen beheren Comment[pl]=Kontroluj ustawienia myszki i panelu dotykowego Comment[pl_PL]=Kontroluj ustawienia myszki i panelu dotykowego Comment[pt]=Controlar as definições de rato e touchpad Comment[pt_BR]=Controlar as configurações de mouse e touchpad Comment[pt_PT]=Controlar as definições de rato e touchpad Comment[ro]=Controlează configurările mouse-ului și touchpadului Comment[ro_RO]=Controlează configurările mouse-ului și touchpadului Comment[ru]=Настройка мыши и сенсорной панели Comment[sk]=Kontrolovať nastavenia myši a touchpadu Comment[sk_SK]=Kontrolovať nastavenia myši a touchpadu Comment[sl]=Upravljajte z nastavitvami miške in drsne ploščice Comment[sl_SI]=Upravljajte z nastavitvami miške in drsne ploščice Comment[sr]=Управљање поставкама миша и додирне плочице Comment[sr@Latn]=Управљање поставкама миша и додирне плочице Comment[sr@ije]=Управљање поставкама миша и додирне плочице Comment[sr@ijekavianlatin]=Управљање поставкама миша и додирне плочице Comment[sr@latin]=Управљање поставкама миша и додирне плочице Comment[sr_RS]=Управљање поставкама миша и додирне плочице Comment[sv]=Hantera inställningar för mus och pekplatta Comment[sv_SE]=Hantera inställningar för mus och pekplatta Comment[tg]=Идоракунии танзимоти муш ва панели ламсӣ Comment[th]=ควบคุมการตั้งค่าเมาส์และทัชแพด Comment[th_TH]=ควบคุมการตั้งค่าเมาส์และทัชแพด Comment[tr]=Fare ve dokunmatik panel ayarlarıı kontrol et Comment[uk]=Керування налаштуваннями миші і тачпаду Comment[uk_UA]=Керування налаштуваннями миші і тачпаду Comment[ur]=ماؤس اور ٹچ پیڈ کی ترتیبات معین کریں Comment[ur_PK]=ماؤس اور ٹچ پیڈ کی ترتیبات معین کریں Comment[vi]=Điều khiển thiết đặt chuột và bàn chạm Comment[vi_VN]=Điều khiển thiết đặt chuột và bàn chạm Comment[zh_CN]=鼠标/触摸板设置 Comment[zh_HK]=控制滑鼠和觸控板設定 Comment[zh_TW]=管理滑鼠與觸控板設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-power.desktop0000664000175000017500000002232012625666430026116 0ustar fabiofabio[Desktop Entry] Icon=cs-power Exec=cinnamon-settings power Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Power Management Name[am]=የሐይል አስተዳዳሪ Name[ar]=إدارة الطاقة Name[ast]=Alministración d'enerxía Name[be]=Кіраваньне сілкаваннем Name[be@latin]=Кіраваньне сілкаваннем Name[bg]=Управление на захранването Name[bg_BG]=Управление на захранването Name[bs]=Upravljanje napajanjem Name[ca]=Gestió d'energia Name[ca@valencia]=Gestió d'energia Name[ca_ES@valencia]=Gestió d'energia Name[cs]=Správa napájení Name[cs_CZ]=Správa napájení Name[cy]=Rheoli Pŵer Name[da]=Strømstyring Name[da_DK]=Strømstyring Name[de]=Energieverwaltung Name[de_DE]=Energieverwaltung Name[el]=Διαχείριση Ενέργειας Name[eo]=Energia administrado Name[es]=Gestión de energía Name[es_AR]=Gestión de energía Name[es_CL]=Gestión de energía Name[es_CO]=Gestión de energía Name[es_CR]=Gestión de energía Name[es_DO]=Gestión de energía Name[es_EC]=Gestión de energía Name[es_ES]=Gestión de energía Name[es_GT]=Gestión de energía Name[es_HN]=Gestión de energía Name[es_MX]=Gestión de energía Name[es_NI]=Gestión de energía Name[es_PA]=Gestión de energía Name[es_PE]=Gestión de energía Name[es_PR]=Gestión de energía Name[es_SV]=Gestión de energía Name[es_UY]=Gestión de energía Name[es_VE]=Gestión de energía Name[et]=Toitehaldus Name[et_EE]=Toitehaldus Name[eu]=Energia-kudeaketa Name[eu_ES]=Energia-kudeaketa Name[fa]=مدیریت نیرو Name[fa_AF]=مدیریت نیرو Name[fa_IR]=مدیریت نیرو Name[fi]=Virranhallinta Name[fi_FI]=Virranhallinta Name[fr]=Gestion d'alimentation Name[fr_CA]=Gestion de Courant Name[fr_FR]=Gestion d'alimentation Name[ga]=Bainisteoireacht Cumhachta Name[gd]=Stiùireadh na cumhachd Name[gl]=Xestión da enerxía Name[gl_ES]=Xestión da enerxía Name[he]=צריכת חשמל Name[he_IL]=צריכת חשמל Name[hr]=Upravljanje energijom Name[hu]=Energiakezelő Name[hu_HU]=Energiakezelő Name[id]=Manajemen Daya Name[id_ID]=Manajemen Daya Name[is]=Aflstjórnun Name[it]=Gestione dell'alimentazione Name[ja]=電源管理 Name[ja_JP]=電源管理 Name[ko]=전원관리 Name[ko_KR]=전원관리 Name[lt]=Energijos valdymas Name[lt_LT]=Energijos valdymas Name[lv]=Barošanas pārvaldība Name[lv_LV]=Barošanas pārvaldība Name[ms]=Pengurusan Kuasa Name[nb]=Strømstyring Name[nb_NO]=Strømstyring Name[nds]=Energieverwaltung Name[nl]=Energiebeheer Name[nl_NL]=Energiebeheer Name[oc]=Gestion de l'alimentacion Name[pa]=ਪਾਵਰ ਪਰਬੰਧ Name[pl]=Zarządzanie energią Name[pl_PL]=Zarządzanie energią Name[pt]=Gestão de energia Name[pt_BR]=Gerenciamento de Energia Name[pt_PT]=Gestão de energia Name[ro]=Administrare consum de energie Name[ro_RO]=Administrare consum de energie Name[ru]=Управление питанием Name[sk]=Správa napájania Name[sk_SK]=Správa napájania Name[sl]=Upravljanje porabe energije Name[sl_SI]=Upravljanje porabe energije Name[sr]=Управљање напајањем Name[sr@Latn]=Управљање напајањем Name[sr@ije]=Управљање напајањем Name[sr@ijekavianlatin]=Управљање напајањем Name[sr@latin]=Управљање напајањем Name[sr_RS]=Управљање напајањем Name[sv]=Strömhantering Name[sv_SE]=Strömhantering Name[ta]=மின்சக்தி மேலாண்மை Name[ta_LK]=மின்சக்தி மேலாண்மை Name[tg]=Идоракунии барқ Name[th]=การจัดการพลังงาน Name[th_TH]=การจัดการพลังงาน Name[tr]=Güç Yönetimi Name[uk]=Керування живленням Name[uk_UA]=Керування живленням Name[ur]=توانائی کا انتظام Name[ur_PK]=توانائی کا انتظام Name[vi]=Quản lý Điện năng Name[vi_VN]=Quản lý Điện năng Name[zh_CN]=电源管理 Name[zh_HK]=電源管理 Name[zh_TW]=電源管理 Comment=Manage power settings Comment[am]=የ ሐይል ማሰናጃዎች አስተዳዳሪ Comment[ar]=إدارة إعدادات الطاقة Comment[ast]=Axustes del alministrador d'enerxía Comment[be]=Налады сілкавання Comment[be@latin]=Налады сілкавання Comment[bg]=Управление на настройките на захранването Comment[bg_BG]=Управление на настройките на захранването Comment[bs]=Upravljaj podešavanjem napajanja Comment[ca]=Administrar configuració d'energia Comment[ca@valencia]=Administra configuració d'energia Comment[ca_ES@valencia]=Administra configuració d'energia Comment[cs]=Správa nastavení napájení Comment[cs_CZ]=Správa nastavení napájení Comment[cy]=Rheoli'r gosodiadau pŵer Comment[da]=Administrér strømstyring Comment[da_DK]=Administrér strømstyring Comment[de]=Energieeinstellungen verwalten Comment[de_DE]=Energieeinstellungen verwalten Comment[el]=Διαχείριση ρυθμίσεων ενέργειας Comment[es]=Administrar configuración de energía Comment[es_AR]=Administrar configuración de energía Comment[es_CL]=Administrar configuración de energía Comment[es_CO]=Administrar configuración de energía Comment[es_CR]=Administrar configuración de energía Comment[es_DO]=Administrar configuración de energía Comment[es_EC]=Administrar configuración de energía Comment[es_ES]=Administrar configuración de energía Comment[es_GT]=Administrar configuración de energía Comment[es_HN]=Administrar configuración de energía Comment[es_MX]=Administrar configuración de energía Comment[es_NI]=Administrar configuración de energía Comment[es_PA]=Administrar configuración de energía Comment[es_PE]=Administrar configuración de energía Comment[es_PR]=Administrar configuración de energía Comment[es_SV]=Administrar configuración de energía Comment[es_UY]=Administrar configuración de energía Comment[es_VE]=Administrar configuración de energía Comment[eu]=Kudeatu energia-ezarpenak Comment[eu_ES]=Kudeatu energia-ezarpenak Comment[fi]=Muuta virranhallinnan asetuksia Comment[fi_FI]=Muuta virranhallinnan asetuksia Comment[fr]=Gérer les paramètres d'alimentation Comment[fr_CA]=Gérer les paramètres d'alimentation Comment[fr_FR]=Gérer les paramètres d'alimentation Comment[gd]=Stiùirich roghainnean na cumhachd Comment[gl]=Administra os axustes de enerxía Comment[gl_ES]=Administra os axustes de enerxía Comment[hr]=Upravljajte postavkama energije Comment[hu]=Energiabeállítások kezelése Comment[hu_HU]=Energiabeállítások kezelése Comment[id]=Atur pengaturan Power Comment[id_ID]=Atur pengaturan Power Comment[is]=Sýsla með orkusparnaðarstillingar Comment[it]=Gestisci impostazioni alimentazione Comment[ja]=電源設定管理 Comment[ja_JP]=電源設定管理 Comment[ko]=전원설정 관리 Comment[ko_KR]=전원설정 관리 Comment[lt]=Tvarkyti energijos nustatymus Comment[lt_LT]=Tvarkyti energijos nustatymus Comment[lv]=Pārvaldīt barošanas iestatījumus Comment[lv_LV]=Pārvaldīt barošanas iestatījumus Comment[ms]=Mengurus tetapan kuasa Comment[nb]=Håndter strømsstyring Comment[nb_NO]=Håndter strømsstyring Comment[nds]=Energieeinstellungen verwalten Comment[nl]=Energie-instellingen beheren Comment[nl_NL]=Energie-instellingen beheren Comment[pl]=Zarządzaj ustawieniami zasilania Comment[pl_PL]=Zarządzaj ustawieniami zasilania Comment[pt]=Gerir as definições de energia Comment[pt_BR]=Gerenciar as configurações de energia Comment[pt_PT]=Gerir as definições de energia Comment[ro]=Gestionează configurările consumului de energie Comment[ro_RO]=Gestionează configurările consumului de energie Comment[ru]=Настройки питания Comment[sk]=Spravovať nastavenia napájania Comment[sk_SK]=Spravovať nastavenia napájania Comment[sl]=Upravljajte z nastavitvami porabe energije Comment[sl_SI]=Upravljajte z nastavitvami porabe energije Comment[sq]=Menaxhoni parametrat e furnizimit të energjisë Comment[sr]=Управљање поставкама напајања Comment[sr@Latn]=Управљање поставкама напајања Comment[sr@ije]=Управљање поставкама напајања Comment[sr@ijekavianlatin]=Управљање поставкама напајања Comment[sr@latin]=Управљање поставкама напајања Comment[sr_RS]=Управљање поставкама напајања Comment[sv]=Hantera ströminställningar Comment[sv_SE]=Hantera ströminställningar Comment[tg]=Идоракунии танзимоти барқ Comment[th]=จัดการการตั้งค่าพลังงาน Comment[th_TH]=จัดการการตั้งค่าพลังงาน Comment[tr]=Güç ayarlarını yönet Comment[uk]=Керування налаштуваннями живлення Comment[uk_UA]=Керування налаштуваннями живлення Comment[ur]=توانائی کی ترتیبات کا انتظام Comment[ur_PK]=توانائی کی ترتیبات کا انتظام Comment[vi]=Quản lý thiết đặt năng lượng Comment[vi_VN]=Quản lý thiết đặt năng lượng Comment[zh_CN]=电源管理 Comment[zh_HK]=管理電源設定 Comment[zh_TW]=管理電源設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-user.desktop0000664000175000017500000002541712625666430025752 0ustar fabiofabio[Desktop Entry] Icon=cs-user Exec=cinnamon-settings user Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Account details Name[am]=የ መግለጫ ዝርዝሮች Name[ar]=تفاصيل الحساب Name[ast]=Detalles de cuenta Name[be]=Дэталі ўліковага запісу Name[be@latin]=Дэталі ўліковага запісу Name[bg]=Данни за акаунта Name[bg_BG]=Данни за акаунта Name[bs]=Detalji o računu Name[ca]=Detalls del compte Name[ca@valencia]=Detalls del compte Name[ca_ES@valencia]=Detalls del compte Name[cs]=Podrobnosti o účtu Name[cs_CZ]=Podrobnosti o účtu Name[cy]=Manylion cyfrif Name[da]=Kontodetaljer Name[da_DK]=Kontodetaljer Name[de]=Kontodetails Name[de_DE]=Kontodetails Name[el]=Λεπτομέρειες λογαριασμού Name[es]=Detalles de cuenta Name[es_AR]=Detalles de cuenta Name[es_CL]=Detalles de cuenta Name[es_CO]=Detalles de cuenta Name[es_CR]=Detalles de cuenta Name[es_DO]=Detalles de cuenta Name[es_EC]=Detalles de cuenta Name[es_ES]=Detalles de cuenta Name[es_GT]=Detalles de cuenta Name[es_HN]=Detalles de cuenta Name[es_MX]=Detalles de cuenta Name[es_NI]=Detalles de cuenta Name[es_PA]=Detalles de cuenta Name[es_PE]=Detalles de cuenta Name[es_PR]=Detalles de cuenta Name[es_SV]=Detalles de cuenta Name[es_UY]=Detalles de cuenta Name[es_VE]=Detalles de cuenta Name[et]=Konto üksikasjad Name[et_EE]=Konto üksikasjad Name[eu]=Kontuaren xehetasunak Name[eu_ES]=Kontuaren xehetasunak Name[fi]=Tilin tiedot Name[fi_FI]=Tilin tiedot Name[fr]=Détails du compte Name[fr_CA]=Détails du compte Name[fr_FR]=Détails du compte Name[fy]=Akkount details Name[gd]=Mion-fhiosrachadh a' chunntais Name[gl]=Detalles da conta Name[gl_ES]=Detalles da conta Name[hr]=Pojedinosti računa Name[hu]=Fiók részletek Name[hu_HU]=Fiók részletek Name[id]=Detail akun Name[id_ID]=Detail akun Name[is]=Nánar um notanda Name[it]=Dettagli dell'account Name[ja]=アカウントの詳細 Name[ja_JP]=アカウントの詳細 Name[ko]=사용자정보 Name[ko_KR]=사용자정보 Name[lt]=Paskyros informacija Name[lt_LT]=Paskyros informacija Name[lv]=Detalizēta informācija par kontu Name[lv_LV]=Detalizēta informācija par kontu Name[ms]=Perincian akaun Name[nb]=Kontodetaljer Name[nb_NO]=Kontodetaljer Name[nds]=Konto - Einzelheiten Name[nl]=Accountdetails Name[nl_NL]=Accountdetails Name[oc]=Informacions del compte Name[om]=Eenyummeessaa baldhaa Name[pa]=ਅਕਾਊਂਟ ਵੇਰਵਾ Name[pl]=Szczegóły konta Name[pl_PL]=Szczegóły konta Name[pt]=Detalhes da conta Name[pt_BR]=Detalhes da conta Name[pt_PT]=Detalhes da conta Name[ro]=Detalii cont Name[ro_RO]=Detalii cont Name[ru]=Детали учётной записи Name[sk]=Podrobnosti o účte Name[sk_SK]=Podrobnosti o účte Name[sl]=Podrobnosti računa Name[sl_SI]=Podrobnosti računa Name[sq]=Detajet e llogarisë Name[sr]=Детаљи о налогу Name[sr@Latn]=Детаљи о налогу Name[sr@ije]=Детаљи о налогу Name[sr@ijekavianlatin]=Детаљи о налогу Name[sr@latin]=Детаљи о налогу Name[sr_RS]=Детаљи о налогу Name[sv]=Kontodetaljer Name[sv_SE]=Kontodetaljer Name[ta]=கணக்கு விவரங்கள் Name[ta_LK]=கணக்கு விவரங்கள் Name[tg]=Тафсилоти ҳисоб Name[th]=รายละเอียดบัญชีผู้ใช้ Name[th_TH]=รายละเอียดบัญชีผู้ใช้ Name[tr]=Hesap Ayrıntıları Name[uk]=Дані облікового запису Name[uk_UA]=Дані облікового запису Name[ur]=کھاتے کی تفصیلات دکھائیں Name[ur_PK]=کھاتے کی تفصیلات دکھائیں Name[vi]=Chi tiết tài khoản Name[vi_VN]=Chi tiết tài khoản Name[zh_CN]=帐户详情 Name[zh_HK]=帳號詳細資料 Name[zh_TW]=帳號詳細資訊 Comment=Change your user preferences and password Comment[am]=የ እርስዎን የመጠቀሚያ ምርጫዎች እና የመግቢያ ቃል መቀየሪያ Comment[ar]=غيّر خيارات المستخدم وكلمة المرور Comment[ast]=Camuda les tos preferencies d'usuariu y contraseña Comment[be]=Змяніць карыстальніцкія налады і пароль Comment[be@latin]=Змяніць карыстальніцкія налады і пароль Comment[bg]=Променете Вашите потребителски настройки и парола Comment[bg_BG]=Променете Вашите потребителски настройки и парола Comment[bs]=Promijeni korisničke postavke i šifru Comment[ca]=Canviar les preferències d'usuari i la contrasenya Comment[ca@valencia]=Canvia les preferències d'usuari i contrasenya Comment[ca_ES@valencia]=Canvia les preferències d'usuari i contrasenya Comment[cs]=Změnit nastavení uživatele a hesla Comment[cs_CZ]=Změnit nastavení uživatele a hesla Comment[cy]=Newid eich dewisiadau defnyddiwr a chyfrinair Comment[da]=Ændr dine brugerindstillinger og din adgangskode Comment[da_DK]=Ændr dine brugerindstillinger og din adgangskode Comment[de]=Benutzereinstellungen und das Passwort ändern Comment[de_DE]=Benutzereinstellungen und das Passwort ändern Comment[el]=Αλλάξτε τις προτιμήσεις χρήστη σας και τον κωδικό πρόσβασης Comment[es]=Cambiar las preferencias de usuario y contraseña Comment[es_AR]=Cambiar las preferencias de usuario y contraseña Comment[es_CL]=Cambiar las preferencias de usuario y contraseña Comment[es_CO]=Cambiar las preferencias de usuario y contraseña Comment[es_CR]=Cambiar las preferencias de usuario y contraseña Comment[es_DO]=Cambiar las preferencias de usuario y contraseña Comment[es_EC]=Cambiar las preferencias de usuario y contraseña Comment[es_ES]=Cambiar las preferencias de usuario y contraseña Comment[es_GT]=Cambiar las preferencias de usuario y contraseña Comment[es_HN]=Cambiar las preferencias de usuario y contraseña Comment[es_MX]=Cambiar las preferencias de usuario y contraseña Comment[es_NI]=Cambiar las preferencias de usuario y contraseña Comment[es_PA]=Cambiar las preferencias de usuario y contraseña Comment[es_PE]=Cambiar las preferencias de usuario y contraseña Comment[es_PR]=Cambiar las preferencias de usuario y contraseña Comment[es_SV]=Cambiar las preferencias de usuario y contraseña Comment[es_UY]=Cambiar las preferencias de usuario y contraseña Comment[es_VE]=Cambiar las preferencias de usuario y contraseña Comment[eu]=Aldatu erabiltzailearen hobespenak eta pasahitza Comment[eu_ES]=Aldatu erabiltzailearen hobespenak eta pasahitza Comment[fi]=Muuta käyttäjäasetuksiasi ja salasanaasi Comment[fi_FI]=Muuta käyttäjäasetuksiasi ja salasanaasi Comment[fr]=Changez vos préférences utilisateur et votre mot de passe Comment[fr_CA]=Modifier vos paramètres d'utilisateur et votre mot de passe Comment[fr_FR]=Changez vos préférences utilisateur et votre mot de passe Comment[gd]=Atharraich roghainnean a' chleachdaiche 's am facal-faire agad Comment[gl]=Cambia as preferencias e o contrasinal do usuario Comment[gl_ES]=Cambia as preferencias e o contrasinal do usuario Comment[hr]=Promijenite svoje osobitosti korisnika i lozinke Comment[hu]=Felhasználói beállítások és jelszavak módosítása Comment[hu_HU]=Felhasználói beállítások és jelszavak módosítása Comment[id]=Ubah kata kunci dan preferensi anda Comment[id_ID]=Ubah kata kunci dan preferensi anda Comment[is]=Breyta notandastillingum þínum og lykilorði Comment[it]=Modifica preferenze utente e password Comment[ja]=ユーザ設定とパスワードの変更 Comment[ja_JP]=ユーザ設定とパスワードの変更 Comment[ko]=사용자 설정과 비밀번호를 바꿉니다 Comment[ko_KR]=사용자 설정과 비밀번호를 바꿉니다 Comment[lt]=Keisti naudotojo nuostatas ir slaptažodį Comment[lt_LT]=Keisti naudotojo nuostatas ir slaptažodį Comment[lv]=Mainīt savas lietotāja vēlmes un paroli Comment[lv_LV]=Mainīt savas lietotāja vēlmes un paroli Comment[ms]=Tukar pilihan pengguna dan kata laluan Comment[nb]=Endre dine brukerinnstillinger og passord Comment[nb_NO]=Endre dine brukerinnstillinger og passord Comment[nds]=Ändern Sie Ihre Benutzereinstellungen und Passwort Comment[nl]=Verander uw gebruikersvoorkeuren en wachtwoord Comment[nl_NL]=Verander uw gebruikersvoorkeuren en wachtwoord Comment[pl]=Zmień swoje preferencje użytkownika i hasło Comment[pl_PL]=Zmień swoje preferencje użytkownika i hasło Comment[pt]=Mudar as suas preferências de utilizador e palavra passe Comment[pt_BR]=Mudar as suas preferências e senha de usuário Comment[pt_PT]=Mudar as suas preferências de utilizador e palavra passe Comment[ro]=Schimbă preferințele de utilizator și parola Comment[ro_RO]=Schimbă preferințele de utilizator și parola Comment[ru]=Изменить пользовательские настройки и пароль Comment[sk]=Zmeniť vaše používateľské predvoľby a heslo Comment[sk_SK]=Zmeniť vaše používateľské predvoľby a heslo Comment[sl]=Spremenite uporabniške možnosti in geslo Comment[sl_SI]=Spremenite uporabniške možnosti in geslo Comment[sq]=Ndrysho parapëlqimet e përdoruesit dhe fjalëkalimin Comment[sr]=Промени поставке корисничког налога и лозинку Comment[sr@Latn]=Промени поставке корисничког налога и лозинку Comment[sr@ije]=Промени поставке корисничког налога и лозинку Comment[sr@ijekavianlatin]=Промени поставке корисничког налога и лозинку Comment[sr@latin]=Промени поставке корисничког налога и лозинку Comment[sr_RS]=Промени поставке корисничког налога и лозинку Comment[sv]=Ändra dina användarinställnigar och lösenord Comment[sv_SE]=Ändra dina användarinställnigar och lösenord Comment[tg]=Тағйир додани хусусиятҳо ва пароли корбар Comment[th]=เปลี่ยนการตั้งค่าผู้ใช้ขั้นสูงและรหัสผ่านของคุณ Comment[th_TH]=เปลี่ยนการตั้งค่าผู้ใช้ขั้นสูงและรหัสผ่านของคุณ Comment[tr]=Kullanıcı tercihlerini/haklarını ve şifresini değiştir Comment[uk]=Змінити налаштування користувача і пароль Comment[uk_UA]=Змінити налаштування користувача і пароль Comment[vi]=Thay đổi tuỳ thích và mật khẩu người dùng của bạn Comment[vi_VN]=Thay đổi tuỳ thích và mật khẩu người dùng của bạn Comment[zh_CN]=修改用户设置和密码 Comment[zh_HK]=變更你的使用者偏好設定與密碼 Comment[zh_TW]=變更您的使用者偏好設定與密碼 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-effects.desktop0000664000175000017500000002050212625666430026401 0ustar fabiofabio[Desktop Entry] Icon=cs-desktop-effects Exec=cinnamon-settings effects Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Effects Name[am]=ውጤቶች Name[ar]=تأثيرات Name[ast]=Efeutos Name[be]=Спецэфекты Name[be@latin]=Спецэфекты Name[bg]=Ефекти Name[bg_BG]=Ефекти Name[bs]=Efekti Name[ca]=Efectes Name[ca@valencia]=Efectes Name[ca_ES@valencia]=Efectes Name[cs]=Efekty Name[cs_CZ]=Efekty Name[cy]=Effeithiau Name[da]=Effekter Name[da_DK]=Effekter Name[de]=Effekte Name[de_DE]=Effekte Name[el]=Εφέ Name[eo]=Efektoj Name[es]=Efectos Name[es_AR]=Efectos Name[es_CL]=Efectos Name[es_CO]=Efectos Name[es_CR]=Efectos Name[es_DO]=Efectos Name[es_EC]=Efectos Name[es_ES]=Efectos Name[es_GT]=Efectos Name[es_HN]=Efectos Name[es_MX]=Efectos Name[es_NI]=Efectos Name[es_PA]=Efectos Name[es_PE]=Efectos Name[es_PR]=Efectos Name[es_SV]=Efectos Name[es_UY]=Efectos Name[es_VE]=Efectos Name[et]=Efektid Name[et_EE]=Efektid Name[eu]=Efektuak Name[eu_ES]=Efektuak Name[fi]=Tehosteet Name[fi_FI]=Tehosteet Name[fr]=Effets Name[fr_CA]=Effets Name[fr_FR]=Effets Name[ga]=Maisíochtaí Name[gd]=Èifeachdan Name[gl]=Efectos Name[gl_ES]=Efectos Name[hr]=Efekti Name[hu]=Effektusok Name[hu_HU]=Effektusok Name[id]=Efek Name[id_ID]=Efek Name[is]=Viðmótsbrellur Name[it]=Effetti Name[ja]=効果 Name[ja_JP]=効果 Name[ko]=효과 Name[ko_KR]=효과 Name[lo]=ເອັບເຟັກ Name[lt]=Efektai Name[lt_LT]=Efektai Name[lv]=Efekti Name[lv_LV]=Efekti Name[ms]=Kesan Name[nb]=Effekter Name[nb_NO]=Effekter Name[nds]=Effekte Name[nl]=Effecten Name[nl_NL]=Effecten Name[oc]=Efièches Name[om]=galteelee Name[pa]=ਪ੍ਰਭਾਵ Name[pl]=Efekty Name[pl_PL]=Efekty Name[pt]=Efeitos Name[pt_BR]=Efeitos Name[pt_PT]=Efeitos Name[ro]=Efecte Name[ro_RO]=Efecte Name[ru]=Эффекты Name[sk]=Efekty Name[sk_SK]=Efekty Name[sl]=Učinki Name[sl_SI]=Učinki Name[sq]=Efektet Name[sr]=Дејства Name[sr@Latn]=Дејства Name[sr@ije]=Дејства Name[sr@ijekavianlatin]=Дејства Name[sr@latin]=Дејства Name[sr_RS]=Дејства Name[sv]=Effekter Name[sv_SE]=Effekter Name[ta]=விளைவுகள் Name[ta_LK]=விளைவுகள் Name[tg]=Таъсирҳо Name[th]=ลูกเล่น Name[th_TH]=ลูกเล่น Name[tr]=Efektler Name[uk]=Ефекти Name[uk_UA]=Ефекти Name[ur]=اثرات Name[ur_PK]=اثرات Name[vi]=Hiệu ứng Name[vi_VN]=Hiệu ứng Name[zh_CN]=特效 Name[zh_HK]=效果 Name[zh_TW]=效果 Comment=Control Cinnamon visual effects. Comment[am]=የ ሲናሞን መመልከቻ ውጤቶች መቆጣጠሪያ Comment[ar]=التحكم في تأثيرات سينامُن المرئية. Comment[be]=Кіраванне візуальнымі эфектамі Cinnamon Comment[be@latin]=Кіраванне візуальнымі эфектамі Cinnamon Comment[bg]=Контролиране на визуалните ефекти на Cinnamon Comment[bg_BG]=Контролиране на визуалните ефекти на Cinnamon Comment[bs]=Upravljaj Cinnamon vizuelnim efektima Comment[ca]=Configurar efectes visuals de Cinnamon Comment[ca@valencia]=Configurar efectes visuals de Cinnamon Comment[ca_ES@valencia]=Configurar efectes visuals de Cinnamon Comment[cs]=Nastavení Cinnamon vizuálních efektů Comment[cs_CZ]=Nastavení Cinnamon vizuálních efektů Comment[cy]=Rheolwch effeitiau gwledol Cinnamon, Comment[da]=Kontrollér Cinnamons visuelle effekter Comment[da_DK]=Kontrollér Cinnamons visuelle effekter Comment[de]=Cinnamons visuelle Effekte steuern. Comment[de_DE]=Cinnamons visuelle Effekte steuern. Comment[el]=Ρυθμίστε τα οπτικά εφέ του Cinnamon Comment[es]=Configurar efectos visuales de Cinnamon Comment[es_AR]=Configurar efectos visuales de Cinnamon Comment[es_CL]=Configurar efectos visuales de Cinnamon Comment[es_CO]=Configurar efectos visuales de Cinnamon Comment[es_CR]=Configurar efectos visuales de Cinnamon Comment[es_DO]=Configurar efectos visuales de Cinnamon Comment[es_EC]=Configurar efectos visuales de Cinnamon Comment[es_ES]=Configurar efectos visuales de Cinnamon Comment[es_GT]=Configurar efectos visuales de Cinnamon Comment[es_HN]=Configurar efectos visuales de Cinnamon Comment[es_MX]=Configurar efectos visuales de Cinnamon Comment[es_NI]=Configurar efectos visuales de Cinnamon Comment[es_PA]=Configurar efectos visuales de Cinnamon Comment[es_PE]=Configurar efectos visuales de Cinnamon Comment[es_PR]=Configurar efectos visuales de Cinnamon Comment[es_SV]=Configurar efectos visuales de Cinnamon Comment[es_UY]=Configurar efectos visuales de Cinnamon Comment[es_VE]=Configurar efectos visuales de Cinnamon Comment[eu]=Kontrolatu Cinnamonen efektu bisualak Comment[eu_ES]=Kontrolatu Cinnamonen efektu bisualak Comment[fi]=Hallitse Cinnamonin ilmettä. Comment[fi_FI]=Hallitse Cinnamonin ilmettä. Comment[fr]=Gérez les effets visuels de Cinnamon Comment[fr_CA]=Contrôler les effets visuels de Cinnamon Comment[fr_FR]=Gérez les effets visuels de Cinnamon Comment[gd]=Stiùirich èifeachdan lèirsinneach Cinnamon. Comment[gl]=Control dos efectos visuais de Cinnamon. Comment[gl_ES]=Control dos efectos visuais de Cinnamon. Comment[hr]=Upravljajte Cinnamon vizualnim efektima. Comment[hu]=Cinnamon vizuális effektusok beállítása Comment[hu_HU]=Cinnamon vizuális effektusok beállítása Comment[id]=Atur efek visual Cinnamon Comment[id_ID]=Atur efek visual Cinnamon Comment[is]=Stýra viðmótsbrellum í Cinnamon. Comment[it]=Controllo effetti grafici di Cinnamon Comment[ja]=Cinnamon ビジュアルエフェクトコントロール Comment[ja_JP]=Cinnamon ビジュアルエフェクトコントロール Comment[ko]=시나몬 비주얼 효과를 조절합니다. Comment[ko_KR]=시나몬 비주얼 효과를 조절합니다. Comment[lo]=ຈັດການ Cinnamon visual effects. Comment[lt]=Valdyti Cinnamon vaizdo efektus. Comment[lt_LT]=Valdyti Cinnamon vaizdo efektus. Comment[lv]=Pārvaldīt Cinnamon vizuālos efektus. Comment[lv_LV]=Pārvaldīt Cinnamon vizuālos efektus. Comment[ms]=Kawalan kesan visual Cinnamon Comment[nb]=Styr Cinnamons visuelle effekter Comment[nb_NO]=Styr Cinnamons visuelle effekter Comment[nds]=Cinnamons Visuelle Effekte bearbeiten Comment[nl]=Visuele effecten van Cinnamon beheren. Comment[nl_NL]=Visuele effecten van Cinnamon beheren. Comment[pl]=Kontroluj efekty wizualne Cinnamona. Comment[pl_PL]=Kontroluj efekty wizualne Cinnamona. Comment[pt]=Controle os efeitos visuais do Cinnamon. Comment[pt_BR]=Controle os efeitos visuais do Cinnamon Comment[pt_PT]=Controle os efeitos visuais do Cinnamon. Comment[ro]=Controlează efectele vizuale Cinnamon. Comment[ro_RO]=Controlează efectele vizuale Cinnamon. Comment[ru]=Управление визуальными эффектами Cinnamon. Comment[sk]=Kontrolovať vizuálne efekty Cinnamon Comment[sk_SK]=Kontrolovať vizuálne efekty Cinnamon Comment[sl]=Upravljajte z vidnimi učinki Cinnamona Comment[sl_SI]=Upravljajte z vidnimi učinki Cinnamona Comment[sq]=Efektet vizuale të Cinnamon. Comment[sr]=Управљање дејствима површи Цимета. Comment[sr@Latn]=Управљање дејствима површи Цимета. Comment[sr@ije]=Управљање дејствима површи Цимета. Comment[sr@ijekavianlatin]=Управљање дејствима површи Цимета. Comment[sr@latin]=Управљање дејствима површи Цимета. Comment[sr_RS]=Управљање дејствима површи Цимета. Comment[sv]=Styr Cinnamons visuella effekter. Comment[sv_SE]=Styr Cinnamons visuella effekter. Comment[tg]=Идоракунии таъсирҳои визуалии Cinnamon Comment[th]=ควบคุมผลภาพ Cinnamon Comment[th_TH]=ควบคุมผลภาพ Cinnamon Comment[tr]=Cinnamon görsel efektlerini kontrol et Comment[uk]=Керування візуальними ефектами Cinnamon Comment[uk_UA]=Керування візуальними ефектами Cinnamon Comment[ur]=سنامون کے نمائشی تاثرات کو کنٹرول کریں Comment[ur_PK]=سنامون کے نمائشی تاثرات کو کنٹرول کریں Comment[vi]=Điều khiển hiệu ứng trực quan Cinnamon. Comment[vi_VN]=Điều khiển hiệu ứng trực quan Cinnamon. Comment[zh_CN]=控制 Cinnamon 显示特效。 Comment[zh_HK]=控制 Cinnamon 視覺效果。 Comment[zh_TW]=管理 Cinnamon 視覺效果。 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-settings-screensaver.desktop0000664000175000017500000002063512625666430027311 0ustar fabiofabio[Desktop Entry] Icon=cs-screensaver Exec=cinnamon-settings screensaver Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; Name=Screensaver Name[am]=መመልከቻ ማዳኛ Name[ar]=شاشة التوقف Name[bg]=Предпазител на екрана Name[bg_BG]=Предпазител на екрана Name[ca]=Estalvi de pantalla Name[ca@valencia]=Estalvi de pantalla Name[ca_ES@valencia]=Estalvi de pantalla Name[cs]=Šetřič obrazovky Name[cs_CZ]=Šetřič obrazovky Name[cy]=Arbedwr sgrin Name[da]=Pauseskærm Name[da_DK]=Pauseskærm Name[de]=Bildschirmschoner Name[de_DE]=Bildschirmschoner Name[es]=Salvapantallas Name[es_AR]=Salvapantallas Name[es_CL]=Salvapantallas Name[es_CO]=Salvapantallas Name[es_CR]=Salvapantallas Name[es_DO]=Salvapantallas Name[es_EC]=Salvapantallas Name[es_ES]=Salvapantallas Name[es_GT]=Salvapantallas Name[es_HN]=Salvapantallas Name[es_MX]=Salvapantallas Name[es_NI]=Salvapantallas Name[es_PA]=Salvapantallas Name[es_PE]=Salvapantallas Name[es_PR]=Salvapantallas Name[es_SV]=Salvapantallas Name[es_UY]=Salvapantallas Name[es_VE]=Salvapantallas Name[eu]=Pantaila-babeslea Name[eu_ES]=Pantaila-babeslea Name[fi]=Näytönsäästäjä Name[fi_FI]=Näytönsäästäjä Name[fr]=Économiseur d'écran Name[fr_CA]=Économiseur d'écran Name[fr_FR]=Économiseur d'écran Name[gd]=Sàbhalaiche-sgrìn Name[gl]=Protector de pantalla Name[gl_ES]=Protector de pantalla Name[hr]=Čuvar zaslona Name[hu]=Képernyővédő Name[hu_HU]=Képernyővédő Name[is]=Skjáhvíla Name[it]=Salvaschermo Name[ko]=화면 보호기 Name[ko_KR]=화면 보호기 Name[lt]=Ekrano užsklanda Name[lt_LT]=Ekrano užsklanda Name[nb]=Skjermsparer Name[nb_NO]=Skjermsparer Name[nds]=Bildschirmschoner Name[nl]=Schermbeveiliging Name[nl_NL]=Schermbeveiliging Name[pl]=Wygaszacz ekranu Name[pl_PL]=Wygaszacz ekranu Name[pt]=Proteção de ecrã Name[pt_BR]=Proteção de tela Name[pt_PT]=Proteção de ecrã Name[ro]=Protector ecran Name[ro_RO]=Protector ecran Name[ru]=Экранная заставка Name[sk]=Šetrič obrazovky Name[sk_SK]=Šetrič obrazovky Name[sv]=Skärmsläckare Name[sv_SE]=Skärmsläckare Name[th]=โปรแกรมรักษาหน้าจอ Name[th_TH]=โปรแกรมรักษาหน้าจอ Name[tr]=Ekran koruyucu Name[uk]=Зберігач екрану Name[uk_UA]=Зберігач екрану Name[ur]=سکرین سیور Name[ur_PK]=سکرین سیور Name[vi]=Trình bảo vệ màn hình Name[vi_VN]=Trình bảo vệ màn hình Name[zh_CN]=屏幕保护程序 Name[zh_HK]=螢幕保護程式 Name[zh_TW]=螢幕保護程式 Comment=Manage screen lock settings Comment[am]=የመመልከቻ መቆለፊያ ማሰናጃ አስተዳዳሪ Comment[ar]=إدارة إعدادات قفل الشاشة Comment[ast]=Axustes del alministrador de bloquéu de pantalla Comment[be]=Кіраванне параметрамі блакiравання экрана Comment[be@latin]=Кіраванне параметрамі блакiравання экрана Comment[bg]=Управление на настройките за заключване на екрана Comment[bg_BG]=Управление на настройките за заключване на екрана Comment[bs]=Upravljaj postavkama zaključavanja ekrana Comment[ca]=Configurar bloqueig de pantalla Comment[ca@valencia]=Configura bloqueig de pantalla Comment[ca_ES@valencia]=Configura bloqueig de pantalla Comment[cs]=Nastavení zamykací obrazovky Comment[cs_CZ]=Nastavení zamykací obrazovky Comment[cy]=Rheoli gosodiadau cloi'r sgrin Comment[da]=Administrér skærmlåsindstillinger Comment[da_DK]=Administrér skærmlåsindstillinger Comment[de]=Einstellungen der Bildschirmsperre verwalten Comment[de_DE]=Einstellungen der Bildschirmsperre verwalten Comment[el]=Διαχείριση ρυθμίσεων κλειδώματος οθόνης Comment[es]=Configurar bloqueo de pantalla Comment[es_AR]=Configurar bloqueo de pantalla Comment[es_CL]=Configurar bloqueo de pantalla Comment[es_CO]=Configurar bloqueo de pantalla Comment[es_CR]=Configurar bloqueo de pantalla Comment[es_DO]=Configurar bloqueo de pantalla Comment[es_EC]=Configurar bloqueo de pantalla Comment[es_ES]=Configurar bloqueo de pantalla Comment[es_GT]=Configurar bloqueo de pantalla Comment[es_HN]=Configurar bloqueo de pantalla Comment[es_MX]=Configurar bloqueo de pantalla Comment[es_NI]=Configurar bloqueo de pantalla Comment[es_PA]=Configurar bloqueo de pantalla Comment[es_PE]=Configurar bloqueo de pantalla Comment[es_PR]=Configurar bloqueo de pantalla Comment[es_SV]=Configurar bloqueo de pantalla Comment[es_UY]=Configurar bloqueo de pantalla Comment[es_VE]=Configurar bloqueo de pantalla Comment[eu]=Kudeatu pantaila blokeatzearen ezarpenak Comment[eu_ES]=Kudeatu pantaila blokeatzearen ezarpenak Comment[fi]=Muokkaa näytön lukituksen asetuksia Comment[fi_FI]=Muokkaa näytön lukituksen asetuksia Comment[fr]=Gérer les paramètres de verrouillage d'écran Comment[fr_CA]=Gérer les paramètres de verrouillage d'écran Comment[fr_FR]=Gérer les paramètres de verrouillage d'écran Comment[gd]=Stiùirich roghainnean glasaidh na sgrìn Comment[gl]=Administra os axustes do bloqueo de pantalla Comment[gl_ES]=Administra os axustes do bloqueo de pantalla Comment[hr]=Upravljajte postavkama zaključavanja zaslona Comment[hu]=Képernyőzárolás beállítások kezelése Comment[hu_HU]=Képernyőzárolás beállítások kezelése Comment[id]=Mengatur setelan kunci layar Comment[id_ID]=Mengatur setelan kunci layar Comment[is]=Sýsla með stillingar skjálæsingar Comment[it]=Gestisci impostazioni di blocco dello schermo Comment[ja]=スクリーンロック管理の設定 Comment[ja_JP]=スクリーンロック管理の設定 Comment[ko]=화면잠금 설정 Comment[ko_KR]=화면잠금 설정 Comment[lt]=Tvarkyti ekrano užrakto nustatymus Comment[lt_LT]=Tvarkyti ekrano užrakto nustatymus Comment[lv]=Pārvaldīt ekrāna bloķēšanas uzstādījumus Comment[lv_LV]=Pārvaldīt ekrāna bloķēšanas uzstādījumus Comment[ms]=Mengurus tetapan kunci skrin Comment[nb]=Håndter innstillinger for skjermlås Comment[nb_NO]=Håndter innstillinger for skjermlås Comment[nds]=Bildschirmsperren-Einstellungen bearbeiten Comment[nl]=Instellingen voor schermvergrendeling beheren Comment[nl_NL]=Instellingen voor schermvergrendeling beheren Comment[pl]=Zarządzaj ustawieniami blokady ekranu Comment[pl_PL]=Zarządzaj ustawieniami blokady ekranu Comment[pt]=Gerir definições de bloqueio de ecrã Comment[pt_BR]=Gerenciar configurações de bloqueio de tela Comment[pt_PT]=Gerir definições de bloqueio de ecrã Comment[ro]=Gestionează blocarea ecranului Comment[ro_RO]=Gestionează blocarea ecranului Comment[ru]=Настройка блокировки экрана Comment[sk]=Spravovať nastavenia uzamykania obrazovky Comment[sk_SK]=Spravovať nastavenia uzamykania obrazovky Comment[sl]=Upravljajte z nastavitvami zaklepa zaslona Comment[sl_SI]=Upravljajte z nastavitvami zaklepa zaslona Comment[sq]=Menaxhoni parametrat e bllokimit të ekranit Comment[sr]=Управљање поставкама закључавања екрана Comment[sr@Latn]=Управљање поставкама закључавања екрана Comment[sr@ije]=Управљање поставкама закључавања екрана Comment[sr@ijekavianlatin]=Управљање поставкама закључавања екрана Comment[sr@latin]=Управљање поставкама закључавања екрана Comment[sr_RS]=Управљање поставкама закључавања екрана Comment[sv]=Hantera skärmlåsinställningar Comment[sv_SE]=Hantera skärmlåsinställningar Comment[tg]=Идоракунии танзимоти қулфи экран Comment[th]=จัดการการตั้งค่าการล็อกหน้าจอ Comment[th_TH]=จัดการการตั้งค่าการล็อกหน้าจอ Comment[tr]=Ekran kilidi seçeneklerini yönet Comment[uk]=Керування налаштуваннями блокування екрану Comment[uk_UA]=Керування налаштуваннями блокування екрану Comment[ur]=سکرین قفل کی ترتیبات کا انتظام Comment[ur_PK]=سکرین قفل کی ترتیبات کا انتظام Comment[vi]=QUản lý thiết đặt khoá màn hình Comment[vi_VN]=QUản lý thiết đặt khoá màn hình Comment[zh_CN]=管理锁屏设置 Comment[zh_HK]=管理螢幕鎖定設定 Comment[zh_TW]=管理螢幕鎖定設定 Cinnamon-2.8.6/files/usr/share/applications/cinnamon-killer-daemon.desktop0000664000175000017500000000034212625666430025647 0ustar fabiofabio[Desktop Entry] Name=Cinnamon Killer Daemon Comment=Restarts Cinnamon, Nemo and cinnamon-settings-daemon when a key combination is pressed Icon=reload Exec=cinnamon-killer-daemon Terminal=false Type=Application NoDisplay=true Cinnamon-2.8.6/files/usr/share/cinnamon/0000775000175000017500000000000012625666430017046 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/0000775000175000017500000000000012625666430020516 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/settings@cinnamon.org/0000775000175000017500000000000012625666430024767 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/settings@cinnamon.org/metadata.json0000664000175000017500000000023312625666430027440 0ustar fabiofabio{ "uuid": "settings@cinnamon.org", "name": "Settings Applet", "description": "Cinnamon Settings", "icon": "preferences-system", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js0000664000175000017500000000327412625666430026620 0ustar fabiofabioconst Lang = imports.lang; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Applet = imports.ui.applet; const Panel = imports.ui.panel; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const Util = imports.misc.util; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; function ConfirmDialog(){ this._init(); } function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("go-up"); this.set_applet_tooltip(_("Settings")); this.menuManager = new PopupMenu.PopupMenuManager(this); this._buildMenu(orientation); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _buildMenu: function(orientation) { this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); Panel.populateSettingsMenu(this.menu); }, on_orientation_changed: function(orientation){ this.menu.destroy(); this._buildMenu(orientation); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/settings-example@cinnamon.org/0000775000175000017500000000000012625666430026420 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/settings-example@cinnamon.org/settings-schema.json0000664000175000017500000000562012625666430032414 0ustar fabiofabio{ "head" : { "type" : "header", "description" : "First Section" }, "icon-name": { "type": "iconfilechooser", "default" : "help", "description" : "The icon to use in the panel:", "tooltip" : "Select an icon file, or you can type an icon name into the entry box" }, "color" : { "type": "colorchooser", "default" : "grey", "description" : "The background color for the applet:", "tooltip" : "Click the button to select a new background color for this applet" }, "spinner-number" : { "type": "spinbutton", "default" : 250, "min" : 0, "max" : 400, "step" : 5, "units" : "pixels", "description" : "Applet width in the panel:", "tooltip" : "Increase or decrease this spinner value to make the applet wider or narrower in the panel" }, "combo-selection" : { "type": "combobox", "default" : "option 1", "description" : "Please pick an option:", "options" : { "First Choice" : "option 1", "Second Choice" : "option 2", "Third Choice" : "option 3", "Fourth Choice" : "option 4" }, "tooltip" : "A combo selection can select between int, boolean, float, and string values" }, "scale-demo" : { "type" : "scale", "default" : 0.5, "min" : 0.0, "max" : 1.0, "step" : 0.05, "description" : "Here is a demonstration of bidirectional control:", "tooltip" : "If you adjust this scale, it updates in the applet. If you adjust the scale in the applet, it updates here" }, "div1" : { "type" : "separator" }, "head1" : { "type" : "header", "description" : "Here's a new section" }, "use-custom-label" : { "type" : "checkbox", "default" : false, "description": "Check this to use a custom label:", "tooltip": "Checking this box allows you to set a custom label in the field below - this demonstrates setting dependencies" }, "custom-label" : { "type" : "entry", "default" : "custom", "description" : "Custom Applet Label:", "indent": true, "dependency" : "use-custom-label", "tooltip" : "Set your custom label here. This field is unavailable unless the checkbox above is enabled" }, "tween-function" : { "type" : "tween", "default" : "EaseNone", "description" : "Animation type", "tooltip": "A tween chooser has as value a string, which you can pass as transition to Tweener.addTween()" }, "demo-button" : { "type" : "button", "description" : "Don't Press This Button!!!", "callback" : "on_config_button_pressed", "tooltip" : "Pressing this button activates a callback method in the applet - go ahead, press it." }, "signal-test" : { "type" : "checkbox", "description" : "This is a test widget for individual key signal firing", "default" : false }, "keybinding-test" : { "type" : "keybinding", "description" : "Set the keybinding here to activate an action: ", "default" : "F8" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/settings-example@cinnamon.org/metadata.json0000664000175000017500000000034212625666430031072 0ustar fabiofabio{ "uuid": "settings-example@cinnamon.org", "name": "Settings API Demo", "description": "A sample applet for demonstrating the capabilities and usage of the applet and desklet settings API", "icon": "preferences-desktop" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/settings-example@cinnamon.org/applet.js0000664000175000017500000002040612625666430030245 0ustar fabiofabioconst Applet = imports.ui.applet; const Lang = imports.lang; const PopupMenu = imports.ui.popupMenu; const Mainloop = imports.mainloop; const Settings = imports.ui.settings; // Needed for settings API const Gio = imports.gi.Gio; const Tweener = imports.ui.tweener; const Main = imports.ui.main; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); // Be sure to pass instanceId from the main function } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); /* Initialize your settings handler instance this, the uuid instance id */ this.settings = new Settings.AppletSettings(this, "settings-example@cinnamon.org", instance_id); /* Now we'll proceed with setting up individual setting bindings. */ this.settings.bindProperty(Settings.BindingDirection.IN, // The binding direction - IN means we only listen for changes from this applet "icon-name", // The setting key, from the setting schema file "icon_name", // The property to bind the setting to - in this case it will initialize this.icon_name to the setting value this.on_settings_changed, // The method to call when this.icon_name has changed, so you can update your applet null); // Any extra information you want to pass to the callback (optional - pass null or just leave out this last argument) this.settings.bindProperty(Settings.BindingDirection.IN, "color", "bg_color", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "spinner-number", "spinner_number", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "combo-selection", "combo_choice", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.BIDIRECTIONAL, // BIDIRECTIONAL means the applet will listen "scale-demo", // for changes to the stored setting, AND the "scale_val", // settings daemon will listen for changes made this.on_settings_changed, // to this.scale_val by the APPLET null); this.settings.bindProperty(Settings.BindingDirection.IN, "use-custom-label", "use_custom", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "custom-label", "custom_label", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "tween-function", "tween_function", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "keybinding-test", "keybinding", this.on_keybinding_changed, null); this.settings.connect("changed::signal-test", Lang.bind(this, this.on_signal_test_fired)); /* Lets create and add our menu items - we'll set their true values after */ this.spinner_val_demo = new PopupMenu.PopupMenuItem(""); this.combo_val_demo = new PopupMenu.PopupMenuItem(""); this.slider_demo = new PopupMenu.PopupSliderMenuItem(0); this.slider_demo.connect("value-changed", Lang.bind(this, this.on_slider_changed)); this.menu.addMenuItem(this.spinner_val_demo); this.menu.addMenuItem(this.combo_val_demo); this.menu.addMenuItem(this.slider_demo); /* Let's set up our applet's initial state now that we have our setting properties defined */ this.on_keybinding_changed(); this.on_settings_changed(); }, on_keybinding_changed: function() { Main.keybindingManager.addHotKey("must-be-unique-id", this.keybinding, Lang.bind(this, this.on_hotkey_triggered)); }, on_settings_changed: function() { if (this.use_custom) { this.set_applet_label(this.custom_label) } else { this.set_applet_label(_("Hi there!")); } let icon_file = Gio.File.new_for_path(this.icon_name); if (icon_file.query_exists(null)) { this.set_applet_icon_path(this.icon_name) } else { this.set_applet_icon_name(this.icon_name) } this.spinner_val_demo.label.clutter_text.set_text("Spinner value is: " + this.spinner_number); this.combo_val_demo.label.clutter_text.set_text("Combo value is: " + this.combo_choice); this.slider_demo.setValue(this.scale_val); this.actor.style = "background-color:" + this.bg_color + "; width:" + this.spinner_number + "px"; }, on_signal_test_fired: function(setting_prov, key, oldval, newval) { global.logError("Test signal fired. Old value for key "+ key + " was " + oldval + ". New value is " + newval + "."); }, on_slider_changed: function(slider, value) { this.scale_val = value; // This is our BIDIRECTIONAL setting - by updating this.scale_val, // Our configuration file will also be updated }, /* This method is a callback that is defined by a button type in our settings file * This button will appear in the configuration dialog, and pressing it will call this method * This could useful to open a link to your web page, or just about anything you want */ on_config_button_pressed: function() { this.set_applet_label(_("YOU PRESSED THE BUTTON!!!")); let timeoutId = Mainloop.timeout_add(3000, Lang.bind(this, function() { this.on_settings_changed(); })); //animate icon Tweener.addTween(this._applet_icon, { margin_left: 10, time: .5, transition: this.tween_function, onComplete: function(){ Tweener.addTween(this._applet_icon, { margin_left: 0, time: .5, transition: this.tween_function }); }, onCompleteScope: this }); }, on_hotkey_triggered: function() { this.set_applet_label(_("YOU USED THE HOTKEY!!!")); let timeoutId = Mainloop.timeout_add(3000, Lang.bind(this, function() { this.on_settings_changed(); })); }, on_applet_clicked: function(event) { this.menu.toggle(); }, on_applet_removed_from_panel: function() { this.settings.finalize(); // This is called when a user removes the applet from the panel.. we want to // Remove any connections and file listeners here, which our settings object // has a few of } }; function main(metadata, orientation, panel_height, instance_id) { // Make sure you collect and pass on instanceId let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/separator@cinnamon.org/0000775000175000017500000000000012625666430025127 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/separator@cinnamon.org/metadata.json0000664000175000017500000000027212625666430027603 0ustar fabiofabio{ "uuid": "separator@cinnamon.org", "name": "Separator", "description": "A separator icon which can be added multiple times", "icon": "gnome-panel-separator", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/separator@cinnamon.org/applet.js0000664000175000017500000000146712625666430026762 0ustar fabiofabioconst Applet = imports.ui.applet; const St = imports.gi.St; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id); this.actor.style_class = 'applet-separator'; this._line = new St.BoxLayout({ style_class: 'applet-separator-line', reactive: false, track_hover: false }); this.actor.add(this._line, { y_align: St.Align.MIDDLE, y_fill: true }); }, }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/calendar@cinnamon.org/0000775000175000017500000000000012625666430024700 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/calendar@cinnamon.org/settings-schema.json0000664000175000017500000000166112625666430030675 0ustar fabiofabio{ "show-week-numbers" : { "type" : "checkbox", "default" : false, "description": "Show week numbers in calendar", "tooltip": "Check this to show week numbers in the calendar." }, "use-custom-format" : { "type" : "checkbox", "default" : false, "description": "Use a custom date format", "tooltip": "Check this to define a custom format for the date in the calendar applet." }, "custom-format" : { "type" : "entry", "default" : "%A %B %e, %H:%M", "description" : "Date format:", "indent": true, "dependency" : "use-custom-format", "tooltip" : "Set your custom format here." }, "format-button" : { "type" : "button", "description" : "Show information on date format syntax", "indent": true, "dependency" : "use-custom-format", "callback" : "on_custom_format_button_pressed", "tooltip" : "Click this button to know more about the syntax for date formats." } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/calendar@cinnamon.org/calendar.js0000664000175000017500000003506612625666430027021 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const Lang = imports.lang; const St = imports.gi.St; const Signals = imports.signals; const Pango = imports.gi.Pango; const Gettext_gtk30 = imports.gettext.domain('gtk30'); const Cinnamon = imports.gi.Cinnamon; const Settings = imports.ui.settings; const MSECS_IN_DAY = 24 * 60 * 60 * 1000; const WEEKDATE_HEADER_WIDTH_DIGITS = 3; const SHOW_WEEKDATE_KEY = 'show-week-numbers'; const FIRST_WEEKDAY_KEY = 'first-day-of-week'; const DESKTOP_SCHEMA = 'org.cinnamon.desktop.interface'; // in org.cinnamon.desktop.interface const CLOCK_FORMAT_KEY = 'clock-format'; function _sameDay(dateA, dateB) { return (dateA.getDate() == dateB.getDate() && dateA.getMonth() == dateB.getMonth() && dateA.getYear() == dateB.getYear()); } function _sameYear(dateA, dateB) { return (dateA.getYear() == dateB.getYear()); } /* TODO: maybe needs config - right now we assume that Saturday and * Sunday are non-work days (not true in e.g. Israel, it's Sunday and * Monday there) */ function _isWorkDay(date) { return date.getDay() != 0 && date.getDay() != 6; } function _getBeginningOfDay(date) { let ret = new Date(date.getTime()); ret.setHours(0); ret.setMinutes(0); ret.setSeconds(0); ret.setMilliseconds(0); return ret; } function _getEndOfDay(date) { let ret = new Date(date.getTime()); ret.setHours(23); ret.setMinutes(59); ret.setSeconds(59); ret.setMilliseconds(999); return ret; } function _formatEventTime(event, clockFormat) { let ret; if (event.allDay) { /* Translators: Shown in calendar event list for all day events * Keep it short, best if you can use less then 10 characters */ ret = C_("event list time", "All Day"); } else { switch (clockFormat) { case '24h': /* Translators: Shown in calendar event list, if 24h format */ ret = event.date.toLocaleFormat(C_("event list time", "%H:%M")); break; default: /* explicit fall-through */ case '12h': /* Transators: Shown in calendar event list, if 12h format */ ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p")); break; } } return ret; } function _getDigitWidth(actor){ let context = actor.get_pango_context(); let themeNode = actor.get_theme_node(); let font = themeNode.get_font(); let metrics = context.get_metrics(font, context.get_language()); let width = metrics.get_approximate_digit_width(); return width; } function _getCalendarDayAbbreviation(dayNumber) { // This returns an array of abbreviated day names, starting with Sunday. // We use 2014/03/02 (months are zero-based in JS) because it was a Sunday let abbreviations = [ new Date(2014, 2, 2).toLocaleFormat('%a'), new Date(2014, 2, 3).toLocaleFormat('%a'), new Date(2014, 2, 4).toLocaleFormat('%a'), new Date(2014, 2, 5).toLocaleFormat('%a'), new Date(2014, 2, 6).toLocaleFormat('%a'), new Date(2014, 2, 7).toLocaleFormat('%a'), new Date(2014, 2, 8).toLocaleFormat('%a') ]; return abbreviations[dayNumber]; } // Abstraction for an appointment/event in a calendar function CalendarEvent(date, end, summary, allDay) { this._init(date, end, summary, allDay); } CalendarEvent.prototype = { _init: function(date, end, summary, allDay) { this.date = date; this.end = end; this.summary = summary; this.allDay = allDay; } }; function _datesEqual(a, b) { if (a < b) return false; else if (a > b) return false; return true; } function _dateIntervalsOverlap(a0, a1, b0, b1) { if (a1 <= b0) return false; else if (b1 <= a0) return false; else return true; } function Calendar(settings) { this._init(settings); } Calendar.prototype = { _init: function(settings) { this._weekStart = Cinnamon.util_get_week_start(); this._weekdate = NaN; this._digitWidth = NaN; this.settings = settings; this.settings.connect("changed::show-week-numbers", Lang.bind(this, this._onSettingsChange)); this.desktop_settings = new Gio.Settings({ schema_id: DESKTOP_SCHEMA }); this.desktop_settings.connect("changed::" + FIRST_WEEKDAY_KEY, Lang.bind(this, this._onSettingsChange)); this.show_week_numbers = this.settings.getValue("show-week-numbers"); // Find the ordering for month/year in the calendar heading let var_name = 'calendar:MY'; switch (Gettext_gtk30.gettext(var_name)) { case 'calendar:MY': this._headerMonthFirst = true; break; case 'calendar:YM': this._headerMonthFirst = false; break; default: log('Translation of "calendar:MY" in GTK+ is not correct'); this._headerMonthFirst = true; break; } // Start off with the current date this._selectedDate = new Date(); this.actor = new St.Table({ homogeneous: false, style_class: 'calendar', reactive: true }); this.actor.connect('scroll-event', Lang.bind(this, this._onScroll)); this._buildHeader (); }, _onSettingsChange: function(object, key, old_val, new_val) { switch (key) { case SHOW_WEEKDATE_KEY: this.show_week_numbers = new_val; break; case FIRST_WEEKDAY_KEY: this._weekStart = Cinnamon.util_get_week_start(); break; } this._buildHeader(); this._update(false); }, // Sets the calendar to show a specific date setDate: function(date, forceReload) { if (!_sameDay(date, this._selectedDate)) { this._selectedDate = date; this._update(forceReload); this.emit('selected-date-changed', new Date(this._selectedDate)); } else { if (forceReload) this._update(forceReload); } }, _buildHeader: function() { let offsetCols = this.show_week_numbers ? 1 : 0; this.actor.destroy_children(); // Top line of the calendar '<| September |> <| 2009 |>' this._topBoxMonth = new St.BoxLayout(); this._topBoxYear = new St.BoxLayout(); if (this._headerMonthFirst) { this.actor.add(this._topBoxMonth, {row: 0, col: 0, col_span: offsetCols + 4}); this.actor.add(this._topBoxYear, {row: 0, col: offsetCols + 4, col_span: 3}); } else { this.actor.add(this._topBoxMonth, {row: 0, col: offsetCols + 3, col_span: 4}); this.actor.add(this._topBoxYear, {row: 0, col: 0, col_span: offsetCols + 3}); } this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange)); let back = new St.Button({ style_class: 'calendar-change-month-back' }); this._topBoxMonth.add(back); back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked)); this._monthLabel = new St.Label({style_class: 'calendar-month-label'}); this._topBoxMonth.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE }); let forward = new St.Button({ style_class: 'calendar-change-month-forward' }); this._topBoxMonth.add(forward); forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked)); back = new St.Button({style_class: 'calendar-change-month-back'}); this._topBoxYear.add(back); back.connect('clicked', Lang.bind(this, this._onPrevYearButtonClicked)); this._yearLabel = new St.Label({style_class: 'calendar-month-label'}); this._topBoxYear.add(this._yearLabel, {expand: true, x_fill: false, x_align: St.Align.MIDDLE}); forward = new St.Button({style_class: 'calendar-change-month-forward'}); this._topBoxYear.add(forward); forward.connect('clicked', Lang.bind(this, this._onNextYearButtonClicked)); // Add weekday labels... // // We need to figure out the abbreviated localized names for the days of the week; // we do this by just getting the next 7 days starting from right now and then putting // them in the right cell in the table. It doesn't matter if we add them in order let iter = new Date(this._selectedDate); iter.setSeconds(0); // Leap second protection. Hah! iter.setHours(12); for (let i = 0; i < 7; i++) { // Could use iter.toLocaleFormat('%a') but that normally gives three characters // and we want, ideally, a single character for e.g. S M T W T F S let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay()); let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading', text: customDayAbbrev }); this.actor.add(label, { row: 1, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7, x_fill: false, x_align: St.Align.MIDDLE }); iter.setTime(iter.getTime() + MSECS_IN_DAY); } // All the children after this are days, and get removed when we update the calendar this._firstDayIndex = this.actor.get_children().length; }, _onStyleChange: function(actor, event) { // width of a digit in pango units this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE; this._setWeekdateHeaderWidth(); }, _setWeekdateHeaderWidth: function() { if (this.digitWidth != NaN && this.show_week_numbers && this._weekdateHeader) { this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS); } }, _onScroll : function(actor, event) { switch (event.get_scroll_direction()) { case Clutter.ScrollDirection.UP: case Clutter.ScrollDirection.LEFT: this._onPrevMonthButtonClicked(); break; case Clutter.ScrollDirection.DOWN: case Clutter.ScrollDirection.RIGHT: this._onNextMonthButtonClicked(); break; } }, _applyDateBrowseAction: function(yearChange, monthChange) { let oldDate = this._selectedDate; let newMonth = oldDate.getMonth() + monthChange; if (newMonth> 11) { yearChange = yearChange + 1; newMonth = 0; } else if (newMonth < 0) { yearChange = yearChange - 1; newMonth = 11; } let newYear = oldDate.getFullYear() + yearChange; let newDayOfMonth = oldDate.getDate(); let daysInMonth = 32 - new Date(newYear, newMonth, 32).getDate(); if (newDayOfMonth > daysInMonth) { newDayOfMonth = daysInMonth; } let newDate = new Date(); newDate.setFullYear(newYear, newMonth, newDayOfMonth); this.setDate(newDate, false); }, _onPrevYearButtonClicked: function() { this._applyDateBrowseAction(-1, 0); }, _onNextYearButtonClicked: function() { this._applyDateBrowseAction(+1, 0); }, _onPrevMonthButtonClicked: function() { this._applyDateBrowseAction(0, -1); }, _onNextMonthButtonClicked: function() { this._applyDateBrowseAction(0, +1); }, _update: function(forceReload) { let now = new Date(); this._monthLabel.text = this._selectedDate.toLocaleFormat('%B').capitalize(); this._yearLabel.text = this._selectedDate.toLocaleFormat('%Y'); // Remove everything but the topBox and the weekday labels let children = this.actor.get_children(); for (let i = this._firstDayIndex; i < children.length; i++) children[i].destroy(); // Start at the beginning of the week before the start of the month let beginDate = new Date(this._selectedDate); beginDate.setDate(1); beginDate.setSeconds(0); beginDate.setHours(12); let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7; beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY); let iter = new Date(beginDate); let row = 2; while (true) { let button = new St.Button({ label: iter.getDate().toString() }); button.reactive = false; let iterStr = iter.toUTCString(); button.connect('clicked', Lang.bind(this, function() { let newlySelectedDate = new Date(iterStr); this.setDate(newlySelectedDate, false); })); let styleClass = 'calendar-day-base calendar-day'; if (_isWorkDay(iter)) styleClass += ' calendar-work-day' else styleClass += ' calendar-nonwork-day' // Hack used in lieu of border-collapse - see cinnamon.css if (row == 2) styleClass = 'calendar-day-top ' + styleClass; if (iter.getDay() == this._weekStart) styleClass = 'calendar-day-left ' + styleClass; if (_sameDay(now, iter)) styleClass += ' calendar-today'; else if (iter.getMonth() != this._selectedDate.getMonth()) styleClass += ' calendar-other-month-day'; if (_sameDay(this._selectedDate, iter)) button.add_style_pseudo_class('active'); button.style_class = styleClass; let offsetCols = this.show_week_numbers ? 1 : 0; this.actor.add(button, { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 }); if (this.show_week_numbers && iter.getDay() == 4) { let label = new St.Label({ text: iter.toLocaleFormat('%V'), style_class: 'calendar-day-base calendar-week-number'}); this.actor.add(label, { row: row, col: 0, y_align: St.Align.MIDDLE }); } iter.setTime(iter.getTime() + MSECS_IN_DAY); if (iter.getDay() == this._weekStart) { row++; // We always stop after placing 6 rows, even if month fits in 4 // to prevent issues with jumping controls, see #226 if (row > 7) { break; } } } } }; Signals.addSignalMethods(Calendar.prototype); Cinnamon-2.8.6/files/usr/share/cinnamon/applets/calendar@cinnamon.org/metadata.json0000664000175000017500000000023312625666430027351 0ustar fabiofabio{ "uuid": "calendar@cinnamon.org", "name": "Calendar", "description": "The Cinnamon calendar applet", "icon": "stock_calendar", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js0000664000175000017500000002120112625666430026517 0ustar fabiofabioconst Applet = imports.ui.applet; const Gio = imports.gi.Gio; const Lang = imports.lang; const Mainloop = imports.mainloop; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Util = imports.misc.util; const PopupMenu = imports.ui.popupMenu; const UPowerGlib = imports.gi.UPowerGlib; const Settings = imports.ui.settings; const Calendar = imports.applet.calendar; const CinnamonDesktop = imports.gi.CinnamonDesktop; String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); } function _onVertSepRepaint (area) { let cr = area.get_context(); let themeNode = area.get_theme_node(); let [width, height] = area.get_surface_size(); let stippleColor = themeNode.get_color('-stipple-color'); let stippleWidth = themeNode.get_length('-stipple-width'); let x = Math.floor(width/2) + 0.5; cr.moveTo(x, 0); cr.lineTo(x, height); Clutter.cairo_set_source_color(cr, stippleColor); cr.setDash([1, 3], 1); // Hard-code for now cr.setLineWidth(stippleWidth); cr.stroke(); cr.$dispose(); }; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.TextApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.TextApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.clock = new CinnamonDesktop.WallClock(); this.settings = new Settings.AppletSettings(this, "calendar@cinnamon.org", this.instance_id); this.menuManager = new PopupMenu.PopupMenuManager(this); this._orientation = orientation; this._initContextMenu(); this._calendarArea = new St.BoxLayout({name: 'calendarArea' }); this.menu.addActor(this._calendarArea); // Fill up the first column let vbox = new St.BoxLayout({vertical: true}); this._calendarArea.add(vbox); // Date this._date = new St.Label(); this._date.style_class = 'datemenu-date-label'; vbox.add(this._date); this._eventList = null; // Calendar this._calendar = new Calendar.Calendar(this.settings); vbox.add(this._calendar.actor); let item = new PopupMenu.PopupMenuItem(_("Date and Time Settings")) item.connect("activate", Lang.bind(this, this._onLaunchSettings)); //this.menu.addMenuItem(item); if (item) { let separator = new PopupMenu.PopupSeparatorMenuItem(); separator.setColumnWidths(1); vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false}); item.actor.can_focus = false; global.reparentActor(item.actor, vbox); } // Track changes to clock settings this._dateFormatFull = _("%A %B %e, %Y"); this.settings.bindProperty(Settings.BindingDirection.IN, "use-custom-format", "use_custom_format", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "custom-format", "custom_format", this.on_settings_changed, null); // Track changes to date&time settings this.datetime_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.interface" }); this.datetime_settings.connect('changed::clock-show-seconds', Lang.bind(this, this.on_settings_changed)); this.datetime_settings.connect('changed::clock-use-24h', Lang.bind(this, this.on_settings_changed)); this.datetime_settings.connect('changed::clock-show-date', Lang.bind(this, this.on_settings_changed)); // https://bugzilla.gnome.org/show_bug.cgi?id=655129 this._upClient = new UPowerGlib.Client(); try { this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate)); } catch (e) { this._upClient.connect('notify::resume', Lang.bind(this, this._updateClockAndDate)); } // Start the clock this.on_settings_changed(); this._updateClockAndDatePeriodic(); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, on_settings_changed: function() { if (this._periodicTimeoutId){ Mainloop.source_remove(this._periodicTimeoutId); } this._updateClockAndDatePeriodic(); }, on_custom_format_button_pressed: function() { Util.spawnCommandLine("xdg-open http://www.foragoodstrftime.com/"); }, _onLaunchSettings: function() { this.menu.close(); Util.spawnCommandLine("cinnamon-settings calendar"); }, _updateClockAndDate: function() { let now = new Date(); let nextUpdate = 60 - now.getSeconds() + 1; // Applet label if (this.use_custom_format) { let label_string = now.toLocaleFormat(this.custom_format); if (!label_string) { global.logError("Calendar applet: bad time format string - check your string."); label_string = "~CLOCK FORMAT ERROR~ " + now.toLocaleFormat("%l:%M %p"); } this.set_applet_label(label_string); if(this.custom_format.search("%S") > 0 || this.custom_format.search("%c") > 0 || this.custom_format.search("%T") > 0 || this.custom_format.search("%X") > 0) { nextUpdate = 1; } } else { if (this.clock) { // We lose cinnamon-desktop temporarily during suspend let label_string = this.clock.get_clock().capitalize(); this.set_applet_label(label_string); if(this.datetime_settings.get_boolean("clock-show-seconds")) { nextUpdate = 1; } } } // Applet content let dateFormattedFull = now.toLocaleFormat(this._dateFormatFull).capitalize(); if (dateFormattedFull !== this._lastDateFormattedFull) { this._date.set_text(dateFormattedFull); this.set_applet_tooltip(dateFormattedFull); this._lastDateFormattedFull = dateFormattedFull; } return nextUpdate; }, _updateClockAndDatePeriodic: function() { let nextUpdate = this._updateClockAndDate(); this._periodicTimeoutId = Mainloop.timeout_add_seconds(nextUpdate, Lang.bind(this, this._updateClockAndDatePeriodic)); }, on_applet_removed_from_panel: function() { if (this._periodicTimeoutId){ Mainloop.source_remove(this._periodicTimeoutId); } }, _initContextMenu: function () { if (this._calendarArea) this._calendarArea.unparent(); if (this.menu) this.menuManager.removeMenu(this.menu); this.menu = new Applet.AppletPopupMenu(this, this._orientation); this.menuManager.addMenu(this.menu); if (this._calendarArea){ this.menu.addActor(this._calendarArea); this._calendarArea.show_all(); } // Whenever the menu is opened, select today this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { if (isOpen) { let now = new Date(); /* Passing true to setDate() forces events to be reloaded. We * want this behavior, because * * o It will cause activation of the calendar server which is * useful if it has crashed * * o It will cause the calendar server to reload events which * is useful if dynamic updates are not supported or not * properly working * * Since this only happens when the menu is opened, the cost * isn't very big. */ this._calendar.setDate(now, true); // No need to update this._eventList as ::selected-date-changed // signal will fire } })); }, on_orientation_changed: function (orientation) { this._orientation = orientation; this._initContextMenu(); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/0000775000175000017500000000000012625666430024216 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/settings-schema.json0000664000175000017500000000017512625666430030212 0ustar fabiofabio{ "activate-on-hover": { "type": "checkbox", "default" : false, "description" : "Activate scale on hover" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/icons/0000775000175000017500000000000012625666430025331 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/icons/cinnamon-scale-symbolic.svg0000664000175000017500000000105712625666430032563 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/icon.png0000664000175000017500000000314712625666430025661 0ustar fabiofabioPNG  IHDR szzsRGBbKGD pHYs B(xtIME 7pIDATXŗMl\Wޛ38MF%@(٠J@)*AbB@,, X EP,"RBJ&2J:u3c{X$ِ{f{2*(hc r翖$IQtZKKK?9{wA̬C"9==Yk.oti%yC✫$@7nҥH\&.g-'Z`ys ݹ?8p`@Wy$CJ᱇>SOϧ"-""}ku o"BO.yۜ|0=>SMbz>ebu֮kd`7?mO?|n}_iGU6h<ڼDa nGQ1B M] 2 "Xkl L9w駿Wroc g-'Zf`24@F`9k,[wmvƦ?gx%4(j<(@q.((N73'xI!m"gsbMA5`[8g\uk-Z&̯.}jqiq5|tPIjj@PEU0=`Lg9s8\DTF\xq9|鹏U&'j`L`LA_!܈,;$ "ɱ $5\ͫ\m%l;`5Ja;ddwCsqO˅oK6Ss|E4#Cz={uMG)HSŧJо55F}` Y=a\V__%vyaxb?qu^u 롩Q!: Z^ H*3ӓ Mt% {I{rL=I۝¤(;"c LD iREC(狵r' ں̓v9aǴZMODgZ$edz5R,AɁSOfVI%iolﺊX ϋ{4WB~ܟhPh,Hb^SBo퓢+V7vVܹs1i6m`eqq/^<522e;b+n4׀<'S@ Ld O@s>`IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/metadata.json0000664000175000017500000000020012625666430026661 0ustar fabiofabio{ "uuid": "scale@cinnamon.org", "name": "Scale", "description": "Click or hover to activate Scale view", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js0000664000175000017500000000342612625666430026046 0ustar fabiofabioconst Applet = imports.ui.applet; const Lang = imports.lang; const Main = imports.ui.main; const Settings = imports.ui.settings; function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("cinnamon-scale"); this.set_applet_tooltip(_("Scale")); this._hover_activates = false; this.settings = new Settings.AppletSettings(this, metadata["uuid"], this.instance_id); this.settings.bindProperty(Settings.BindingDirection.IN, "activate-on-hover", "_hover_activates", function () {}); this.actor.connect('enter-event', Lang.bind(this, this._onEntered)); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { if (this._hover_activates) return; this.doAction(); }, _onEntered: function(event) { if (!this._hover_activates || global.settings.get_boolean("panel-edit-mode")) return; this.doAction(); }, doAction: function() { if (!Main.overview.animationInProgress) Main.overview.toggle(); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/window-list@cinnamon.org/0000775000175000017500000000000012625666430025407 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/window-list@cinnamon.org/settings-schema.json0000664000175000017500000000171112625666430031400 0ustar fabiofabio{ "enable-alerts" : { "type" : "checkbox", "default" : true, "description" : "Show an alert in the window list when a window from another workspace requires attention" }, "enable-scrolling" : { "type" : "checkbox", "default" : false, "description" : "Enable mouse-wheel scrolling in the window list" }, "reverse-scrolling" : { "type" : "checkbox", "default" : false, "description" : "Reverse the direction of mouse-wheel scrolling in the window list" }, "middle-click-close": { "type": "checkbox", "default": true, "description": "Middle click to close window" }, "buttons-use-entire-space": { "type": "checkbox", "default": false, "description": "Window buttons can have different sizes and use the entire space available" }, "window-preview": { "type": "checkbox", "default": true, "description": "Show window thumbnails on hover" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/window-list@cinnamon.org/metadata.json0000664000175000017500000000024712625666430030065 0ustar fabiofabio{ "uuid": "window-list@cinnamon.org", "name": "Window list", "description": "Main Cinnamon window list", "icon": "gnome-panel-window-list", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js0000664000175000017500000012400412625666430027233 0ustar fabiofabio/* Window-list applet * * The applet code consists of four main object. WindowPreview, AppMenuButton, * AppMenuButtonRightClickMenu and the main applet code. * * The main applet object listens to different events and updates the window * list accordingly. Since addition/removal of windows is emitted by the * workspace, we have to listen to the changes to the number of workspaces and * update our signals accordingly as well. It also listens to the change in * window state (eg tile/maximize) since the window titles are displayed * differently in the AppMenuButton for each different state, eg minimized * windows are shown as [Title]. * * For each window the main applet object wants to show, an AppMenuButton is * created. This is created for every window in the monitors it is responsible * for, regardless of whether it is on the active workspace. Individual applet * objects are then shown/hidden according to which workspace they are in. * * The AppMenuButton is responsible for managing its own appearance using a * CinnamonGenericContainer. We manage the allocation ourselves and shrink the * label when there isn't enough space in the panel (the space available is * divided among all AppMenuButtons and each is told how much space they can * use through the "allocate" signal). It also has an onFocus function that the * main applet calls when a window is focused. * * When a window is marked urgent or demand attention ("urgent" windows are not * more important that those demanding attention. These are two unrelated * notions in different specifications that both mean approximately the same * thing), we will ask the AppMenuButton to flash. If the window is from a * separate workspace, we generate a new temporary AppMenuButton and add it to * our actor box. It stops flashing when the onFocus function is called (and if * the window is indeed focused) (destroyed in the case of temporary * AppMenuButtons). * * The AppMenuButtonRightClickMenu is, as the name suggests, the right click * menu of the AppMenuButton. The menu is generated every time the button is * right-clicked, since this rarely happens and generating all at the beginning * would be a waste of time/memory. This also saves us from having to listen to * signals for changes in workspace/monitor etc. * * Finally, the WindowPreview object is a tooltip that shows a preview of the * window. Users can opt to show a window preview (using this), or the title * (using Tooltips.PanelItemTooltip) in the tooltip. The window preview is * generated on the fly when needed instead of cached. */ const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const Gdk = imports.gi.Gdk; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const St = imports.gi.St; const Applet = imports.ui.applet; const DND = imports.ui.dnd; const Main = imports.ui.main; const Panel = imports.ui.panel; const PopupMenu = imports.ui.popupMenu; const Settings = imports.ui.settings; const SignalManager = imports.misc.signalManager; const Tooltips = imports.ui.tooltips; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const DEFAULT_ICON_SIZE = 16; // too bad this can't be defined in theme (cinnamon-app.create_icon_texture returns a clutter actor, not a themable object - // probably something that could be addressed const ICON_HEIGHT_FACTOR = .64; const MAX_TEXT_LENGTH = 1000; const FLASH_INTERVAL = 500; const WINDOW_PREVIEW_WIDTH = 200; const WINDOW_PREVIEW_HEIGHT = 150; function WindowPreview(item, metaWindow) { this._init(item, metaWindow); } WindowPreview.prototype = { __proto__: Tooltips.TooltipBase.prototype, _init: function(item, metaWindow) { Tooltips.TooltipBase.prototype._init.call(this, item.actor); this._applet = item._applet; this.actor = new St.Bin({style_class: "switcher-list", style: "margin: 0px; padding: 8px;"}); this.actor.show_on_set_parent = false; this.scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this.actor.set_size(WINDOW_PREVIEW_WIDTH * 1.3 * this.scaleFactor, WINDOW_PREVIEW_HEIGHT * 1.3 * this.scaleFactor); Main.uiGroup.add_actor(this.actor); this.metaWindow = metaWindow; let box = new St.BoxLayout({ vertical: true }); let hbox = new St.BoxLayout(); let iconBox = new St.Bin(); let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_window_app(this.metaWindow); let icon = app ? app.create_icon_texture(16) : new St.Icon({ icon_name: 'application-default-icon', icon_type: St.IconType.FULLCOLOR, icon_size: 16 }); iconBox.set_child(icon); hbox.add_actor(iconBox); let label = new St.Label(); label.set_text(this.metaWindow.get_title()); label.style = "padding: 2px;"; hbox.add_actor(label); box.add_actor(hbox); this.thumbnailBin = new St.Bin(); box.add_actor(this.thumbnailBin); this.actor.set_child(box); }, _onEnterEvent: function(actor, event) { if (this._applet._tooltipShowing) this.show(); else if (!this._showTimer) this._showTimer = Mainloop.timeout_add(300, Lang.bind(this, this._onTimerComplete)); this.mousePosition = event.get_coords(); }, _hide: function(actor, event) { Tooltips.TooltipBase.prototype._hide.call(this, actor, event); this._applet.erodeTooltip(); }, show: function() { if (!this.actor) return let muffinWindow = this.metaWindow.get_compositor_private(); let windowTexture = muffinWindow.get_texture(); let [width, height] = windowTexture.get_size(); let scale = Math.min(1.0, WINDOW_PREVIEW_WIDTH / width, WINDOW_PREVIEW_HEIGHT / height); if (this.thumbnail) { this.thumbnailBin.set_child(null); this.thumbnail.destroy(); } this.thumbnail = new Clutter.Clone({ source: windowTexture, width: width * scale * this.scaleFactor, height: height * scale * this.scaleFactor }); this.thumbnailBin.set_child(this.thumbnail); let allocation = this.actor.get_allocation_box(); let previewHeight = allocation.y2 - allocation.y1; let previewWidth = allocation.x2 - allocation.x1; let monitor = Main.layoutManager.findMonitorForActor(this.item); let previewTop; if (this._applet.orientation == St.Side.BOTTOM) { previewTop = this.item.get_transformed_position()[1] - previewHeight - 5; } else { previewTop = this.item.get_transformed_position()[1] + this.item.get_transformed_size()[1] + 5; } let previewLeft = this.item.get_transformed_position()[0] + this.item.get_transformed_size()[0]/2 - previewWidth/2; previewLeft = Math.round(previewLeft); previewLeft = Math.max(previewLeft, monitor.x); previewLeft = Math.min(previewLeft, monitor.x + monitor.width - previewWidth); this.actor.set_position(previewLeft, previewTop); this.actor.show(); this.visible = true; this._applet.cancelErodeTooltip(); this._applet._tooltipShowing = true; }, hide: function() { if (this.thumbnail) { this.thumbnailBin.set_child(null); this.thumbnail.destroy(); } this.actor.hide(); this.visible = false; }, _destroy: function() { if (this.thumbnail) { this.thumbnailBin.set_child(null); this.thumbnail.destroy(); } if (this.actor) { this.actor.destroy(); } this.actor = null; } } function AppMenuButton(applet, metaWindow, alert) { this._init(applet, metaWindow, alert); } AppMenuButton.prototype = { _init: function(applet, metaWindow, alert) { this.actor = new Cinnamon.GenericContainer({ name: 'appMenu', style_class: 'window-list-item-box', reactive: true, can_focus: true, track_hover: true }); this._applet = applet; this.metaWindow = metaWindow; this.alert = alert; if (this._applet.orientation == St.Side.TOP) this.actor.add_style_class_name('window-list-item-box-top'); else this.actor.add_style_class_name('window-list-item-box-bottom'); this.actor._delegate = this; this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this._iconBox = new Cinnamon.Slicer({ name: 'appMenuIcon' }); this._iconBox.connect('style-changed', Lang.bind(this, this._onIconBoxStyleChanged)); this._iconBox.connect('notify::allocation', Lang.bind(this, this._updateIconBoxClipAndGeometry)); this.actor.add_actor(this._iconBox); this._label = new St.Label(); this.actor.add_actor(this._label); this._iconBottomClip = 0; this._visible = true; this._updateCaptionId = this.metaWindow.connect('notify::title', Lang.bind(this, this.setDisplayTitle)); this._updateTileTypeId = this.metaWindow.connect('notify::tile-type', Lang.bind(this, this.setDisplayTitle)); this.onPreviewChanged(); if (!this.alert) { this._menuManager = new PopupMenu.PopupMenuManager(this); this.rightClickMenu = new AppMenuButtonRightClickMenu(this, this.metaWindow, this._applet.orientation); this._menuManager.addMenu(this.rightClickMenu); this._draggable = DND.makeDraggable(this.actor, null, this._applet.actor); this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); } this.onPanelEditModeChanged(); global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.onPanelEditModeChanged)); this._windows = this._applet._windows; this.scrollConnector = null; this.onScrollModeChanged(); this._needsAttention = false; this.setDisplayTitle(); this.onFocus() if (this.alert) this.getAttention(); }, onPreviewChanged: function() { if (this._tooltip) this._tooltip.destroy(); if (this._applet.usePreview) { this._tooltip = new WindowPreview(this, this.metaWindow, this._applet.orientation); } else { this._tooltip = new Tooltips.PanelItemTooltip(this, "", this._applet.orientation); this.setDisplayTitle(); } }, onPanelEditModeChanged: function() { if (this._draggable) this._draggable.inhibit = global.settings.get_boolean("panel-edit-mode"); }, onScrollModeChanged: function() { if (this._applet.scrollable) { this.scrollConnector = this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); } else { if (this.scrollConnector) { this.actor.disconnect(this.scrollConnector); this.scrollConnector = null; } } }, _onScrollEvent: function(actor, event) { let direction = event.get_scroll_direction(); // Find the current focused window let windows = this.actor.get_parent().get_children() .filter(function(item) { return item.visible; }).map(function(item) { return item._delegate; }); windows = windows.reverse(); let i = windows.length; while (i-- && !windows[i].metaWindow.has_focus()); if (i == -1) return; // v home-made xor if ((direction == 0) != this._applet.reverseScroll) i++; else i--; if (i == windows.length) i = 0; else if (i == -1) i = windows.length - 1; Main.activateWindow(windows[i].metaWindow, global.get_current_time()); }, _onDragBegin: function() { this._draggable._overrideY = this.actor.get_transformed_position()[1]; this._tooltip.hide(); this._tooltip.preventShow = true; }, _onDragEnd: function() { this.actor.show(); this._applet.clearDragPlaceholder(); this._tooltip.preventShow = false; }, _onDragCancelled: function() { this.actor.show(); this._applet.clearDragPlaceholder(); this._tooltip.preventShow = false; }, getDragActor: function() { let clone = new Clutter.Clone({ source: this.actor }); clone.width = this.actor.width; clone.height = this.actor.height; return clone; }, getDragActorSource: function() { return this.actor; }, handleDragOver: function(source, actor, x, y, time) { if (this._draggable && this._draggable.inhibit) return DND.DragMotionResult.MOVE_DROP; if (source instanceof AppMenuButton) return DND.DragMotionResult.CONTINUE; /* Users can drag things from one window to another window (eg drag an * image from Firefox to LibreOffice). However, if the target window is * hidden, they will drag to the AppWindowButton of the target window, * and we will open the window for them. */ this._toggleWindow(true); return DND.DragMotionResult.NO_DROP; }, acceptDrop: function(source, actor, x, y, time) { return false; }, setDisplayTitle: function() { let title = this.metaWindow.get_title(); let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_window_app(this.metaWindow); if (!title) title = app ? app.get_name() : '?'; /* Sanitize the window title to prevent dodgey window titles such as * "); DROP TABLE windows; --. Turn all whitespaces into " " because * newline characters are known to cause trouble. Also truncunate the * title when necessary or else cogl might get unhappy and crash * Cinnamon. */ title = title.replace(/\s/g, " "); if (title.length > MAX_TEXT_LENGTH) title = title.substr(0, MAX_TEXT_LENGTH); if (this.metaWindow.minimized) { title = "["+ title +"]"; } else if (this.metaWindow.tile_type == Meta.WindowTileType.TILED) { title = "|"+ title; } else if (this.metaWindow.tile_type == Meta.WindowTileType.SNAPPED) { title = "||"+ title; } this._label.set_text(title); if (this._tooltip && this._tooltip.set_text) this._tooltip.set_text(title); }, destroy: function() { this.metaWindow.disconnect(this._updateCaptionId); this.metaWindow.disconnect(this._updateTileTypeId); this._tooltip.destroy(); if (this.rightClickMenu) { this.rightClickMenu.destroy(); } this.actor.destroy(); }, _hasFocus: function() { if (this.metaWindow.minimized) return false; if (this.metaWindow.has_focus()) return true; let transientHasFocus = false; this.metaWindow.foreach_transient(function(transient) { if (transient.has_focus()) { transientHasFocus = true; return false; } return true; }); return transientHasFocus; }, onFocus: function() { if (this._hasFocus()) { this.actor.add_style_pseudo_class('focus'); this.actor.remove_style_class_name("window-list-item-demands-attention"); this.actor.remove_style_class_name("window-list-item-demands-attention-top"); this._needsAttention = false; if (this.alert) { this.destroy(); this._windows.splice(this._windows.indexOf(this), 1); } } else { this.actor.remove_style_pseudo_class('focus'); } this.setIcon(); }, _onButtonRelease: function(actor, event) { this._tooltip.hide(); if (this.alert) { if (event.get_button() == 1) this._toggleWindow(false); return false; } if (event.get_button() == 1) { if (this.rightClickMenu.isOpen) this.rightClickMenu.toggle(); this._toggleWindow(false); } else if (event.get_button() == 2 && this._applet.middleClickClose) { this.metaWindow.delete(global.get_current_time()); } return true; }, _onButtonPress: function(actor, event) { this._tooltip.hide(); if (!this.alert && event.get_button() == 3) { this.rightClickMenu.mouseEvent = event; this.rightClickMenu.toggle(); } }, _toggleWindow: function(fromDrag){ if (!this._hasFocus()) { Main.activateWindow(this.metaWindow, global.get_current_time()); this.actor.add_style_pseudo_class('focus'); } else if (!fromDrag) { this.metaWindow.minimize(global.get_current_time()); this.actor.remove_style_pseudo_class('focus'); } }, _onIconBoxStyleChanged: function() { let node = this._iconBox.get_theme_node(); this._iconBottomClip = node.get_length('app-icon-bottom-clip'); this._updateIconBoxClipAndGeometry(); }, _updateIconBoxClipAndGeometry: function() { let allocation = this._iconBox.allocation; if (this._iconBottomClip > 0) this._iconBox.set_clip(0, 0, allocation.x2 - allocation.x1, allocation.y2 - allocation.y1 - this._iconBottomClip); else this._iconBox.remove_clip(); let rect = new Meta.Rectangle(); [rect.x, rect.y] = this.actor.get_transformed_position(); [rect.width, rect.height] = this.actor.get_transformed_size(); this.metaWindow.set_icon_geometry(rect); }, _getPreferredWidth: function(actor, forHeight, alloc) { let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight); // minimum size just enough for icon if we ever get that many apps going alloc.min_size = naturalSize + 2 * 3 * global.ui_scale; if (this._applet.buttonsUseEntireSpace) { let [lminSize, lnaturalSize] = this._label.get_preferred_width(forHeight); alloc.natural_size = Math.max(150 * global.ui_scale, lnaturalSize + naturalSize + 3 * 3 * global.ui_scale); } else { alloc.natural_size = 150 * global.ui_scale; } }, _getPreferredHeight: function(actor, forWidth, alloc) { let [minSize1, naturalSize1] = this._iconBox.get_preferred_height(forWidth); let [minSize2, naturalSize2] = this._label.get_preferred_height(forWidth); alloc.min_size = Math.max(minSize1, minSize2); alloc.natural_size = Math.max(naturalSize1, naturalSize2); }, _allocate: function(actor, box, flags) { let allocWidth = box.x2 - box.x1; let allocHeight = box.y2 - box.y1; let childBox = new Clutter.ActorBox(); let [minWidth, minHeight, naturalWidth, naturalHeight] = this._iconBox.get_preferred_size(); let direction = this.actor.get_text_direction(); let xPadding = 3 * global.ui_scale; let yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2); childBox.y1 = yPadding; childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight); if (direction == Clutter.TextDirection.LTR) { if (allocWidth < naturalWidth + xPadding * 2) childBox.x1 = Math.max(0, (allocWidth - naturalWidth) / 2) else childBox.x1 = Math.min(allocWidth, xPadding); childBox.x2 = Math.min(childBox.x1 + naturalWidth, allocWidth); } else { if (allocWidth < naturalWidth + xPadding * 2) childBox.x1 = Math.max(0, (allocWidth - naturalWidth)/2); else childBox.x1 = allocWidth - naturalWidth - xPadding; childBox.x2 = Math.min(childBox.x1 + naturalWidth, allocWidth); } this._iconBox.allocate(childBox, flags); let remX1 = childBox.x2, remX2 = allocWidth; [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.get_preferred_size(); yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2); childBox.y1 = yPadding; childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight); if (direction == Clutter.TextDirection.LTR) { // Reuse the values from the previous allocation childBox.x1 = Math.min(childBox.x2 + xPadding, Math.max(0, allocWidth - xPadding)); childBox.x2 = Math.max(childBox.x1, allocWidth - xPadding); } else { childBox.x2 = Math.max(childBox.x1 - xPadding, 0); childBox.x1 = Math.min(childBox.x2, xPadding); } this._label.allocate(childBox, flags); }, setIcon: function() { let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_window_app(this.metaWindow); if (this._applet._scaleMode) this.iconSize = Math.round(this._applet._panelHeight * ICON_HEIGHT_FACTOR / global.ui_scale); else this.iconSize = DEFAULT_ICON_SIZE; let icon = app ? app.create_icon_texture(this.iconSize) : new St.Icon({ icon_name: 'application-default-icon', icon_type: St.IconType.FULLCOLOR, icon_size: this.iconSize }); let old_child = this._iconBox.get_child(); this._iconBox.set_child(icon); if (old_child) old_child.destroy(); }, getAttention: function() { if (this._needsAttention) return false; this._needsAttention = true; let counter = 0; this._flashButton(counter); return true; }, _flashButton: function(counter) { if (!this._needsAttention) return; this.actor.add_style_class_name("window-list-item-demands-attention"); if (counter < 4) { Mainloop.timeout_add(FLASH_INTERVAL, Lang.bind(this, function () { if (this.actor.has_style_class_name("window-list-item-demands-attention")) { this.actor.remove_style_class_name("window-list-item-demands-attention"); } Mainloop.timeout_add(FLASH_INTERVAL, Lang.bind(this, function () { this._flashButton(++counter); })); })); } } }; function AppMenuButtonRightClickMenu(launcher, metaWindow, orientation) { this._init(launcher, metaWindow, orientation); } AppMenuButtonRightClickMenu.prototype = { __proto__: Applet.AppletPopupMenu.prototype, _init: function(launcher, metaWindow, orientation) { Applet.AppletPopupMenu.prototype._init.call(this, launcher, orientation); this._launcher = launcher; this._windows = launcher._applet._windows; this.connect('open-state-changed', Lang.bind(this, this._onToggled)); this.orientation = orientation; this.metaWindow = metaWindow; }, _populateMenu: function(){ this.box.pack_start = this.orientation == St.Side.TOP; let mw = this.metaWindow; let item; let length; item = new PopupMenu.PopupIconMenuItem(_("Configure the window list"), "system-run", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings applets window-list@cinnamon.org " + this._launcher._applet.instance_id); })); this.addMenuItem(item); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Move to monitor if ((length = Main.layoutManager.monitors.length) == 2) { Main.layoutManager.monitors.forEach(function (monitor, index) { if (index === mw.get_monitor()) return; item = new PopupMenu.PopupMenuItem(_("Move to the other monitor")); item.connect('activate', function() { mw.move_to_monitor(index); }); this.addMenuItem(item); }, this); } else if ((length = Main.layoutManager.monitors.length) > 2) { Main.layoutManager.monitors.forEach(function (monitor, index) { if (index === mw.get_monitor()) return; item = new PopupMenu.PopupMenuItem(_("Move to monitor %d").format(index + 1)); item.connect('activate', function() { mw.move_to_monitor(index); }); this.addMenuItem(item); }, this); } // Move to workspace if ((length = global.screen.n_workspaces) > 1) { if (mw.is_on_all_workspaces()) { item = new PopupMenu.PopupMenuItem(_("Only on this workspace")); item.connect('activate', function() { mw.unstick(); }); this.addMenuItem(item); } else { item = new PopupMenu.PopupMenuItem(_("Visible on all workspaces")); item.connect('activate', function() { mw.stick(); }); this.addMenuItem(item); item = new PopupMenu.PopupSubMenuMenuItem(_("Move to another workspace")); this.addMenuItem(item); let curr_index = mw.get_workspace().index(); for (let i = 0; i < length; i++) { if (i == curr_index) continue; // Make the index a local variable to pass to function let j = i; item.menu.addAction( Main.workspace_names[i] ? Main.workspace_names[i] : Main._makeDefaultWorkspaceName(i), function() { mw.change_workspace(global.screen.get_workspace_by_index(j)); }); } } } this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Close all/others item = new PopupMenu.PopupMenuItem(_("Close all")); item.connect('activate', Lang.bind(this, function() { for (let window of this._windows) if (window.actor.visible && !window._needsAttention) window.metaWindow.delete(global.get_current_time); })); this.addMenuItem(item); item = new PopupMenu.PopupMenuItem(_("Close others")); item.connect('activate', Lang.bind(this, function() { for (let window of this._windows) if (window.actor.visible && window.metaWindow != this.metaWindow && !window._needsAttention) window.metaWindow.delete(global.get_current_time); })); this.addMenuItem(item); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Miscellaneous if (mw.get_compositor_private().opacity != 255) { item = new PopupMenu.PopupMenuItem(_("Restore to full opacity")); item.connect('activate', function() { mw.get_compositor_private().set_opacity(255); }); this.addMenuItem(item); } if (mw.minimized) { item = new PopupMenu.PopupMenuItem(_("Restore")); item.connect('activate', function() { Main.activateWindow(mw, global.get_current_time()); }); } else { item = new PopupMenu.PopupMenuItem(_("Minimize")); item.connect('activate', function() { mw.minimize(global.get_current_time()); }); } this.addMenuItem(item); if (mw.get_maximized()) { item = new PopupMenu.PopupMenuItem(_("Unmaximize")); item.connect('activate', function() { mw.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); }); } else { item = new PopupMenu.PopupMenuItem(_("Maximize")); item.connect('activate', function() { mw.maximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); }); } this.addMenuItem(item); item = new PopupMenu.PopupMenuItem(_("Close")); item.connect('activate', function() { mw.delete(global.get_current_time()); }); this.addMenuItem(item); }, _onToggled: function(actor, isOpening){ if (!isOpening) { return; } this.removeAll(); this._populateMenu(); }, }; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id); this.actor.set_track_hover(false); this.actor.add_style_class_name("window-list-box"); this.orientation = orientation; this.dragInProgress = false; this._tooltipShowing = false; this._tooltipErodeTimer = null; this._urgentSignal = null; this._windows = []; this._monitorWatchList = []; this.settings = new Settings.AppletSettings(this, "window-list@cinnamon.org", this.instance_id); this.settings.bindProperty(Settings.BindingDirection.IN, "enable-alerts", "enableAlerts", this._updateAttentionGrabber, null); this.settings.bindProperty(Settings.BindingDirection.IN, "enable-scrolling", "scrollable", this._onEnableScrollChanged, null); this.settings.bindProperty(Settings.BindingDirection.IN, "reverse-scrolling", "reverseScroll", null, null); this.settings.bindProperty(Settings.BindingDirection.IN, "middle-click-close", "middleClickClose", null, null); this.settings.bindProperty(Settings.BindingDirection.IN, "buttons-use-entire-space", "buttonsUseEntireSpace", this._refreshItems, null); this.settings.bindProperty(Settings.BindingDirection.IN, "window-preview", "usePreview", this._onPreviewChanged, null); this.signals = new SignalManager.SignalManager(this); let tracker = Cinnamon.WindowTracker.get_default(); this.signals.connect(tracker, "notify::focus-app", this._onFocus); this.signals.connect(global.screen, 'window-entered-monitor', this._onWindowAdded); this.signals.connect(global.screen, 'window-left-monitor', this._onWindowRemoved); this.signals.connect(global.screen, 'notify::n-workspaces', this._onNWorkspacesChanged); this.signals.connect(global.screen, 'monitors-changed', this._updateWatchedMonitors); this.signals.connect(global.window_manager, 'switch-workspace',this._refreshItems); this.signals.connect(global.window_manager, 'minimize', this._onWindowStateChange); this.signals.connect(global.window_manager, 'maximize', this._onWindowStateChange); this.signals.connect(global.window_manager, 'unmaximize', this._onWindowStateChange); this.signals.connect(global.window_manager, 'map', this._onWindowStateChange); this.signals.connect(global.window_manager, 'tile', this._onWindowStateChange); global.settings.bind("panel-edit-mode", this.actor, "reactive", Gio.SettingsBindFlags.DEFAULT); this.on_orientation_changed(orientation); this._onNWorkspacesChanged(); this._updateAttentionGrabber(); }, on_applet_removed_from_panel: function() { this.signals.disconnectAllSignals(); }, on_applet_instances_changed: function() { this._updateWatchedMonitors(); }, on_panel_height_changed: function() { this._refreshItems(); }, on_orientation_changed: function(orientation) { this.orientation = orientation; if (orientation == St.Side.TOP) { for (let child of this.actor.get_children()) { child.set_style_class_name('window-list-item-box window-list-box-top'); child.set_style('margin-top: 0px; padding-top: 0px;'); } this.actor.set_style('margin-top: 0px; padding-top: 0px;'); } else { for (let child of this.actor.get_children()) { child.set_style_class_name('window-list-item-box window-list-box-bottom'); child.set_style('margin-bottom: 0px; padding-bottom: 0px;'); } this.actor.set_style('margin-bottom: 0px; padding-bottom: 0px;'); } }, /* These are callbacks to two different signals that pass different number * of arguments. However, the last argument is always metaWindow, which is * the only thing we want. */ _onWindowAdded: function() { if (this._shouldAdd(arguments[arguments.length - 1])) this._addWindow(arguments[arguments.length - 1]); }, _onWindowRemoved: function() { this._removeWindow(arguments[arguments.length - 1]); }, _onNWorkspacesChanged: function() { this.signals.disconnect("window-added"); this.signals.disconnect("window-removed"); for (let i = 0; i < global.screen.n_workspaces; i++) { let ws = global.screen.get_workspace_by_index(i); this.signals.connect(ws, "window-added", this._onWindowAdded); this.signals.connect(ws, "window-removed", this._onWindowRemoved); } }, _updateAttentionGrabber: function() { if (this.enableAlerts) { this.signals.connect(global.display, "window-marked-urgent", this._onWindowDemandsAttention); this.signals.connect(global.display, "window-demands-attention", this._onWindowDemandsAttention); } else { this.signals.disconnect("window-marked-urgent"); this.signals.disconnect("window-demands-attention"); } }, _onEnableScrollChanged: function() { for (let window of this._windows) window.onScrollModeChanged(); }, _onPreviewChanged: function() { for (let window of this._windows) window.onPreviewChanged(); }, _onWindowDemandsAttention : function(display, window) { // Magic to look for AppMenuButton owning window let i = this._windows.length; while (i-- && this._windows[i].metaWindow != window); // Window is not in our list if (i == -1) return; // Asks AppMenuButton to flash. Returns false if already flashing if (!this._windows[i].getAttention()) return; if (window.get_workspace() != global.screen.get_active_workspace()) this._addWindow(window, true); }, _onFocus: function() { for (let window of this._windows) window.onFocus(); }, _refreshItems: function() { for (let window of this._windows) { window.actor.visible = (window.metaWindow.get_workspace() == global.screen.get_active_workspace()) || window.metaWindow.is_on_all_workspaces(); /* The above calculates the visibility if it were the normal * AppMenuButton. If this is actually a temporary AppMenuButton for * urgent windows on other workspaces, it is shown iff the normal * one isn't shown! */ if (window.alert) window.actor.visible = !window.actor.visible; } this._onFocus(); }, _onWindowStateChange: function(cinnamonwm, actor) { for (let window of this._windows) if (window.metaWindow == actor.metaWindow) window.setDisplayTitle(); }, // Used by windowManager for traditional minimize and map effect getOriginFromWindow: function(metaWindow) { for (let window of this._windows) if (window.metaWindow == metaWindow) return window.actor; return false; }, _updateWatchedMonitors: function() { let n_mons = Gdk.Screen.get_default().get_n_monitors(); let on_primary = this.panel.monitorIndex == Main.layoutManager.primaryIndex; let instances = Main.AppletManager.getRunningInstancesForUuid(this._uuid); /* Simple cases */ if (n_mons == 1) { this._monitorWatchList = [Main.layoutManager.primaryIndex]; } else if (instances.length > 1 && !on_primary) { this._monitorWatchList = [this.panel.monitorIndex]; } else { /* This is an instance on the primary monitor - it will be * responsible for any monitors not covered individually. First * convert the instances list into a list of the monitor indices, * and then add the monitors not present to the monitor watch list * */ this._monitorWatchList = [this.panel.monitorIndex]; instances = instances.map(function(x) { return x.panel.monitorIndex; }); for (let i = 0; i < n_mons; i++) if (instances.indexOf(i) == -1) this._monitorWatchList.push(i); } // Now track the windows in our favorite monitors let windows = global.display.list_windows(0); for (let window of windows) { if (this._shouldAdd(window)) this._addWindow(window); else this._removeWindow(window); } }, _addWindow: function(metaWindow, alert) { for (let window of this._windows) if (window.metaWindow == metaWindow && window.temp == alert) return; let appButton = new AppMenuButton(this, metaWindow, alert); this.actor.add(appButton.actor); this._windows.push(appButton); /* We want to make the AppMenuButtons look like they are ordered by * workspace. So if we add an AppMenuButton for a window in another * workspace, put it in the right position. It is at the end by * default, so move it to the start if needed */ if (alert) { if (metaWindow.get_workspace().index() < global.screen.get_active_workspace_index()) this.actor.move_child(appButton.actor, 0); } else { if (metaWindow.get_workspace() != global.screen.get_active_workspace()) appButton.actor.hide(); } }, _removeWindow: function(metaWindow) { let i = this._windows.length; // Do an inverse loop because we might remove some elements while (i--) { if (this._windows[i].metaWindow == metaWindow) { this._windows[i].destroy(); this._windows.splice(i, 1); } } }, _shouldAdd: function(metaWindow) { return Main.isInteresting(metaWindow) && this._monitorWatchList.indexOf(metaWindow.get_monitor()) != -1; }, handleDragOver: function(source, actor, x, y, time) { if (this._inEditMode) return DND.DragMotionResult.MOVE_DROP; if (!(source instanceof AppMenuButton)) return DND.DragMotionResult.NO_DROP; source.actor.hide(); let children = this.actor.get_children(); let pos = children.length; while (--pos && x < children[pos].get_allocation_box().x1); this._dragPlaceholderPos = pos; if (this._dragPlaceholder == undefined) { this._dragPlaceholder = new DND.GenericDragPlaceholderItem(); this._dragPlaceholder.child.set_width (source.actor.width); this._dragPlaceholder.child.set_height (source.actor.height); this.actor.insert_actor(this._dragPlaceholder.actor, this._dragPlaceholderPos); } else { this.actor.move_child(this._dragPlaceholder.actor, this._dragPlaceholderPos); } return DND.DragMotionResult.MOVE_DROP; }, acceptDrop: function(source, actor, x, y, time) { if (!(source instanceof AppMenuButton)) return false; if (this._dragPlaceholderPos == undefined) return false; this.actor.move_child(source.actor, this._dragPlaceholderPos); return true; }, clearDragPlaceholder: function() { if (this._dragPlaceholder) { this._dragPlaceholder.actor.destroy(); this._dragPlaceholder = undefined; this._dragPlaceholderPos = undefined; } }, erodeTooltip: function() { if (this._tooltipErodeTimer) { Mainloop.source_remove(this._tooltipErodeTimer); this._tooltipErodeTimer = null; } this._tooltipErodeTimer = Mainloop.timeout_add(300, Lang.bind(this, function() { this._tooltipShowing = false; this._tooltipErodeTimer = null; return false; })); }, cancelErodeTooltip: function() { if (this._tooltipErodeTimer) { Mainloop.source_remove(this._tooltipErodeTimer); this._tooltipErodeTimer = null; } } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/0000775000175000017500000000000012625666430026210 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/settings-schema.json0000664000175000017500000000042512625666430032202 0ustar fabiofabio{ "launcherList": { "type": "generic", "default": ["firefox.desktop", "gnome-terminal.desktop", "nemo.desktop"] }, "allow-dragging": { "type": "checkbox", "default": true, "description": "Allow dragging of launchers" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/metadata.json0000664000175000017500000000031712625666430030664 0ustar fabiofabio{ "uuid": "panel-launchers@cinnamon.org", "name": "Panel launchers", "description": "Launch applications from the panel", "icon": "gnome-panel-launcher", "role": "panellauncher", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js0000664000175000017500000005127012625666430030040 0ustar fabiofabioconst Applet = imports.ui.applet; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Gio = imports.gi.Gio; const PopupMenu = imports.ui.popupMenu; const Main = imports.ui.main; const GLib = imports.gi.GLib; const Tooltips = imports.ui.tooltips; const DND = imports.ui.dnd; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const Settings = imports.ui.settings; const Signals = imports.signals; const DEFAULT_ICON_SIZE = 20; const DEFAULT_ANIM_SIZE = 13; const ICON_HEIGHT_FACTOR = .8; const ICON_ANIM_FACTOR = .65; const PANEL_EDIT_MODE_KEY = 'panel-edit-mode'; const PANEL_LAUNCHERS_KEY = 'panel-launchers'; const PANEL_LAUNCHERS_DRAGGABLE_KEY = 'panel-launchers-draggable'; const CUSTOM_LAUNCHERS_PATH = GLib.get_home_dir() + '/.cinnamon/panel-launchers'; let pressLauncher = null; function PanelAppLauncherMenu(launcher, orientation) { this._init(launcher, orientation); } PanelAppLauncherMenu.prototype = { __proto__: Applet.AppletPopupMenu.prototype, _init: function(launcher, orientation) { this._launcher = launcher; Applet.AppletPopupMenu.prototype._init.call(this, launcher, orientation); this.addAction(_("Launch"), Lang.bind(this, this._onLaunchActivate)); this.addAction(_("Add"), Lang.bind(this, this._onAddActivate)); this.addAction(_("Edit"), Lang.bind(this, this._onEditActivate)); this.addAction(_("Remove"), Lang.bind(this, this._onRemoveActivate)); }, _onLaunchActivate: function(event) { this._launcher.launch(); }, _onRemoveActivate: function(event) { this._launcher.launchersBox.removeLauncher(this._launcher, this._launcher.isCustom()); this._launcher.actor.destroy(); }, _onAddActivate: function(event) { this._launcher.launchersBox.showAddLauncherDialog(event.get_time()); }, _onEditActivate: function(event) { this._launcher.launchersBox.showAddLauncherDialog(event.get_time(), this._launcher); } } function PanelAppLauncher(launchersBox, app, appinfo, orientation, panel_height, scale) { this._init(launchersBox, app, appinfo, orientation, panel_height, scale); } PanelAppLauncher.prototype = { __proto__: DND.LauncherDraggable.prototype, _init: function(launchersBox, app, appinfo, orientation, panel_height, scale) { DND.LauncherDraggable.prototype._init.call(this); this.app = app; this.appinfo = appinfo; this.launchersBox = launchersBox; this._applet = launchersBox; this.actor = new St.Bin({ style_class: 'panel-launcher', reactive: true, can_focus: true, x_fill: true, y_fill: false, track_hover: true }); this.actor._delegate = this; this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this._iconBox = new St.Bin({ name: 'panel-launcher-icon' }); this._iconBox.connect('style-changed', Lang.bind(this, this._onIconBoxStyleChanged)); this._iconBox.connect('notify::allocation', Lang.bind(this, this._updateIconBoxClip)); this.actor.add_actor(this._iconBox); this._iconBottomClip = 0; if (scale) { this.icon_height = Math.floor((panel_height * ICON_HEIGHT_FACTOR) / global.ui_scale); this.icon_anim_height = Math.floor((panel_height * ICON_ANIM_FACTOR) / global.ui_scale); } else { this.icon_height = DEFAULT_ICON_SIZE; this.icon_anim_height = DEFAULT_ANIM_SIZE; } this.icon = this._getIconActor(); this._iconBox.set_child(this.icon); this._menuManager = new PopupMenu.PopupMenuManager(this); this._menu = new PanelAppLauncherMenu(this, orientation); this._menuManager.addMenu(this._menu); let tooltipText = this.isCustom() ? appinfo.get_name() : app.get_name(); this._tooltip = new Tooltips.PanelItemTooltip(this, tooltipText, orientation); this._dragging = false; this._draggable = DND.makeDraggable(this.actor); this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); this._draggable.inhibit = !this.launchersBox.allowDragging || global.settings.get_boolean(PANEL_EDIT_MODE_KEY); this.launchersBox.connect("launcher-draggable-setting-changed", Lang.bind(this, this._updateInhibit)); global.settings.connect('changed::' + PANEL_EDIT_MODE_KEY, Lang.bind(this, this._updateInhibit)); }, _onDragBegin: function() { this._dragging = true; this._tooltip.hide(); this._tooltip.preventShow = true; }, _onDragEnd: function() { this._dragging = false; this._tooltip.preventShow = false; this._applet._clearDragPlaceholder(); }, _onDragCancelled: function() { this._dragging = false; this._tooltip.preventShow = false; }, _updateInhibit: function(){ this._draggable.inhibit = !this.launchersBox.allowDragging || global.settings.get_boolean(PANEL_EDIT_MODE_KEY); }, getDragActor: function() { return this._getIconActor(); }, // Returns the original actor that should align with the actor // we show as the item is being dragged. getDragActorSource: function() { return this.icon; }, _getIconActor: function() { if (this.isCustom()) { let icon = this.appinfo.get_icon(); if (icon == null) icon = new Gio.ThemedIcon({name: "gnome-panel-launcher"}); return St.TextureCache.get_default().load_gicon(null, icon, this.icon_height); } else { return this.app.create_icon_texture(this.icon_height); } }, _animateIcon: function(step){ if (step>=3) return; Tweener.addTween(this.icon, { width: this.icon_anim_height * global.ui_scale, height: this.icon_anim_height * global.ui_scale, time: 0.2, transition: 'easeOutQuad', onComplete: function(){ Tweener.addTween(this.icon, { width: this.icon_height * global.ui_scale, height: this.icon_height * global.ui_scale, time: 0.2, transition: 'easeOutQuad', onComplete: function(){ this._animateIcon(step+1); }, onCompleteScope: this }); }, onCompleteScope: this }); }, launch: function() { let allocation = this._iconBox.get_allocation_box(); this._iconBox.width = allocation.x2 - allocation.x1; this._iconBox.height = allocation.y2 - allocation.y1; this._animateIcon(0); if (this.isCustom()) this.appinfo.launch([], null); else this.app.open_new_window(-1); }, getId: function() { if (this.isCustom()) return Gio.file_new_for_path(this.appinfo.get_filename()).get_basename(); else return this.app.get_id(); }, isCustom: function() { return (this.app==null); }, _onButtonPress: function(actor, event) { pressLauncher = this.getAppname(); if (event.get_button() == 3) this._menu.toggle(); }, _onButtonRelease: function(actor, event) { if (pressLauncher == this.getAppname()){ let button = event.get_button(); if (button==1) { if (this._menu.isOpen) this._menu.toggle(); else this.launch(); } } }, _onIconBoxStyleChanged: function() { let node = this._iconBox.get_theme_node(); this._iconBottomClip = node.get_length('panel-launcher-bottom-clip'); this._updateIconBoxClip(); }, _updateIconBoxClip: function() { let allocation = this._iconBox.allocation; if (this._iconBottomClip > 0) this._iconBox.set_clip(0, 0, allocation.x2 - allocation.x1, allocation.y2 - allocation.y1 - this._iconBottomClip); else this._iconBox.remove_clip(); }, getAppInfo: function() { return (this.isCustom() ? this.appinfo : this.app.get_app_info()); }, getCommand: function() { return this.getAppInfo().get_commandline(); }, getAppname: function() { return this.getAppInfo().get_name(); }, getIcon: function() { let icon = this.getAppInfo().get_icon(); if (icon){ if (icon instanceof Gio.FileIcon) { return icon.get_file().get_path(); } else { return icon.get_names().toString(); } } return null; } } function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id); this.actor.set_track_hover(false); this.orientation = orientation; this._dragPlaceholder = null; this._dragPlaceholderPos = -1; this._animatingPlaceholdersCount = 0; this.myactor = new St.BoxLayout({ name: 'panel-launchers-box', style_class: 'panel-launchers-box' }); this.settings = new Settings.AppletSettings(this, metadata.uuid, instance_id); this.settings.bindProperty(Settings.BindingDirection.BIDIRECTIONAL, "launcherList", "launcherList", this._onSettingsChanged, null); this.settings.bindProperty(Settings.BindingDirection.IN, "allow-dragging", "allowDragging", this._updateLauncherDrag, null); this.uuid = metadata.uuid; this._settings_proxy = new Array(); this._launchers = new Array(); this.actor.add(this.myactor); this.actor.reactive = global.settings.get_boolean(PANEL_EDIT_MODE_KEY); global.settings.connect('changed::' + PANEL_EDIT_MODE_KEY, Lang.bind(this, this._onPanelEditModeChanged)); this.do_gsettings_import(); // We shouldn't need to call reload() here... since we get a "icon-theme-changed" signal when CSD starts. // The reason we do is in case the Cinnamon icon theme is the same as the one specificed in GTK itself (in .config) // In that particular case we get no signal at all. this.reload(); St.TextureCache.get_default().connect("icon-theme-changed", Lang.bind(this, this.reload)); }, _updateLauncherDrag: function() { this.emit("launcher-draggable-setting-changed"); }, do_gsettings_import: function() { let old_launchers = global.settings.get_strv(PANEL_LAUNCHERS_KEY); if (old_launchers.length >= 1 && old_launchers[0] != "DEPRECATED") { this.launcherList = old_launchers; } global.settings.set_strv(PANEL_LAUNCHERS_KEY, ["DEPRECATED"]); }, _onPanelEditModeChanged: function() { this.actor.reactive = global.settings.get_boolean(PANEL_EDIT_MODE_KEY); }, _onSettingsChanged: function() { this.reload(); }, sync_settings_proxy_to_settings: function() { this.launcherList = this._settings_proxy.map(x => x.file); }, _remove_launcher_from_proxy: function(visible_index) { let j = -1; for (let i = 0; i < this._settings_proxy.length; i++) { if (this._settings_proxy[i].valid) { j++; if (j == visible_index) { this._settings_proxy.splice(i, 1); break; } } } }, _move_launcher_in_proxy: function(launcher, new_index) { let proxy_member; for (let i = 0; i < this._settings_proxy.length; i++) { if (this._settings_proxy[i].launcher == launcher) { proxy_member = this._settings_proxy.splice(i, 1)[0]; break; } } if (!proxy_member) return; let j = -1; for (let i = 0; i < this._settings_proxy.length; i++) { if (this._settings_proxy[i].valid) { j++; if (j == new_index) { this._settings_proxy.splice(i, 0, proxy_member); return; } } } if (new_index == j + 1) this._settings_proxy.push(proxy_member); }, loadSingleApp: function(path) { let appSys = Cinnamon.AppSystem.get_default(); let app = appSys.lookup_app(path); let appinfo = null; if (!app) appinfo = Gio.DesktopAppInfo.new_from_filename(CUSTOM_LAUNCHERS_PATH+"/"+path); return [app, appinfo] }, on_panel_height_changed: function() { this.reload(); }, reload: function() { this.myactor.destroy_children(); this._launchers = new Array(); this._settings_proxy = new Array(); for (let file of this.launcherList) { let [app, appinfo] = this.loadSingleApp(file); if (app || appinfo) { let launcher = new PanelAppLauncher(this, app, appinfo, this.orientation, this._panelHeight, this._scaleMode); this.myactor.add(launcher.actor); this._launchers.push(launcher); this._settings_proxy.push({ file: file, valid: true, launcher: launcher }); } else { this._settings_proxy.push({ file: file, valid: false }); } } }, removeLauncher: function(launcher, delete_file) { let i = this._launchers.indexOf(launcher); if (i >= 0) { launcher.actor.destroy(); this._launchers.splice(i, 1); this._remove_launcher_from_proxy(i); } if (delete_file) { let appid = launcher.getId(); let file = Gio.file_new_for_path(CUSTOM_LAUNCHERS_PATH+"/"+appid); if (file.query_exists(null)) file.delete(null); } this.sync_settings_proxy_to_settings(); }, getDummyLauncher: function(path) { let [app, appinfo] = this.loadSingleApp(path); let dummy; if (app || appinfo) { dummy = new PanelAppLauncher(this, app, appinfo, this.orientation, this._panelHeight); } if (dummy && dummy.actor) return dummy.actor; else return null; }, acceptNewLauncher: function(path) { this.myactor.add(this.getDummyLauncher(path)); let launchers = this.launcherList; launchers.push(path); this.launcherList = launchers; this.reload(); }, addForeignLauncher: function(path, position, source) { this.myactor.insert_actor(this.getDummyLauncher(path), position); this._settings_proxy.splice(position, 0, { file: path, valid: true }); this.sync_settings_proxy_to_settings(); }, moveLauncher: function(launcher, pos) { let origpos = this._launchers.indexOf(launcher); if (origpos >= 0) { launcher.actor.destroy(); this.myactor.insert_actor(this.getDummyLauncher(launcher.getId()), pos); this._launchers.splice(origpos, 1); this._move_launcher_in_proxy(launcher, pos); this.sync_settings_proxy_to_settings(); } }, showAddLauncherDialog: function(timestamp, launcher){ let args = this.uuid + " " + this.instance_id + " " + this.settings.get_file_path(); if (launcher) { Util.spawnCommandLine("cinnamon-desktop-editor -mcinnamon-launcher -f" + launcher.getId() + " " + args); } else { Util.spawnCommandLine("cinnamon-desktop-editor -mcinnamon-launcher " + args); } }, _clearDragPlaceholder: function() { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._dragPlaceholder = null; this._dragPlaceholderPos = -1; } }, handleDragOver: function(source, actor, x, y, time) { if (!(source.isDraggableApp || (source instanceof DND.LauncherDraggable))) return DND.DragMotionResult.NO_DROP; let children = this.myactor.get_children(); let numChildren = children.length; let boxWidth = this.myactor.width; if (this._dragPlaceholder) { boxWidth -= this._dragPlaceholder.actor.width; numChildren--; } let launcherPos = this._launchers.indexOf(source); let pos = Math.round(x * numChildren / boxWidth); if (pos != this._dragPlaceholderPos && pos <= numChildren) { if (this._animatingPlaceholdersCount > 0) { let launchersChildren = children.filter(function(actor) { return actor._delegate instanceof DND.LauncherDraggable; }); this._dragPlaceholderPos = children.indexOf(launchersChildren[pos]); } else { this._dragPlaceholderPos = pos; } // Don't allow positioning before or after self if (launcherPos != -1 && pos == launcherPos) { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._animatingPlaceholdersCount++; this._dragPlaceholder.actor.connect('destroy', Lang.bind(this, function() { this._animatingPlaceholdersCount--; })); } this._dragPlaceholder = null; return DND.DragMotionResult.CONTINUE; } // If the placeholder already exists, we just move // it, but if we are adding it, expand its size in // an animation let fadeIn; if (this._dragPlaceholder) { this._dragPlaceholder.actor.destroy(); fadeIn = false; } else { fadeIn = true; } this._dragPlaceholder = new DND.GenericDragPlaceholderItem(); this._dragPlaceholder.child.set_width (20); this._dragPlaceholder.child.set_height (10); this.myactor.insert_actor(this._dragPlaceholder.actor, this._dragPlaceholderPos); if (fadeIn) this._dragPlaceholder.animateIn(); } return DND.DragMotionResult.MOVE_DROP; }, acceptDrop: function(source, actor, x, y, time) { if (!(source.isDraggableApp || (source instanceof DND.LauncherDraggable))) return DND.DragMotionResult.NO_DROP; let sourceId; if (source instanceof DND.LauncherDraggable) sourceId = source.getId(); else sourceId = source.get_app_id(); let launcherPos = 0; let children = this.myactor.get_children(); for (let i = 0; i < this._dragPlaceholderPos; i++) { if (this._dragPlaceholder && children[i] == this._dragPlaceholder.actor) continue; let childId = children[i]._delegate.getId(); if (source === children[i]._delegate) continue; launcherPos++; } if (source instanceof DND.LauncherDraggable && source.launchersBox == this) this.moveLauncher(source, launcherPos); else { if (source instanceof DND.LauncherDraggable) source.launchersBox.removeLauncher(source, false); this.addForeignLauncher(sourceId, launcherPos, source); } actor.destroy(); return true; } }; Signals.addSignalMethods(MyApplet.prototype); function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/power@cinnamon.org/0000775000175000017500000000000012625666430024263 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/power@cinnamon.org/settings-schema.json0000664000175000017500000000053412625666430030256 0ustar fabiofabio{ "labelinfo": { "type": "radiogroup", "description": "Display", "default": "nothing", "options" : { "Show percentage": "percentage", "Show time remaining": "time", "Show percentage and time remaining": "percentage_time", "Hide label": "nothing" } } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/power@cinnamon.org/metadata.json0000664000175000017500000000022212625666430026732 0ustar fabiofabio{ "uuid": "power@cinnamon.org", "name": "Power Manager", "description": "Cinnamon power management applet", "icon": "battery-full-charging" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js0000664000175000017500000004615512625666430026121 0ustar fabiofabioconst Applet = imports.ui.applet; const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const Interfaces = imports.misc.interfaces const Lang = imports.lang; const St = imports.gi.St; const Tooltips = imports.ui.tooltips; const PopupMenu = imports.ui.popupMenu; const Pango = imports.gi.Pango; const Main = imports.ui.main; const Settings = imports.ui.settings; const GnomeSession = imports.misc.gnomeSession; const BrightnessBusName = "org.cinnamon.SettingsDaemon.Power.Screen"; const KeyboardBusName = "org.cinnamon.SettingsDaemon.Power.Keyboard"; const UPDeviceType = { UNKNOWN: 0, AC_POWER: 1, BATTERY: 2, UPS: 3, MONITOR: 4, MOUSE: 5, KEYBOARD: 6, PDA: 7, PHONE: 8, MEDIA_PLAYER: 9, TABLET: 10, COMPUTER: 11 }; const UPDeviceState = { UNKNOWN: 0, CHARGING: 1, DISCHARGING: 2, EMPTY: 3, FULLY_CHARGED: 4, PENDING_CHARGE: 5, PENDING_DISCHARGE: 6 }; function deviceTypeToString(type) { switch (type) { case UPDeviceType.AC_POWER: return _("AC adapter"); case UPDeviceType.BATTERY: return _("Laptop battery"); case UPDeviceType.UPS: return _("UPS"); case UPDeviceType.MONITOR: return _("Monitor"); case UPDeviceType.MOUSE: return _("Mouse"); case UPDeviceType.KEYBOARD: return _("Keyboard"); case UPDeviceType.PDA: return _("PDA"); case UPDeviceType.PHONE: return _("Cell phone"); case UPDeviceType.MEDIA_PLAYER: return _("Media player"); case UPDeviceType.TABLET: return _("Tablet"); case UPDeviceType.COMPUTER: return _("Computer"); default: return _("Unknown"); } } function deviceToIcon(type, icon) { switch (type) { case UPDeviceType.MONITOR: return ("video-display"); case UPDeviceType.MOUSE: return ("input-mouse"); case UPDeviceType.KEYBOARD: return ("input-keyboard"); case UPDeviceType.PHONE: case UPDeviceType.MEDIA_PLAYER: return ("phone-apple-iphone"); case UPDeviceType.TABLET: return ("input-tablet"); case UPDeviceType.COMPUTER: return ("computer"); default: if (icon) { return icon; } else { return ("battery-full"); } } } function DeviceItem() { this._init.apply(this, arguments); } DeviceItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(device, status) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, { reactive: false }); let [device_id, vendor, model, device_type, icon, percentage, state, time, timepercentage] = device; this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' }); this._vbox = new St.BoxLayout({ style_class: 'popup-device-menu-item', vertical: true}); let description = deviceTypeToString(device_type); if (vendor != "" || model != "") { description = "%s %s".format(vendor, model); } this.label = new St.Label({ text: "%s %d%%".format(description, Math.round(percentage)) }); let statusLabel = new St.Label({ text: "%s".format(status), style_class: 'popup-inactive-menu-item' }); let device_icon = deviceToIcon(device_type, icon); if (device_icon == icon) { this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon), icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' }); } else { this._icon = new St.Icon({icon_name: device_icon, icon_type: St.IconType.SYMBOLIC, icon_size: 16}); } this._box.add_actor(this._icon); this._box.add_actor(this.label); this._vbox.add_actor(this._box); this._vbox.add_actor(statusLabel); this.addActor(this._vbox); } } function BrightnessSlider(applet, label, icon, busName, minimum_value){ this._init(applet, label, icon, busName, minimum_value); } BrightnessSlider.prototype = { __proto__: PopupMenu.PopupSliderMenuItem.prototype, _init: function(applet, label, icon, busName, minimum_value){ PopupMenu.PopupSliderMenuItem.prototype._init.call(this, 0); this.actor.hide(); this._applet = applet; this._seeking = false; this._minimum_value = minimum_value; this.connect("drag-begin", Lang.bind(this, function(){ this._seeking = true; })); this.connect("drag-end", Lang.bind(this, function(){ this._seeking = false; })); this.icon = new St.Icon({icon_name: icon, icon_type: St.IconType.SYMBOLIC, icon_size: 16}); this.removeActor(this._slider); this.addActor(this.icon, {span: 0}); this.addActor(this._slider, {span: -1, expand: true}); this.label = label; this.toolTipText = label; this.tooltip = new Tooltips.Tooltip(this.actor, this.tooltipText); Interfaces.getDBusProxyAsync(busName, Lang.bind(this, function(proxy, error) { this._proxy = proxy; this._proxy.GetPercentageRemote(Lang.bind(this, this._dbusAcquired)); })); }, _dbusAcquired: function(b, error){ if(error) return; this._updateBrightnessLabel(b); this.setValue(b / 100); this.connect("value-changed", Lang.bind(this, this._sliderChanged)); this.actor.show(); //get notified this._proxy.connectSignal('Changed', Lang.bind(this, this._getBrightness)); this._applet.menu.connect("open-state-changed", Lang.bind(this, this._getBrightnessForcedUpdate)); }, _sliderChanged: function(slider, value) { if (value < this._minimum_value) { value = this._minimum_value; } this._setBrightness(Math.round(value * 100)); }, _getBrightness: function() { //This func is called when dbus signal is received. //Only update items value when slider is not used if (!this._seeking) this._getBrightnessForcedUpdate(); }, _getBrightnessForcedUpdate: function() { this._proxy.GetPercentageRemote(Lang.bind(this, function(b) { this._updateBrightnessLabel(b); this.setValue(b / 100); })); }, _setBrightness: function(value) { this._proxy.SetPercentageRemote(value, Lang.bind(this, function(b) { this._updateBrightnessLabel(b); })); }, _updateBrightnessLabel: function(value) { this.tooltipText = this.label; if(value) this.tooltipText += ": " + value + "%"; this.tooltip.set_text(this.tooltipText); } }; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instanceId); this.metadata = metadata; this.settings = new Settings.AppletSettings(this, metadata.uuid, instanceId); this.settings.bindProperty(Settings.BindingDirection.IN, "labelinfo", "labelinfo", Lang.bind(this, this._devicesChanged), null); Main.systrayManager.registerRole("power", metadata.uuid); Main.systrayManager.registerRole("battery", metadata.uuid); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._deviceItems = [ ]; this._devices = [ ]; this._primaryDeviceId = null; this.panel_icon_name = ''; // remember the panel icon name (so we only set it when it actually changes) this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.brightness = new BrightnessSlider(this, _("Brightness"), "display-brightness", BrightnessBusName, 0.01); this.keyboard = new BrightnessSlider(this, _("Keyboard backlight"), "keyboard-brightness", KeyboardBusName, 0); this.menu.addMenuItem(this.brightness); this.menu.addMenuItem(this.keyboard); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addSettingsAction(_("Power Settings"), 'power'); this.actor.connect("scroll-event", Lang.bind(this, this._onScrollEvent)); Interfaces.getDBusProxyAsync("org.cinnamon.SettingsDaemon.Power", Lang.bind(this, function(proxy, error) { this._proxy = proxy; this._proxy.connect("g-properties-changed", Lang.bind(this, this._devicesChanged)); this._devicesChanged(); })); }, _onButtonPressEvent: function(actor, event){ //toggle keyboard brightness on middle click if(event.get_button() === 2){ this.keyboard._proxy.ToggleRemote(function(){}); } return Applet.Applet.prototype._onButtonPressEvent.call(this, actor, event); }, on_applet_clicked: function(event) { this.menu.toggle(); }, _onScrollEvent: function(actor, event) { //adjust screen brightness on scroll let direction = event.get_scroll_direction(); if (direction == Clutter.ScrollDirection.UP) { this.brightness._proxy.StepUpRemote(function(){}); } else if (direction == Clutter.ScrollDirection.DOWN) { this.brightness._proxy.StepDownRemote(function(){}); } this.brightness._getBrightnessForcedUpdate(); }, _getDeviceStatus: function(device) { let status = "" let [device_id, vendor, model, device_type, icon, percentage, state, seconds] = device; let time = Math.round(seconds / 60); let minutes = time % 60; let hours = Math.floor(time / 60); if (state == UPDeviceState.CHARGING) { if (time == 0) { status = _("Charging"); } else if (time > 60) { if (minutes == 0) { status = ngettext("Charging - %d hour until fully charged", "Charging - %d hours until fully charged", hours).format(hours); } else { /* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */ let template = _("Charging - %d %s %d %s until fully charged"); status = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes)); } } else { status = ngettext("Charging - %d minute until fully charged", "Charging - %d minutes until fully charged", minutes).format(minutes); } } else if (state == UPDeviceState.FULLY_CHARGED) { status = _("Fully charged"); } else { if (time == 0) { status = _("Using battery power"); } else if (time > 60) { if (minutes == 0) { status = ngettext("Using battery power - %d hour remaining", "Using battery power - %d hours remaining", hours).format(hours); } else { /* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */ let template = _("Using battery power - %d %s %d %s remaining"); status = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes)); } } else { status = ngettext("Using battery power - %d minute remaining", "Using battery power - %d minutes remaining", minutes).format(minutes); } } return status; }, on_panel_height_changed: function() { if (this._proxy) this._devicesChanged(); }, showDeviceInPanel: function(device) { let [device_id, vendor, model, device_type, icon, percentage, state, seconds] = device; let status = this._getDeviceStatus(device); this.set_applet_tooltip(status); let labelText = ""; if (this.labelinfo == "nothing") { ; } else if (this.labelinfo == "time" && seconds != 0) { let time = Math.round(seconds / 60); let minutes = time % 60; let hours = Math.floor(time / 60); labelText = C_("time of battery remaining", "%d:%02d").format(hours,minutes); } else if (this.labelinfo == "percentage" || (this.labelinfo == "percentage_time" && seconds == 0)) { labelText = C_("percent of battery remaining", "%d%%").format(Math.round(percentage)); } else if (this.labelinfo == "percentage_time") { let time = Math.round(seconds / 60); let minutes = Math.floor(time % 60); let hours = Math.floor(time / 60); labelText = C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) + " (" + C_("time of battery remaining", "%d:%02d").format(hours,minutes) + ")"; } this.set_applet_label(labelText); if (this.labelinfo != "nothing") { this._applet_label.set_margin_left(1.0); } if(icon){ if(this.panel_icon_name != icon){ this.panel_icon_name = icon; this.set_applet_icon_symbolic_name('battery-full'); let gicon = Gio.icon_new_for_string(icon); this._applet_icon.gicon = gicon; } } else { if (this.panel_icon_name != 'battery-full') { this.panel_icon_name = 'battery-full'; this.set_applet_icon_symbolic_name('battery-full'); } } }, _devicesChanged: function() { this._devices = []; this._primaryDevice = null; // Identify the primary battery device this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) { if (error) { this._primaryDeviceId = null; } else { if (device.length == 1) { // Primary Device can be an array of primary devices rather than a single device, in that case, take the first one. device = device[0]; } let [device_id, vendor, model, device_type, icon, percentage, state, seconds] = device this._primaryDeviceId = device_id; } })); // Scan battery devices this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) { this._deviceItems.forEach(function(i) { i.destroy(); }); this._deviceItems = []; let devices_stats = []; if (!error) { let devices = result[0]; let position = 0; for (let i = 0; i < devices.length; i++) { let [device_id, vendor, model, device_type, icon, percentage, state, seconds] = devices[i]; // Ignore AC_POWER devices if (device_type == UPDeviceType.AC_POWER) continue; // Ignore devices which state is unknown if (state == UPDeviceState.UNKNOWN) continue; let stats = "%s (%d%%)".format(deviceTypeToString(device_type), percentage); devices_stats.push(stats); this._devices.push(devices[i]); if (this._primaryDeviceId == null || this._primaryDeviceId == device_id) { // Info for the primary battery (either the primary device, or any battery device if there is no primary device) if (device_type == UPDeviceType.BATTERY && this._primaryDevice == null) { this._primaryDevice = devices[i]; } } let status = this._getDeviceStatus(devices[i]); let item = new DeviceItem (devices[i], status); this.menu.addMenuItem(item, position); this.num_devices = this.num_devices + 1; this._deviceItems.push(item); position++; } } else { global.log(error); } // The menu is built. Below, we update the information present in the panel (icon, tooltip and label) this.set_applet_enabled(true); let panel_device = null; if (this._primaryDevice != null) { this.showDeviceInPanel(this._primaryDevice); } else { if (this._devices.length == 1) { this.showDeviceInPanel(this._devices[0]); } else if (this._devices.length > 1) { // Show a summary this.set_applet_tooltip(devices_stats.join(", ")); this.set_applet_label(""); let icon = this._proxy.Icon; if(icon) { if (icon != this.panel_icon_name){ this.panel_icon_name = icon; this.set_applet_icon_symbolic_name('battery-full'); let gicon = Gio.icon_new_for_string(icon); this._applet_icon.gicon = gicon; } } else { if (this.panel_icon_name != 'battery-full') { this.panel_icon_name = 'battery-full'; this.set_applet_icon_symbolic_name('battery-full'); } } } else { // If there are no battery devices, show brightness info or disable the applet if (this.brightness.actor.visible) { // Show the brightness info this.set_applet_tooltip(_("Brightness")); this.panel_icon_name = 'display-brightness'; this.set_applet_icon_symbolic_name('display-brightness'); } else if (this.keyboard.actor.visible) { // Show the brightness info this.set_applet_tooltip(_("Keyboard backlight")); this.panel_icon_name = 'keyboard-brightness'; this.set_applet_icon_symbolic_name('keyboard-brightness'); } else { // Disable the applet this.set_applet_enabled(false); } } } })); }, on_applet_removed_from_panel: function() { Main.systrayManager.unregisterId(this.metadata.uuid); } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/sound@cinnamon.org/0000775000175000017500000000000012625666430024257 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/sound@cinnamon.org/settings-schema.json0000664000175000017500000000331212625666430030247 0ustar fabiofabio{ "showtrack": { "type": "checkbox", "default": false, "description": "Show song information on the panel" }, "truncatetext": { "type": "spinbutton", "default": 30, "min": 5, "max": 512, "units": "characters", "step": 1, "description": "Limit song information to", "dependency": "showtrack", "indent": true }, "middleClickAction": { "type": "combobox", "default": "mute", "options": { "Toggle Mute": "mute", "Toggle Play / Pause": "player" }, "description": "Action on middle click" }, "showalbum": { "type": "checkbox", "default": false, "description": "Show album art as icon" }, "hideSystray" : { "type" : "checkbox", "description" : "Hide system tray icons for compatible players", "default": true }, "separator": { "type": "separator" }, "playerControl": { "type": "checkbox", "default": true, "description": "Control Players" }, "extendedPlayerControl": { "type": "checkbox", "default": false, "description": "Show Loop and Shuffle controls", "dependency": "playerControl", "indent": true }, "positionLabelType": { "type": "combobox", "default": "length", "description": "Position label", "options": { "Song length": "length", "Countdown": "countdown" }, "dependency": "playerControl", "indent": true }, "_knownPlayers": { "type": "generic", "default": ["banshee", "vlc"] } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/sound@cinnamon.org/metadata.json0000664000175000017500000000021512625666430026730 0ustar fabiofabio{ "uuid": "sound@cinnamon.org", "name": "Sound", "description": "A Cinnamon applet to control sound and music", "icon": "stock_volume" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js0000664000175000017500000016016612625666430026114 0ustar fabiofabioconst Applet = imports.ui.applet; const Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const Interfaces = imports.misc.interfaces; const Util = imports.misc.util; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; const GLib = imports.gi.GLib; const Gvc = imports.gi.Gvc; const Pango = imports.gi.Pango; const Tooltips = imports.ui.tooltips; const Main = imports.ui.main; const Settings = imports.ui.settings; const Slider = imports.ui.slider; const MEDIA_PLAYER_2_PATH = "/org/mpris/MediaPlayer2"; const MEDIA_PLAYER_2_NAME = "org.mpris.MediaPlayer2"; const MEDIA_PLAYER_2_PLAYER_NAME = "org.mpris.MediaPlayer2.Player"; /* global values */ let players_without_seek_support = ['spotify', 'totem', 'gnome-mplayer', 'pithos']; let players_with_seek_support = [ 'clementine', 'banshee', 'rhythmbox', 'rhythmbox3', 'pragha', 'quodlibet', 'amarok', 'xnoise', 'gmusicbrowser', 'vlc', 'qmmp', 'deadbeef', 'audacious']; /* dummy vars for translation */ let x = _("Playing"); x = _("Paused"); x = _("Stopped"); const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */ const ICON_SIZE = 28; function ControlButton() { this._init.apply(this, arguments); } ControlButton.prototype = { _init: function(icon, tooltip, callback, small = false) { this.actor = new St.Bin(); this.button = new St.Button(); this.button.connect('clicked', callback); if(small) this.button.add_style_pseudo_class("small"); this.icon = new St.Icon({ icon_type: St.IconType.SYMBOLIC, icon_name: icon }); this.button.set_child(this.icon); this.actor.add_actor(this.button); this.tooltip = new Tooltips.Tooltip(this.button, tooltip); }, getActor: function() { return this.actor; }, setData: function(icon, tooltip) { this.icon.icon_name = icon; this.tooltip.set_text(tooltip); }, setActive: function(status){ this.button.change_style_pseudo_class("active", status); }, setEnabled: function(status){ this.button.change_style_pseudo_class("disabled", !status); this.button.can_focus = status; this.button.reactive = status; } } function VolumeSlider(){ this._init.apply(this, arguments); } VolumeSlider.prototype = { __proto__: PopupMenu.PopupSliderMenuItem.prototype, _init: function(applet, stream, tooltip, app_icon){ PopupMenu.PopupSliderMenuItem.prototype._init.call(this, 0); this.applet = applet; if(tooltip) this.tooltipText = tooltip + ": "; else this.tooltipText = ""; this.tooltip = new Tooltips.Tooltip(this.actor, this.tooltipText); this.connect("value-changed", Lang.bind(this, this._onValueChanged)); this.app_icon = app_icon; if (this.app_icon == null) { this.iconName = this.isMic? "microphone-sensitivity-none" : "audio-volume-muted"; this.icon = new St.Icon({icon_name: this.iconName, icon_type: St.IconType.SYMBOLIC, icon_size: 16}); } else { this.icon = new St.Icon({icon_name: this.app_icon, icon_type: St.IconType.FULLCOLOR, icon_size: 16}); } this.removeActor(this._slider); this.addActor(this.icon, {span: 0}); this.addActor(this._slider, {span: -1, expand: true}); this.connectWithStream(stream); }, connectWithStream: function(stream){ if(!stream){ this.actor.hide(); this.stream = null; } else { this.actor.show(); this.stream = stream; this.isMic = stream instanceof Gvc.MixerSource || stream instanceof Gvc.MixerSourceOutput; let mutedId = stream.connect("notify::is-muted", Lang.bind(this, this._update)); let volumeId = stream.connect("notify::volume", Lang.bind(this, this._update)); this.connect("destroy", function(){ stream.disconnect(mutedId); stream.disconnect(volumeId); }); } this._update(); }, _onValueChanged: function(){ if(!this.stream) return; let volume = this._value * this.applet._volumeMax, muted; if(this._value < .005){ volume = 0; muted = true; } else { muted = false; } this.stream.volume = volume; this.stream.push_volume(); if(this.stream.is_muted !== muted) this.stream.change_is_muted(muted); if(!this._dragging) this.applet._notifyVolumeChange(this.stream); }, _update: function(){ let value = (!this.stream || this.stream.is_muted)? 0 : this.stream.volume / this.applet._volumeMax; let percentage = Math.round(value * 100) + "%"; this.tooltip.set_text(this.tooltipText + percentage); let iconName = this._volumeToIcon(value); if (this.app_icon == null) { this.icon.icon_name = iconName; } this.setValue(value); // send data to applet this.emit("values-changed", iconName, percentage); }, _volumeToIcon: function(value){ if(value < .005) return this.isMic? "microphone-sensitivity-none" : "audio-volume-muted"; let n = Math.floor(3 * value), icon; if(n < 1) icon = "low"; else if(n < 2) icon = "medium"; else icon = "high"; return this.isMic? "microphone-sensitivity-" + icon : "audio-volume-" + icon; } } function StreamMenuSection(){ this._init.apply(this, arguments); } StreamMenuSection.prototype = { __proto__: PopupMenu.PopupMenuSection.prototype, _init: function(applet, stream){ PopupMenu.PopupMenuSection.prototype._init.call(this); let iconName = stream.icon_name; let name = stream.name; // capitalize the stream name if (name.length > 2) { name = name.charAt(0).toUpperCase() + name.slice(1); } // Trim stream name if(name.length > 16) { name = name.substring(0, 16) + "... "; } // Special cases if(name === "Banshee") { iconName = "banshee"; } else if (name === "Spotify") { iconName = "spotify"; } if(name === "VBox") { name = "Virtualbox"; iconName = "virtualbox"; } else if (iconName === "audio") { iconName = "audio-x-generic"; } let slider = new VolumeSlider(applet, stream, name, iconName); slider._slider.set_style("padding-right: 1.75em;"); this.addMenuItem(slider); } } function Player() { this._init.apply(this, arguments); } Player.prototype = { __proto__: PopupMenu.PopupMenuSection.prototype, _init: function(applet, busname, owner) { PopupMenu.PopupMenuSection.prototype._init.call(this); this.showPosition = true; this._owner = owner; this._busName = busname; this._applet = applet; this._name = this._busName.split('.')[3]; Interfaces.getDBusProxyWithOwnerAsync(MEDIA_PLAYER_2_NAME, this._busName, Lang.bind(this, function(proxy, error) { if (error) { log(error); } else { this._mediaServer = proxy; this._dbus_acquired(); } })); Interfaces.getDBusProxyWithOwnerAsync(MEDIA_PLAYER_2_PLAYER_NAME, this._busName, Lang.bind(this, function(proxy, error) { if (error) { log(error) } else { this._mediaServerPlayer = proxy; this._dbus_acquired(); } })); Interfaces.getDBusPropertiesAsync(this._busName, MEDIA_PLAYER_2_PATH, Lang.bind(this, function(proxy, error) { if (error) { log(error) } else { this._prop = proxy; this._dbus_acquired(); } })); }, _dbus_acquired: function() { if (!this._prop || !this._mediaServerPlayer || !this._mediaServer) return; let mainBox = new PopupMenu.PopupMenuSection; this.addMenuItem(mainBox); this.vertBox = new St.BoxLayout({ style_class: "sound-player", important: true, vertical: true }); mainBox.addActor(this.vertBox, { expand: false }); // Player info let playerBox = new St.BoxLayout(); this.playerIcon = new St.Icon({icon_type: St.IconType.SYMBOLIC, style_class: "popup-menu-icon"}); this.playerLabel = new St.Label({y_expand: true, y_align: Clutter.ActorAlign.CENTER}); playerBox.add_actor(this.playerIcon, { expand: true, x_fill: false, x_align: St.Align.START }); playerBox.add_actor(this.playerLabel, { expand: true, x_fill: false, x_align: St.Align.START }); if (this._mediaServer.CanRaise) { let btn = new ControlButton("go-up", _("Open Player"), Lang.bind(this, function(){ if (this._name === "spotify") { // Spotify isn't able to raise via Dbus once its main UI is closed Util.spawn(['spotify']); } else { this._mediaServer.RaiseRemote(); } this._applet.menu.close(); }), true); playerBox.add_actor(btn.actor, { expand: true, x_fill: false, x_align: St.Align.END }); } if (this._mediaServer.CanQuit) { let btn = new ControlButton("window-close", _("Quit Player"), Lang.bind(this, function(){ this._mediaServer.QuitRemote(); this._applet.menu.close(); }), true); playerBox.add_actor(btn.actor, { expand: true, x_fill: false, x_align: St.Align.END }); } this.vertBox.add_actor(playerBox, {expand: false, x_fill: false}); // Cover Box (art + track info) this._trackCover = new St.Bin({x_align: St.Align.MIDDLE}); this._trackCoverFile = this._trackCoverFileTmp = false; this.coverBox = new Clutter.Box(); let l = new Clutter.BinLayout({x_align: Clutter.BinAlignment.FILL, y_align: Clutter.BinAlignment.END}); this.coverBox.set_layout_manager(l); // Cover art this.cover = new St.Icon({icon_name: "media-optical-cd-audio", icon_size: 300, icon_type: St.IconType.FULLCOLOR}); this.coverBox.add_actor(this.cover); // Track info (artist + title) this._artist = _("Unknown Artist"); this._album = _("Unknown Album"); this._title = _("Unknown Title"); this.trackInfo = new St.BoxLayout({style_class: 'sound-player-overlay', important: true, vertical: true}); let artistInfo = new St.BoxLayout(); let artistIcon = new St.Icon({ icon_type: St.IconType.SYMBOLIC, icon_name: "system-users", style_class: 'popup-menu-icon' }); this.artistLabel = new St.Label({text:this._artist}); artistInfo.add_actor(artistIcon); artistInfo.add_actor(this.artistLabel); let titleInfo = new St.BoxLayout(); let titleIcon = new St.Icon({ icon_type: St.IconType.SYMBOLIC, icon_name: "audio-x-generic", style_class: 'popup-menu-icon' }); this.titleLabel = new St.Label({text:this._title}); titleInfo.add_actor(titleIcon); titleInfo.add_actor(this.titleLabel); this.trackInfo.add_actor(artistInfo); this.trackInfo.add_actor(titleInfo); this.coverBox.add_actor(this.trackInfo); this._trackCover.set_child(this.coverBox); this.vertBox.add_actor(this._trackCover); // Playback controls let trackControls = new St.Bin({x_align: St.Align.MIDDLE}); this._prevButton = new ControlButton("media-skip-backward", _("Previous"), Lang.bind(this, function(){ this._mediaServerPlayer.PreviousRemote(); })); this._playButton = new ControlButton("media-playback-start", _("Play"), Lang.bind(this, function(){ this._mediaServerPlayer.PlayPauseRemote(); })); this._stopButton = new ControlButton("media-playback-stop", _("Stop"), Lang.bind(this, function(){ this._mediaServerPlayer.StopRemote(); })); this._nextButton = new ControlButton("media-skip-forward", _("Next"), Lang.bind(this, function(){ this._mediaServerPlayer.NextRemote(); })); this.trackInfo.add_actor(trackControls); this.controls = new St.BoxLayout(); this.controls.add_actor(this._prevButton.getActor()); this.controls.add_actor(this._playButton.getActor()); this.controls.add_actor(this._stopButton.getActor()); this.controls.add_actor(this._nextButton.getActor()); trackControls.set_child(this.controls); if(this._mediaServerPlayer.LoopStatus){ this._loopButton = new ControlButton("media-playlist-consecutive", _("Consecutive Playing"), Lang.bind(this, this._toggleLoopStatus)); this._loopButton.actor.visible = this._applet.extendedPlayerControl; this.controls.add_actor(this._loopButton.getActor()); this._setLoopStatus(this._mediaServerPlayer.LoopStatus); } if(this._mediaServerPlayer.Shuffle !== undefined){ this._shuffleButton = new ControlButton("media-playlist-shuffle", _("No Shuffle"), Lang.bind(this, this._toggleShuffle)); this._shuffleButton.actor.visible = this._applet.extendedPlayerControl; this.controls.add_actor(this._shuffleButton.getActor()); this._setShuffle(this._mediaServerPlayer.Shuffle); } // Position slider this._positionSlider = new Slider.Slider(0, true); this._currentTimeLabel = new St.Label({text: "0:00"}); this._songLengthLabel = new St.Label({text: "0:00"}); this._seeking = false; this._positionSlider.connect('drag-begin', Lang.bind(this, function(item) { this._seeking = true; })); this._positionSlider.connect('drag-end', Lang.bind(this, function(item) { this._seeking = false; this._setPosition("slider"); })); this._positionSlider.connect('value-changed', Lang.bind(this, function(item) { //update the label virtually if we are seeking, else set the value (scroll event) if(this._seeking) this._updateTimeLabel(item._value * this._songLength); else this._setPosition("slider"); })); this.vertBox.add_actor(this._positionSlider.actor); this._applet._updatePlayerMenuItems(); /* these players don't support seek */ if (!this._getCanSeek() || this._mediaServerPlayer.Rate != 1) { this.showPosition = false; this._positionSlider.actor.hide(); } this._timeoutId = 0; this._setStatus(this._mediaServerPlayer.PlaybackStatus); this._trackId = {}; this._setMetadata(this._mediaServerPlayer.Metadata); this._currentTime = 0; this._timerTicker = 0; this._wantedSeekValue = 0; this._updatePositionSlider(); this._mediaServerPlayerId = this._mediaServerPlayer.connectSignal('Seeked', Lang.bind(this, function(id, sender, value) { if (value > 0) { this._setPosition(value); } // Seek initiated by the position slider else if (this._wantedSeekValue > 0) { // Some broken gstreamer players (Banshee) reports always 0 // when the track is seeked so we set the position at the // value we set on the slider this._setPosition(this._wantedSeekValue); } // Seek value send by the player else this._setPosition(value); this._wantedSeekValue = 0; })); this._propChangedId = this._prop.connectSignal('PropertiesChanged', Lang.bind(this, function(proxy, sender, [iface, props]) { if (props.PlaybackStatus) this._setStatus(props.PlaybackStatus.unpack()); if (props.Metadata) this._setMetadata(props.Metadata.deep_unpack()); if (props.CanGoNext || props.CanGoPrevious) this._updateControls(); if (props.LoopStatus) this._setLoopStatus(props.LoopStatus.unpack()); if (props.Shuffle) this._setShuffle(props.Shuffle.unpack()); })); //get the desktop entry and pass it to the applet this._prop.GetRemote(MEDIA_PLAYER_2_NAME, "DesktopEntry", Lang.bind(this, function(value){ this._applet.passDesktopEntry(value[0].unpack()); })); this._getPosition(); }, _getName: function() { return this._name.charAt(0).toUpperCase() + this._name.slice(1); }, _setName: function(status) { this.playerLabel.set_text(this._getName() + " - " + _(status)); }, _updateControls: function() { this._prop.GetRemote(MEDIA_PLAYER_2_PLAYER_NAME, 'CanGoNext', Lang.bind(this, function(value, err) { let canGoNext = true; if (!err) canGoNext = value[0].unpack(); this._nextButton.setEnabled(canGoNext); }) ); this._prop.GetRemote(MEDIA_PLAYER_2_PLAYER_NAME, 'CanGoPrevious', Lang.bind(this, function(value, err) { let canGoPrevious = true; if (!err) canGoPrevious = value[0].unpack(); this._prevButton.setEnabled(canGoPrevious); }) ); }, _updatePositionSlider: function(position) { this._canSeek = this._getCanSeek(); if (this._songLength == 0 || position == false) this._canSeek = false }, _setPosition: function(value) { if(value === "slider"){ let time = this._positionSlider._value * this._songLength; this._wantedSeekValue = Math.round(time * 1000000); this._mediaServerPlayer.SetPositionRemote(this._trackObj, time * 1000000); this._updateTimeLabel(time); } else if (value == null && this._playerStatus != 'Stopped') { this._updatePositionSlider(false); } else { this._currentTime = value / 1000000; this._updateTimer(); } }, _getPosition: function() { this._prop.GetRemote(MEDIA_PLAYER_2_PLAYER_NAME, 'Position', Lang.bind(this, function(position, ex) { if (!ex) { this._setPosition(position[0].get_int64()); } })); }, _getCanSeek: function() { let can_seek = true; this._prop.GetRemote(MEDIA_PLAYER_2_PLAYER_NAME, 'CanSeek', Lang.bind(this, function(position, ex) { if (!ex) { can_seek = position[0].get_boolean(); } })); // Some players say they "CanSeek" but don't actually give their position over dbus (spotify for instance) for (let i = 0; i < players_without_seek_support.length; i++) { if (players_without_seek_support[i] === this._name) { can_seek = false; break; } } return can_seek; }, _setMetadata: function(metadata) { if (!metadata) return; if (metadata["mpris:length"]) { this._stopTimer(); if (this._playerStatus == "Playing") this._runTimer(); // song length in secs this._songLength = metadata["mpris:length"].unpack() / 1000000; } else { this._songLength = 0; this._stopTimer(); } if (metadata["xesam:artist"]) { this._artist = metadata["xesam:artist"].deep_unpack().join(", "); } else this._artist = _("Unknown Artist"); this.artistLabel.set_text(this._artist); if (metadata["xesam:album"]) this._album = metadata["xesam:album"].unpack(); else this._album = _("Unknown Album"); if (metadata["xesam:title"]) this._title = metadata["xesam:title"].unpack(); else this._title = _("Unknown Title"); this.titleLabel.set_text(this._title); if (metadata["mpris:trackid"]) { this._trackObj = metadata["mpris:trackid"].unpack(); } let change = false; if (metadata["mpris:artUrl"]) { if (this._trackCoverFile != metadata["mpris:artUrl"].unpack()) { this._trackCoverFile = metadata["mpris:artUrl"].unpack(); if ( this._name === "spotify" ) this._trackCoverFile = this._trackCoverFile.replace("/thumb/", "/300/"); change = true; } } else { if (this._trackCoverFile != false) { this._trackCoverFile = false; change = true; } } if (change) { if (this._trackCoverFile) { let cover_path = ""; if (this._trackCoverFile.match(/^http/)) { this._hideCover(); let cover = Gio.file_new_for_uri(decodeURIComponent(this._trackCoverFile)); this._trackCoverFileTmp = Gio.file_new_tmp('XXXXXX.mediaplayer-cover')[0]; cover.read_async(null, null, Lang.bind(this, this._onReadCover)); } else { cover_path = decodeURIComponent(this._trackCoverFile); cover_path = cover_path.replace("file://", ""); this._showCover(cover_path); } } else this._showCover(false); } this._applet.setAppletTextIcon(this, true); }, _setStatus: function(status) { if (!status) return; this._updatePositionSlider(); this._playerStatus = status; if (status == "Playing") { this._playButton.setData("media-playback-pause", _("Pause")); this.playerIcon.set_icon_name("media-playback-start"); this._applet.setAppletTextIcon(this, true); this._runTimer(); } else if (status == "Paused") { this._playButton.setData("media-playback-start", _("Play")); this.playerIcon.set_icon_name("media-playback-pause"); this._applet.setAppletTextIcon(this, false); this._pauseTimer(); } else if (status == "Stopped") { this._playButton.setData("media-playback-start", _("Play")); this.playerIcon.set_icon_name("media-playback-stop"); this._applet.setAppletTextIcon(this, false); this._stopTimer(); } else { this._applet.setAppletTextIcon(this, false); } this._setName(status); }, _toggleLoopStatus: function(){ let mapping = { "None": "Playlist", "Playlist": "Track", "Track": "None" }; this._mediaServerPlayer.LoopStatus = mapping[this._mediaServerPlayer.LoopStatus]; this._setLoopStatus(this._mediaServerPlayer.LoopStatus); }, _setLoopStatus: function(status){ if(status === "None") this._loopButton.setData("media-playlist-consecutive-symbolic", _("Consecutive Playing")); else if(status === "Track") this._loopButton.setData("media-playlist-repeat-song", _("Repeat Single")); else if(status === "Playlist") this._loopButton.setData("media-playlist-repeat", _("Repeat All")); this._loopButton.setActive(status !== "None"); }, _toggleShuffle: function(){ this._mediaServerPlayer.Shuffle = !this._mediaServerPlayer.Shuffle; }, _setShuffle: function(status){ this._shuffleButton.setData("media-playlist-shuffle", status? _("Shuffle") : _("No Shuffle")); this._shuffleButton.setActive(status); }, _updateTimer: function() { if (!this._seeking && this.showPosition && this._canSeek) { if (!isNaN(this._currentTime) && !isNaN(this._songLength) && this._currentTime > 0) this._positionSlider.setValue(this._currentTime / this._songLength); else this._positionSlider.setValue(0); } if(!this._seeking) this._updateTimeLabel(); }, _updateTimeLabel: function(time){ if(time === undefined) time = this._currentTime; this._currentTimeLabel.text = this._formatTime(time); if(this._applet.positionLabelType === "length") this._songLengthLabel.text = this._formatTime(this._songLength); else this._songLengthLabel.text = "-" + this._formatTime(this._songLength - time); }, _runTimerCallback: function() { if (this._playerStatus == 'Playing') { if (this._timerTicker < 10) { this._currentTime += 1; this._timerTicker++; this._updateTimer(); } else { this._getPosition(); this._timerTicker = 0; } return true; } return false; }, _runTimer: function() { if (this._canSeek) { if (this._timeoutId != 0) { Mainloop.source_remove(this._timeoutId); this._timeoutId = 0; } if (this._playerStatus == 'Playing') { this._getPosition(); this._timerTicker = 0; this._timeoutId = Mainloop.timeout_add(1000, Lang.bind(this, this._runTimerCallback)); } } }, _pauseTimer: function() { if (this._timeoutId != 0) { Mainloop.source_remove(this._timeoutId); this._timeoutId = 0; } this._updateTimer(); }, _stopTimer: function() { this._currentTime = 0; this._pauseTimer(); this._updateTimer(); }, _formatTime: function(s) { let ms = s * 1000; let msSecs = (1000); let msMins = (msSecs * 60); let msHours = (msMins * 60); let numHours = Math.floor(ms/msHours); let numMins = Math.floor((ms - (numHours * msHours)) / msMins); let numSecs = Math.floor((ms - (numHours * msHours) - (numMins * msMins))/ msSecs); if (numSecs < 10) numSecs = "0" + numSecs.toString(); if (numMins < 10 && numHours > 0) numMins = "0" + numMins.toString(); if (numHours > 0) numHours = numHours.toString() + ":"; else numHours = ""; return numHours + numMins.toString() + ":" + numSecs.toString(); }, _onReadCover: function(cover, result) { let inStream = cover.read_finish(result); let outStream = this._trackCoverFileTmp.replace(null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null, null); outStream.splice_async(inStream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET, 0, null, Lang.bind(this, this._onSavedCover)); }, _onSavedCover: function(outStream, result) { outStream.splice_finish(result, null); let cover_path = this._trackCoverFileTmp.get_path(); this._showCover(cover_path); }, _hideCover: function() { /*Tweener.addTween(this.trackCoverContainer, { opacity: 0, time: 0.3, transition: 'easeOutCubic', });*/ }, _showCover: function(cover_path) { /*Tweener.addTween(this._trackCover, { opacity: 0, time: 0.3, transition: 'easeOutCubic', onComplete: Lang.bind(this, function() {*/ this.coverBox.remove_actor(this.cover); if (! cover_path || ! GLib.file_test(cover_path, GLib.FileTest.EXISTS)) { this.cover = new St.Icon({style_class: 'sound-player-generic-coverart', important: true, icon_name: "media-optical-cd-audio", icon_size: 300, icon_type: St.IconType.FULLCOLOR}); cover_path = null; } else { this.cover = new Clutter.Texture({width: 300, keep_aspect_ratio: true, filter_quality: 2, filename: cover_path}); } this.coverBox.add_actor(this.cover); this.coverBox.set_child_below_sibling(this.cover, this.trackInfo); this._applet.setAppletTextIcon(this, cover_path); /*Tweener.addTween(this._trackCover, { opacity: 255, time: 0.3, transition: 'easeInCubic' }); }) });*/ }, onSettingsChanged: function(){ this._loopButton.actor.visible = this._applet.extendedPlayerControl; this._shuffleButton.actor.visible = this._applet.extendedPlayerControl; }, destroy: function() { if (this._timeoutId != 0) { Mainloop.source_remove(this._timeoutId); this._timeoutId = 0; } if (this._mediaServerPlayer) this._mediaServerPlayer.disconnectSignal(this._mediaServerPlayerId); if (this._prop) this._prop.disconnectSignal(this._propChangedId); PopupMenu.PopupMenuSection.prototype.destroy.call(this); } } function MediaPlayerLauncher(app, menu) { this._init(app, menu); } MediaPlayerLauncher.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function (app, menu) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {}); this._app = app; this._menu = menu; this.label = new St.Label({ text: app.get_name() }); this.addActor(this.label); this._icon = app.create_icon_texture(ICON_SIZE); this.addActor(this._icon, { expand: false }); }, activate: function (event) { this._menu.actor.hide(); this._app.activate_full(-1, event.get_time()); return true; } }; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instanceId); try { this.metadata = metadata; this.settings = new Settings.AppletSettings(this, metadata.uuid, instanceId); this.settings.bindProperty(Settings.BindingDirection.IN, "showtrack", "showtrack", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "middleClickAction", "middleClickAction"); this.settings.bindProperty(Settings.BindingDirection.IN, "showalbum", "showalbum", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "truncatetext", "truncatetext", this.on_settings_changed, null); this.settings.bindProperty(Settings.BindingDirection.IN, "hideSystray", "hideSystray", function() { if (this.hideSystray) this.registerSystrayIcons(); else this.unregisterSystrayIcons(); }); this.settings.bindProperty(Settings.BindingDirection.IN, "playerControl", "playerControl", this.on_settings_changed); this.settings.bindProperty(Settings.BindingDirection.IN, "extendedPlayerControl", "extendedPlayerControl", function(){ for(let i in this._players) this._players[i].onSettingsChanged(); }); this.settings.bindProperty(Settings.BindingDirection.IN, "positionLabelType", "positionLabelType", function(){ for(let i in this._players) this._players[i].onSettingsChanged(); }); this.settings.bindProperty(Settings.BindingDirection.OUT, "_knownPlayers", "_knownPlayers"); if (this.hideSystray) this.registerSystrayIcons(); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.set_applet_icon_symbolic_name('audio-x-generic'); this._players = {}; this._activePlayer = null; Interfaces.getDBusAsync(Lang.bind(this, function (proxy, error) { this._dbus = proxy; // player DBus name pattern let name_regex = /^org\.mpris\.MediaPlayer2\./; // load players this._dbus.ListNamesRemote(Lang.bind(this, function(names) { for (let n in names[0]) { let name = names[0][n]; if (name_regex.test(name)) { this._dbus.GetNameOwnerRemote(name, Lang.bind(this, function(owner) { this._addPlayer(name, owner); } )); } } } )); // watch players this._ownerChangedId = this._dbus.connectSignal('NameOwnerChanged', Lang.bind(this, function(proxy, sender, [name, old_owner, new_owner]) { if (name_regex.test(name)) { if (new_owner && !old_owner) this._addPlayer(name, new_owner); else if (old_owner && !new_owner) this._removePlayer(name, old_owner); else this._changePlayerOwner(name, old_owner, new_owner); } } )); })); this._control = new Gvc.MixerControl({ name: 'Cinnamon Volume Control' }); this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged)); this._control.connect('output-added', Lang.bind(this, this._onDeviceAdded, "output")); this._control.connect('output-removed', Lang.bind(this, this._onDeviceRemoved, "output")); this._control.connect('active-output-update', Lang.bind(this, this._onDeviceUpdate, "output")); this._control.connect('input-added', Lang.bind(this, this._onDeviceAdded, "input")); this._control.connect('input-removed', Lang.bind(this, this._onDeviceRemoved, "input")); this._control.connect('active-input-update', Lang.bind(this, this._onDeviceUpdate, "input")); this._control.connect('stream-added', Lang.bind(this, this._onStreamAdded)); this._control.connect('stream-removed', Lang.bind(this, this._onStreamRemoved)); this._volumeMax = 1*this._control.get_vol_max_norm(); // previously was 1.5*this._control.get_vol_max_norm();, but we'd need a little mark on the slider to make it obvious to the user we're going over 100%.. this._streams = []; this._devices = []; this._recordingAppsNum = 0; this._output = null; this._outputMutedId = 0; this._outputIcon = "audio-volume-muted"; this._input = null; this._inputMutedId = 0; this._icon_name = ''; this._icon_path = null; this._iconTimeoutId = 0; this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.mute_out_switch = new PopupMenu.PopupSwitchMenuItem(_("Mute output"), false); this.mute_in_switch = new PopupMenu.PopupSwitchMenuItem(_("Mute input"), false); this._applet_context_menu.addMenuItem(this.mute_out_switch); this._applet_context_menu.addMenuItem(this.mute_in_switch); this._applet_context_menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem); this._outputApplicationsMenu = new PopupMenu.PopupSubMenuMenuItem(_("Applications")); this._selectOutputDeviceItem = new PopupMenu.PopupSubMenuMenuItem(_("Output device")); this._applet_context_menu.addMenuItem(this._outputApplicationsMenu); this._applet_context_menu.addMenuItem(this._selectOutputDeviceItem); this._outputApplicationsMenu.actor.hide(); this._selectOutputDeviceItem.actor.hide(); this._inputSection = new PopupMenu.PopupMenuSection; this._inputVolumeSection = new VolumeSlider(this, null, _("Microphone"), null); this._selectInputDeviceItem = new PopupMenu.PopupSubMenuMenuItem(_("Input device")); this._inputSection.addMenuItem(this._inputVolumeSection); this._inputSection.addMenuItem(this._selectInputDeviceItem); this._applet_context_menu.addMenuItem(this._inputSection); this._selectInputDeviceItem.actor.hide(); this._inputSection.actor.hide(); this._applet_context_menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem); this.mute_out_switch.connect('toggled', Lang.bind(this, this._toggle_out_mute)); this.mute_in_switch.connect('toggled', Lang.bind(this, this._toggle_in_mute)); this._control.open(); this._volumeControlShown = false; this._showFixedElements(); let appsys = Cinnamon.AppSystem.get_default(); appsys.connect("installed-changed", Lang.bind(this, this._updateLaunchPlayer)); } catch (e) { global.logError(e); } }, on_settings_changed : function() { if(this.playerControl && this._activePlayer) this.setAppletTextIcon(this._players[this._activePlayer], true); else this.setAppletTextIcon(); this._changeActivePlayer(this._activePlayer); }, on_applet_removed_from_panel : function() { if (this.hideSystray) this.unregisterSystrayIcons(); if (this._iconTimeoutId) { Mainloop.source_remove(this._iconTimeoutId); } this._dbus.disconnectSignal(this._ownerChangedId); for(let i in this._players) this._players[i].destroy(); }, on_applet_clicked: function(event) { this.menu.toggle(); }, _toggle_out_mute: function() { if (this._output.is_muted) { this._output.change_is_muted(false); this.mute_out_switch.setToggleState(false); } else { this._output.change_is_muted(true); this.mute_out_switch.setToggleState(true); } }, _toggle_in_mute: function() { if (this._input.is_muted) { this._input.change_is_muted(false); this.mute_in_switch.setToggleState(false); } else { this._input.change_is_muted(true); this.mute_in_switch.setToggleState(true); } }, _onScrollEvent: function(actor, event) { let direction = event.get_scroll_direction(); let currentVolume = this._output.volume; if (direction == Clutter.ScrollDirection.DOWN) { let prev_muted = this._output.is_muted; this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP); if (this._output.volume < 1) { this._output.volume = 0; if (!prev_muted) this._output.change_is_muted(true); } this._output.push_volume(); } else if (direction == Clutter.ScrollDirection.UP) { this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP); this._output.push_volume(); this._output.change_is_muted(false); } this._notifyVolumeChange(this._output); }, _onButtonPressEvent: function (actor, event) { //mute or play / pause players on middle click if(event.get_button() === 2){ if(this.middleClickAction === "mute") this._toggle_out_mute(); else if(this.middleClickAction === "player") this._players[this._activePlayer]._mediaServerPlayer.PlayPauseRemote(); } return Applet.Applet.prototype._onButtonPressEvent.call(this, actor, event); }, setIcon: function(icon, source) { if(this._iconTimeoutId){ Mainloop.source_remove(this._iconTimeoutId); this._iconTimeoutId = null; } //save the icon if(source){ if(source === "output") this._outputIcon = icon; else this._playerIcon = [icon, source === "player-path"]; } if(this.playerControl && this._activePlayer && this._playerIcon[0]){ if(source === "output"){ //if we have an active player, but are changing the volume, show the output icon and after three seconds change back to the player icon this.set_applet_icon_symbolic_name(this._outputIcon); this._iconTimeoutId = Mainloop.timeout_add(3000, Lang.bind(this, function(){ this._iconTimeoutId = null; this.setIcon(); })); } else { //if we have an active player and want to change the icon, change it immediately if(this._playerIcon[1]) this.set_applet_icon_path(this._playerIcon[0]); else this.set_applet_icon_symbolic_name(this._playerIcon[0]); } } else //if we have no active player show the output icon this.set_applet_icon_symbolic_name(this._outputIcon); }, setAppletIcon: function(player, path) { if (path) { if (path === true) { // Restore the icon path from the saved path. path = this._icon_path; } else { this._icon_path = path; } } else if (path === null) { // This track has no art, erase the saved path. this._icon_path = null; } if (this.showalbum) { if (path && player && (player === true || player._playerStatus == 'Playing')) { this.setIcon(path, "player-path"); } else { this.setIcon('media-optical-cd-audio', 'player-name'); } } else { this.setIcon('audio-x-generic', 'player-name'); } }, setAppletText: function(player) { let title_text = ""; if (this.showtrack && player && player._playerStatus == 'Playing') { title_text = player._title + ' - ' + player._artist; if (this.truncatetext < title_text.length) { title_text = title_text.substr(0, this.truncatetext) + "..."; } } this.set_applet_label(title_text); }, setAppletTextIcon: function(player, icon) { if (player && player._owner != this._activePlayer) return; this.setAppletIcon(player, icon); this.setAppletText(player); }, _isInstance: function(busName) { // MPRIS instances are in the form // org.mpris.MediaPlayer2.name.instanceXXXX // ...except for VLC, which to this day uses // org.mpris.MediaPlayer2.name-XXXX return busName.split('.').length > 4 || /^org\.mpris\.MediaPlayer2\.vlc-\d+$/.test(busName); }, _addPlayer: function(busName, owner) { let position; if (this._players[owner]) { let prevName = this._players[owner]._busName; // HAVE: ADDING: ACTION: // master master reject, cannot happen // master instance upgrade to instance // instance master reject, duplicate // instance instance reject, cannot happen if (this._isInstance(busName) && !this._isInstance(prevName)) this._players[owner]._busName = busName; else return; } else if (owner) { this._players[owner] = new Player(this, busName, owner); this._changeActivePlayer(owner); this._updatePlayerMenuItems(); this.setAppletTextIcon(); } }, _removePlayer: function(busName, owner) { if (this._players[owner] && this._players[owner]._busName == busName) { this._players[owner].destroy(); delete this._players[owner]; if(this._activePlayer == owner){ //set _activePlayer to null if we have none now, or to the first value in the players list this._activePlayer = null; for (let i in this._players) { this._changeActivePlayer(i); break; } } this._updatePlayerMenuItems(); this.setAppletTextIcon(); } }, _changePlayerOwner: function(busName, oldOwner, newOwner) { if (this._players[oldOwner] && busName == this._players[oldOwner]._busName) { this._players[newOwner] = this._players[oldOwner]; this._players[newOwner]._owner = newOwner; delete this._players[oldOwner]; if (this._activePlayer == oldOwner) this._activePlayer = newOwner; } }, //will be called by an instance of #Player passDesktopEntry: function(entry){ //do we know already this player? for(let i = 0, l = this._knownPlayers.length; i < l; ++i){ if(this._knownPlayers[i] === entry) return } //No, save it to _knownPlayers and update player list this._knownPlayers.push(entry); this._knownPlayers.save(); this._updateLaunchPlayer(); }, _showFixedElements: function() { //we'll show the launch player item or the selector item + a player section this._launchPlayerItem = new PopupMenu.PopupSubMenuMenuItem(_("Launch player")); this.menu.addMenuItem(this._launchPlayerItem); this._updateLaunchPlayer(); // this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem); //between these two separators will be the player MenuSection (position 3) this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem); this._outputVolumeSection = new VolumeSlider(this, null, _("Volume"), null); this._outputVolumeSection.connect("values-changed", Lang.bind(this, this._outputValuesChanged)); this.menu.addMenuItem(this._outputVolumeSection); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem); this.menu.addSettingsAction(_("Sound Settings"), 'sound'); }, _updateLaunchPlayer: function() { let availablePlayers = []; let appsys = Cinnamon.AppSystem.get_default(); //_knownPlayers is an array containing the paths of desktop files for(let i = 0, l = this._knownPlayers.length; i < l; ++i){ let app = appsys.lookup_app(this._knownPlayers[i] + ".desktop"); if (app) availablePlayers.push(app); } this._launchPlayerItem.menu.removeAll(); if (availablePlayers.length > 0){ for (var p = 0; p < availablePlayers.length; p++){ let playerApp = availablePlayers[p]; let menuItem = new MediaPlayerLauncher(playerApp, this._launchPlayerItem.menu); this._launchPlayerItem.menu.addMenuItem(menuItem); } } else this._launchPlayerItem.actor.hide(); }, _updatePlayerMenuItems: function() { if (this.playerControl && this._activePlayer) { this._launchPlayerItem.actor.hide(); //go through the players list and create the player info (icon + label) for(let i in this._players) { let info = this._players[i].playerInfo, item; item = new PopupMenu.PopupBaseMenuItem; item.activate = Lang.bind(this, function(event, keepMenu, player){ this._changeActivePlayer(player); }, i); } } else { if(this._launchPlayerItem.menu.numMenuItems) { this._launchPlayerItem.actor.show(); } else { this._launchPlayerItem.actor.hide(); } } }, _changeActivePlayer: function(player) { if (this._activePlayer) this.menu.box.remove_actor(this._players[this._activePlayer].actor); this._activePlayer = player; if(this.playerControl) this.menu.addMenuItem(this._players[player], 1); this._updatePlayerMenuItems(); }, _notifyVolumeChange: function(stream) { Main.soundManager.playVolume('volume', stream.decibel); }, _mutedChanged: function(object, param_spec, property) { if (property == "_output"){ this.mute_out_switch.setToggleState(this._output.is_muted); } else if (property == "_input"){ this.mute_in_switch.setToggleState(this._input.is_muted); } }, _outputValuesChanged: function(actor, iconName, percentage) { this.setIcon(iconName, "output"); this.set_applet_tooltip(_("Volume") + ": " + percentage); }, _onControlStateChanged: function() { if (this._control.get_state() == Gvc.MixerControlState.READY) { this._readOutput(); this._readInput(); this.actor.show(); } else { this.actor.hide(); } }, _readOutput: function() { if (this._outputMutedId) { this._output.disconnect(this._outputMutedId); this._outputMutedId = 0; } this._output = this._control.get_default_sink(); if (this._output) { this._outputVolumeSection.connectWithStream(this._output); this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output')); this._mutedChanged (null, null, '_output'); } else { this.setIcon("audio-volume-muted-symbolic", "output"); } }, _readInput: function() { if (this._inputMutedId) { this._input.disconnect(this._inputMutedId); this._inputMutedId = 0; } this._input = this._control.get_default_source(); if (this._input) { this._inputVolumeSection.connectWithStream(this._input); this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input')); this._mutedChanged (null, null, '_input'); } else { this._inputSection.actor.hide(); } }, _onDeviceAdded: function(control, id, type){ let device = this._control["lookup_" + type + "_id"](id); let item = new PopupMenu.PopupMenuItem(device.description); item.activate = Lang.bind(this, function(){ this._control["change_" + type](device); }); let bin = new St.Bin({ x_align: St.Align.END, style_class: 'popup-inactive-menu-item' }); let label = new St.Label({ text: device.origin }); label.set_style("padding-right: 1.75em;"); bin.add_actor(label); item.addActor(bin, { expand: true, span: -1, align: St.Align.END }); let selectItem = this["_select" + type[0].toUpperCase() + type.slice(1) + "DeviceItem"]; selectItem.menu.addMenuItem(item); //show the menu if we have more than two devices if(selectItem.menu.numMenuItems > 1) selectItem.actor.show(); this._devices.push({id: id, type: type, item: item}); }, _onDeviceRemoved: function(control, id, type){ for(let i = 0, l = this._devices.length; i < l; ++i){ if(this._devices[i].type === type && this._devices[i].id === id){ let device = this._devices[i]; if(device.item) device.item.destroy(); //hide submenu if showing them is unnecessary let selectItem = this["_select" + type[0].toUpperCase() + type.slice(1) + "DeviceItem"]; if(selectItem.menu.numMenuItems <= 1) selectItem.actor.hide(); this._devices.splice(i, 1); break; } } }, _onDeviceUpdate: function(control, id, type){ this["_read" + type[0].toUpperCase() + type.slice(1)](); for(let i = 0, l = this._devices.length; i < l; ++i){ if(this._devices[i].type === type) this._devices[i].item.setShowDot(id === this._devices[i].id); } }, _onStreamAdded: function(control, id){ let stream = this._control.lookup_stream_id(id); let appId = stream.application_id; if(stream.is_virtual || appId === "org.freedesktop.libcanberra"){ //sort out unwanted streams return; } if(stream instanceof Gvc.MixerSinkInput){ //for sink inputs, add a menuitem to the application submenu let item = new StreamMenuSection(this, stream); this._outputApplicationsMenu.menu.addMenuItem(item); this._outputApplicationsMenu.actor.show(); this._streams.push({id: id, type: "SinkInput", item: item}); } else if(stream instanceof Gvc.MixerSourceOutput){ //for source outputs, only show the input section this._streams.push({id: id, type: "SourceOutput"}); if(this._recordingAppsNum++ === 0) this._inputSection.actor.show(); } }, _onStreamRemoved: function(control, id){ for(let i = 0, l = this._streams.length; i < l; ++i){ if(this._streams[i].id === id){ let stream = this._streams[i]; if(stream.item) stream.item.destroy(); //hide submenus or sections if showing them is unnecessary if(stream.type === "SinkInput"){ if(this._outputApplicationsMenu.menu.numMenuItems === 0) this._outputApplicationsMenu.actor.hide(); } else if(stream.type === "SourceOutput"){ if(--this._recordingAppsNum === 0) this._inputSection.actor.hide(); } this._streams.splice(i, 1); break; } } }, registerSystrayIcons: function() { for (let i = 0; i < players_with_seek_support.length; i++) { Main.systrayManager.registerRole(players_with_seek_support[i], this.metadata.uuid); } for (let i = 0; i < players_without_seek_support.length; i++) { Main.systrayManager.registerRole(players_without_seek_support[i], this.metadata.uuid); } }, unregisterSystrayIcons: function() { Main.systrayManager.unregisterId(this.metadata.uuid); } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/recent@cinnamon.org/0000775000175000017500000000000012625666430024407 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/recent@cinnamon.org/metadata.json0000664000175000017500000000026312625666430027063 0ustar fabiofabio{ "uuid": "recent@cinnamon.org", "name": "Recent documents", "description": "Access the documents you recently opened", "icon": "document-open-recent", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js0000664000175000017500000001100312625666430026225 0ustar fabiofabioconst DocInfo = imports.misc.docInfo; const Gtk = imports.gi.Gtk; const Gio = imports.gi.Gio; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; const Lang = imports.lang; const Applet = imports.ui.applet; const PRIVACY_SCHEMA = "org.cinnamon.desktop.privacy"; const REMEMBER_RECENT_KEY = "remember-recent-files"; function MyPopupMenuItem() { this._init.apply(this, arguments); } MyPopupMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(icon, text, params) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params); this.box = new St.BoxLayout({ style_class: 'popup-combobox-item' }); this.icon = icon; this.box.add(this.icon); this.label = new St.Label({ text: text }); this.box.add(this.label); this.addActor(this.box); } }; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("document-open-recent"); this.set_applet_tooltip(_("Recent documents")); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.RecentManager = new DocInfo.DocManager(); this.privacy_settings = new Gio.Settings( {schema_id: PRIVACY_SCHEMA} ); this._display(); this.RecentManager.connect('changed', Lang.bind(this, this._redisplay)); this.privacy_settings.connect("changed::" + REMEMBER_RECENT_KEY, Lang.bind(this, this._redisplay)); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _display: function() { if (!this.privacy_settings.get_boolean(REMEMBER_RECENT_KEY)) { let item = new PopupMenu.PopupMenuItem(_("Recent file tracking is currently disabled.")); item.actor.reactive = false; this.menu.addMenuItem(item); let icon = new St.Icon({ icon_name: 'ok', icon_type: St.IconType.FULLCOLOR, icon_size: 16 }); item = new MyPopupMenuItem(icon, _("Click here to enable it"), {}); item.connect("activate", Lang.bind(this, function () { this.privacy_settings.set_boolean(REMEMBER_RECENT_KEY, true); })) this.menu.addMenuItem(item); return; } for (let id = 0; id < 15 && id < this.RecentManager._infosByTimestamp.length; id++) { let icon = this.RecentManager._infosByTimestamp[id].createIcon(22); let menuItem = new MyPopupMenuItem(icon, this.RecentManager._infosByTimestamp[id].name, {}); this.menu.addMenuItem(menuItem); menuItem.connect('activate', Lang.bind(this, this._launchFile, this.RecentManager._infosByTimestamp[id])); } if (this.RecentManager._infosByTimestamp.length > 0) { this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); let icon = new St.Icon({ icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC, icon_size: 22 }); let menuItem = new MyPopupMenuItem(icon, _("Clear list"), {}); this.menu.addMenuItem(menuItem); menuItem.connect('activate', Lang.bind(this, this._clearAll)); } else { this.menu.addMenuItem(new PopupMenu.PopupMenuItem(_("No recent documents"))); } }, _redisplay: function() { this.menu.removeAll(); this._display(); }, _launchFile: function(a, b, c, docinfo) { docinfo.launch(); }, _clearAll: function() { let GtkRecent = new Gtk.RecentManager(); GtkRecent.purge_items(); }, destroy: function() { this.RecentManager.disconnectAll(); this.actor._delegate = null; this.menu.destroy(); this.actor.destroy(); this.emit('destroy'); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/0000775000175000017500000000000012625666430024555 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/icons/0000775000175000017500000000000012625666430025670 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/icons/inhibit-symbolic.svg0000664000175000017500000001100412625666430031652 0ustar fabiofabio Gnome Symbolic Icon Theme image/svg+xml Gnome Symbolic Icon Theme Z Z ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/icons/inhibit-active-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/icons/inhibit-active-symbolic.s0000664000175000017500000001225712625666430032601 0ustar fabiofabio Gnome Symbolic Icon Theme image/svg+xml Gnome Symbolic Icon Theme Z Cinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/icon.png0000664000175000017500000000162112625666430026213 0ustar fabiofabioPNG  IHDR szzsBIT|d pHYsՇtEXtSoftwarewww.inkscape.org<tEXtTitleGnome Symbolic Icon Theme)IDATX͕Ohw?(C&[<v[Pj7ef\$C=Ty(kxH C)rЀFb)J/œ$ ) l$32;37?QUQU= ;~T*WbCfffe?$J`` "' זk:sj f8J[*N["z 1]znI*A03̏|~Z{yPoCqz>|?Y#w3j<{ y$0[XXۻ2 >ˑ/_nƘY`@UV&,<;CFqx8ΉjRTRd2"l6GZ{B4Bp 8'WA=$ȘzCŸEd%mڣ(w؈ zܱUNk#x@x4I_F߁gq<Y{I1FnɞV[g~~[bnI\~|-<6lf{ PDƍ뺵Z6#V1^]׭jIN1Lc=\>s]$R(J q/0L#%rg;Fb~Ց)1gH]?hIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/metadata.json0000664000175000017500000000032012625666430027223 0ustar fabiofabio{ "uuid": "inhibit@cinnamon.org", "name": "Inhibit Applet", "description": "This applet is used to disable notifications and power management (including the screensaver and the session from going idle)" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/inhibit@cinnamon.org/applet.js0000664000175000017500000001375512625666430026413 0ustar fabiofabioconst Applet = imports.ui.applet; const Gio = imports.gi.Gio; const Lang = imports.lang; const St = imports.gi.St; const Tooltips = imports.ui.tooltips; const PopupMenu = imports.ui.popupMenu; const GnomeSession = imports.misc.gnomeSession; const INHIBIT_IDLE_FLAG = 8; /* idle inhibit only */ function InhibitSwitch(applet) { this._init(applet); } InhibitSwitch.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(applet) { this._applet = applet; PopupMenu.PopupBaseMenuItem.prototype._init.call(this); this.label = new St.Label({ text: _("Power management") }); this._statusIcon = new St.Icon({ style_class: 'popup-menu-icon', icon_type: St.IconType.SYMBOLIC, icon_name: "dialog-warning-symbolic", reactive: true}); this._switch = new PopupMenu.Switch(true); this.addActor(this.label); this.addActor(this._statusIcon); this._statusBin = new St.Bin({ x_align: St.Align.END }); this.addActor(this._statusBin, { expand: true, span: -1, align: St.Align.END }); this._statusBin.child = this._switch.actor; this.actor.hide(); this.tooltip = new Tooltips.Tooltip(this._statusIcon, ""); this.sessionProxy = null; this.sessionCookie = null; this.sigAddedId = 0 this.sigRemovedId = 0 GnomeSession.SessionManager(Lang.bind(this, function(proxy, error) { if (error) return; this.sessionProxy = proxy; this.actor.show(); this.updateStatus(); this.propId = this.sessionProxy.connect("g-properties-changed", Lang.bind(this, this.updateStatus)); })); }, activate: function(event) { if (this._switch.actor.mapped) { this._switch.toggle(); } this.toggled(this._switch.state); PopupMenu.PopupBaseMenuItem.prototype.activate.call(this, event, true); }, updateStatus: function(o) { this.sessionProxy.IsInhibitedRemote(INHIBIT_IDLE_FLAG, Lang.bind(this, function(is_inhibited) { if (is_inhibited[0]) { this._applet.set_applet_icon_symbolic_name('inhibit-active'); this._applet.set_applet_tooltip(_("Power management: inhibited")); } else { this._applet.set_applet_icon_symbolic_name('inhibit'); this._applet.set_applet_tooltip(_("Power management: active")); } if (is_inhibited[0] && !this.sessionCookie) { this.tooltip.set_text(_("Power management is already inhibited by another program")); this._applet.set_applet_tooltip(_("Power management: inhibited by another program")); this._statusIcon.show(); } else { this.tooltip.set_text(""); this._statusIcon.hide(); } })); }, toggled: function(active) { if (!active && !this.sessionCookie) { this.sessionProxy.InhibitRemote("inhibit@cinnamon.org", 0, "prevent idle functions like screen blanking and dimming", INHIBIT_IDLE_FLAG, Lang.bind(this, function(cookie) { this.sessionCookie = cookie; this.updateStatus(); })); } else if (active && this.sessionCookie) { this.sessionProxy.UninhibitRemote(this.sessionCookie); this.sessionCookie = null; this.updateStatus(); } }, kill: function() { if (!this.sessionProxy) return; if (this.sessionCookie) { this.sessionProxy.UninhibitRemote(this.sessionCookie); this.sessionCookie = null; } this.sessionProxy.disconnect(this.propId); } }; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instanceId); this.metadata = metadata; this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.inhibitSwitch = new InhibitSwitch(this); this.menu.addMenuItem(this.inhibitSwitch); this.set_applet_icon_symbolic_name('inhibit'); this.set_applet_tooltip(_("Inhibit applet")) this.notif_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.notifications" }) this.notificationsSwitch = new PopupMenu.PopupSwitchMenuItem(_("Notifications"), this.notif_settings.get_boolean("display-notifications")); this.notif_settings.connect('changed::display-notifications', Lang.bind(this, function() { this.notificationsSwitch.setToggleState(this.notif_settings.get_boolean("display-notifications")); })); this.notificationsSwitch.connect('toggled', Lang.bind(this, function() { this.notif_settings.set_boolean("display-notifications", this.notificationsSwitch.state); })); this.menu.addMenuItem(this.notificationsSwitch); }, on_applet_clicked: function(event) { this.menu.toggle(); }, on_applet_removed_from_panel: function() { this.inhibitSwitch.kill(); } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/spacer@cinnamon.org/0000775000175000017500000000000012625666430024404 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/spacer@cinnamon.org/settings-schema.json0000664000175000017500000000035512625666430030400 0ustar fabiofabio{ "width": { "type": "scale", "default" : 10, "min" : 2, "max" : 400, "step" : 1, "description" : "Amount of space in pixels", "tooltip" : "Adjust this to increase or decrease the width of this spacer" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/spacer@cinnamon.org/metadata.json0000664000175000017500000000024212625666430027055 0ustar fabiofabio{ "uuid": "spacer@cinnamon.org", "name": "Spacer", "description": "Add some space between your other applets", "icon": "view-restore", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/spacer@cinnamon.org/applet.js0000664000175000017500000000247312625666430026235 0ustar fabiofabioconst Applet = imports.ui.applet; const Settings = imports.ui.settings; function MyApplet(metadata, orientation, panelHeight, instance_id) { this._init(metadata, orientation, panelHeight, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panelHeight, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panelHeight, instance_id); this.settings = new Settings.AppletSettings(this, "spacer@cinnamon.org", this.instance_id); this.settings.bindProperty(Settings.BindingDirection.IN, // Setting type "width", // The setting key "width", // The property to manage (this.width) this.width_changed, // Callback when value changes null); // Optional callback data this.width_changed(); }, width_changed: function() { this.actor.width = this.width; }, on_applet_removed_from_panel: function() { this.settings.finalize(); } }; function main(metadata, orientation, panelHeight, instance_id) { let myApplet = new MyApplet(metadata, orientation, panelHeight, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/a11y@cinnamon.org/0000775000175000017500000000000012625666430023702 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/a11y@cinnamon.org/metadata.json0000664000175000017500000000024712625666430026360 0ustar fabiofabio{ "uuid": "a11y@cinnamon.org", "name": "Accessibility", "description": "Accessibility applet", "icon": "preferences-desktop-accessibility", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js0000664000175000017500000002324512625666430025533 0ustar fabiofabioconst PopupMenu = imports.ui.popupMenu; const Gio = imports.gi.Gio; const Lang = imports.lang; const Applet = imports.ui.applet; const GConf = imports.gi.GConf; const Main = imports.ui.main; const Gdk = imports.gi.Gdk; const A11Y_SCHEMA = 'org.cinnamon.desktop.a11y.keyboard'; const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable'; const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable'; const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable'; const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable'; const APPLICATIONS_SCHEMA = 'org.cinnamon.desktop.a11y.applications'; const DPI_LOW_REASONABLE_VALUE = 50; const DPI_HIGH_REASONABLE_VALUE = 500; const DPI_FACTOR_LARGE = 1.25; const DPI_FACTOR_LARGER = 1.5; const DPI_FACTOR_LARGEST = 2.0; const KEY_META_DIR = '/apps/metacity/general'; const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell'; const DESKTOP_INTERFACE_SCHEMA = 'org.cinnamon.desktop.interface'; const KEY_GTK_THEME = 'gtk-theme'; const KEY_ICON_THEME = 'icon-theme'; const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor'; const HIGH_CONTRAST_THEME = 'HighContrast'; const Keymap = Gdk.Keymap.get_default(); function MyApplet(metadata, orientation, panel_height, applet_id) { this._init(metadata, orientation, panel_height, applet_id); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.metadata = metadata; Main.systrayManager.registerRole("a11y", metadata.uuid); this.set_applet_icon_symbolic_name("preferences-desktop-accessibility"); this.set_applet_tooltip(_("Accessibility")); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); let client = GConf.Client.get_default(); client.add_dir(KEY_META_DIR, GConf.ClientPreloadType.PRELOAD_ONELEVEL, null); let highContrast = this._buildHCItem(); this.menu.addMenuItem(highContrast); let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA, 'screen-magnifier-enabled'); this.menu.addMenuItem(magnifier); let textZoom = this._buildFontItem(); this.menu.addMenuItem(textZoom); // let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA, // 'screen-reader-enabled'); // this.menu.addMenuItem(screenReader); let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA, 'screen-keyboard-enabled'); this.menu.addMenuItem(screenKeyboard); let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL); this.menu.addMenuItem(visualBell); let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_SCHEMA, KEY_STICKY_KEYS_ENABLED); this.menu.addMenuItem(stickyKeys); let slowKeys = this._buildItem(_("Slow Keys"), A11Y_SCHEMA, KEY_SLOW_KEYS_ENABLED); this.menu.addMenuItem(slowKeys); let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_SCHEMA, KEY_BOUNCE_KEYS_ENABLED); this.menu.addMenuItem(bounceKeys); let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED); this.menu.addMenuItem(mouseKeys); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addSettingsAction(_("Universal Access Settings"), 'universal-access'); this.a11y_settings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); this._keyboardStateChangedId = Keymap.connect('state-changed', Lang.bind(this, this._handleStateChange)); this.set_applet_label(''); } catch (e) { global.logError(e); } }, _handleStateChange: function(actor, event) { if (this.a11y_settings.get_boolean(KEY_STICKY_KEYS_ENABLED)) { let state = Keymap.get_modifier_state(); let modifiers = []; if (state & Gdk.ModifierType.LOCK_MASK) modifiers.push('Lock'); if (state & Gdk.ModifierType.CONTROL_MASK) modifiers.push('Ctrl'); if (state & Gdk.ModifierType.MOD4_MASK) modifiers.push('Super'); if (state & Gdk.ModifierType.SUPER_MASK) modifiers.push('Super'); if (state & Gdk.ModifierType.META_MASK) modifiers.push('Meta'); if (state & Gdk.ModifierType.ALT_MASK) modifiers.push('Alt'); if (state & Gdk.ModifierType.MOD5_MASK) modifiers.push('Alt Gr'); if (state & Gdk.ModifierType.SHIFT_MASK) modifiers.push('Shift'); if (state & Gdk.ModifierType.MOD1_MASK) modifiers.push('Alt'); if (state & Gdk.ModifierType.MOD2_MASK) modifiers.push('Mod2'); if (state & Gdk.ModifierType.MOD3_MASK) modifiers.push('Mod3'); this.set_applet_label(modifiers.join('+')); } else { this.set_applet_label(''); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _buildItemExtended: function(string, initial_value, writable, on_set) { let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value); if (!writable) widget.actor.reactive = false; else widget.connect('toggled', function(item) { on_set(item.state); }); return widget; }, _buildItemGConf: function(string, client, key) { function on_get() { return client.get_bool(key); } let widget = this._buildItemExtended(string, client.get_bool(key), client.key_is_writable(key), function(enabled) { client.set_bool(key, enabled); }); return widget; }, _buildItem: function(string, schema, key) { let settings = new Gio.Settings({ schema_id: schema }); let widget = this._buildItemExtended(string, settings.get_boolean(key), settings.is_writable(key), function(enabled) { return settings.set_boolean(key, enabled); }); settings.connect('changed::'+key, function() { widget.setToggleState(settings.get_boolean(key)); }); return widget; }, _buildHCItem: function() { let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); let gtkTheme = settings.get_string(KEY_GTK_THEME); let iconTheme = settings.get_string(KEY_ICON_THEME); let hasHC = (gtkTheme == HIGH_CONTRAST_THEME); let highContrast = this._buildItemExtended( _("High Contrast"), hasHC, settings.is_writable(KEY_GTK_THEME) && settings.is_writable(KEY_ICON_THEME), function (enabled) { if (enabled) { settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME); settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME); } else if(!hasHC) { settings.set_string(KEY_GTK_THEME, gtkTheme); settings.set_string(KEY_ICON_THEME, iconTheme); } else { settings.reset(KEY_GTK_THEME); settings.reset(KEY_ICON_THEME); } }); settings.connect('changed::' + KEY_GTK_THEME, function() { let value = settings.get_string(KEY_GTK_THEME); if (value == HIGH_CONTRAST_THEME) { highContrast.setToggleState(true); } else { highContrast.setToggleState(false); gtkTheme = value; } }); settings.connect('changed::' + KEY_ICON_THEME, function() { let value = settings.get_string(KEY_ICON_THEME); if (value != HIGH_CONTRAST_THEME) iconTheme = value; }); return highContrast; }, _buildFontItem: function() { let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); let initial_setting = (factor > 1.0); let widget = this._buildItemExtended(_("Large Text"), initial_setting, settings.is_writable(KEY_TEXT_SCALING_FACTOR), function (enabled) { if (enabled) settings.set_double(KEY_TEXT_SCALING_FACTOR, DPI_FACTOR_LARGE); else settings.reset(KEY_TEXT_SCALING_FACTOR); }); settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() { let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); let active = (factor > 1.0); widget.setToggleState(active); }); return widget; }, on_applet_removed_from_panel: function() { Main.systrayManager.unregisterRole("a11y", this.metadata.uuid); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/0000775000175000017500000000000012625666430024727 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/0000775000175000017500000000000012625666430026023 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sz.png0000664000175000017500000001156412625666430027174 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[lyryR(JMdK-1UJb'mYi 1- 4/K$@/6PyhS8h6MvDHr"+du7IIx.g眯3;%)gfs|3Q|T>*={/YMjk Љ6K M٨.PzÙ-ZiPJ)VTTLP yB׎EUn`&iߦxP\PT|HBCSe3KO,%ѓEZTpt״A045ZkFtcਨJJ\ iaV!(YUzUADFkG# &kԄ׳V<+.V\u@qaFj+ 0c!IcnYS3"VD'Њ`-X`c6U﬑q7zV2 R1WUm礪fk.ݪxե' (kX P(259ˉ!x*cg~=VM8NW'JUjkp$yd*ՇE/fWcY-3ۖ~%0:|ݒx&t^XF.bb0©Sb GB+uԎuQ'i(K(5DD Q| B~Y94oXAk6ccc3;;GOa5#nb!}E+El۶}1>><Ge߾}7OM迢0A4xq2ece TgaG heeA.TvX[^Ft/c}Xk1=[[1Zc}̙(s#ccիWE~z~ nݺyٵktuu= fr9ZZZHӡ߼[aOMHG6~-΍*0Ư!>T*'=CDK7`kB9n<>\DxwP:!eZZZXf X4۷W9}ȑE J.v &Qojl:֨8ug֭}XRS 918 i7'hMRJYknYX4HH{oFGG9|0o6}}}]vIfnM̱c8VzuZpF3y a:/"W9~͘ P$+p"UfI$\TIg=\ j2_RlUåCVu""sNv]oj\x%ƾ})ն7ch5>`:Y,P)-'S8pS68XhϽ+ܹ3ϐN"LpWzeyK;Zש$"Wj@V1%X-N)<8.xepRڱc}4CCeZe jRcQ6絃.qw(f"XllQ-d+{+*WО8Gkxj]]*ӣj[zմ|& AC޲AhL/ʭRVƏmcxY1={>Dz>mY{\Xt V^LH"a`&/S]شNhؐVH{?.`b!|zj  34,K:eCp2sjK\nCt4uak8<;CR6;R-!mQgjuy[iIϢi$JadĝQ`T/O x#uC0iM\1?ޛN*yp/}P P„̏_89?=8@E9*H7߿[ZS{*,4O]o >6v8:zLg;6t|oAaPONc!NBz:v+:r@+_k D (0g PPt"P=|t@(DPY YD>& a"e2p%u%B|l( \J^q$Ei" G_X{q,%tEXtdate:create2012-09-26T20:26:34+01:00{%tEXtdate:modify2012-09-26T20:26:34+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/dm.png0000664000175000017500000001157212625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxY]y w10 aJBFQ*U[RR#CVCFDyACRQ+ҨU N$clc_kNgs!RCYW}g}׿ֆvt[!Fnu `JyfeN4[ʳi~-Q piZ;w9SZHCDDDD@H#ԓbE$/A1Y͝xW#'@DQ޳|3`3?}y;?ͮcEΓBT!x.;Z?q''?yc0"Sh_7 cg;vuR=eVZ˖[[FUq9kK0@yѼf`:Si1#(12bZ7YѾ#ڤB)F~_^B CfækyU "'/sΤ\J8\L#F X,SDh ؒ֘ljM"6*Gjd'}fUAD $#`P&&vq(=А>1>5,HN E"urWQK7hOEr_( Fʦ"ͨAU)jRL3"I8 7uNаVkzjUrg Ir OnX0WQʮNݵ!b L ]M)u71/owԗY>WLj54_utefEQpMz(AԘ&[j7ndyUUlJYL k>Mhʔ.7Jp-nl6Aa}skZ2òAG|H>`إK|Vx(Pve}0sb૏7i7)Z_/|Z@8 ԨPaLY8/[w¶>'_!:ywN6>6d6`9\V1Yv( >w.Jo.fa,w#x0}IUXiC/qw\v-=.~#T.lԋ̦SY$Ch2tVy?ϖMZ<|g᪐ö5IgtīpdfN;Xnk VswYhj  *3--9z-6 i`M#1 d\I|Tfǃ}z٣'F񚖋s\ L0DPUgb'Zi3}r?%(M@ʟZh΃kX V @g?]y]p6W8V~eYrQ\Əx8 :p L2LT{Bs|.T"%Z_ XY_6 pփkq^1Xj\(>Ku,X \<7Q%+hߧ]?odLjf6OzLbfm MK*gU4~@kF<*jds _vX:*\қl e~U 38׽;$H-縇wХ@`uH=%}q>+u.NU{]&#> ׭edz~:&넰"x|Wśl?ĸ,wU)WcNp芘NzNh4ʚ73: V1(N۬=% y8xX9s^o+Iͦ&@`|3慆+_4L_\ S]Oo"s7Jc?+Բ-˓1^ЙNz_LB*+̿~'q1ےEI7+1u>|ar}|+#4;z}nx]Y>~TlHȵư9kuz/^jB)%l(F`t& 8Y40J 2)s}piEc!  8e> 48ͥ6'snڧJ6dbŶ{ɧb%>i+\ݴKǸnRcFzihu/cwT80#j ^_c=zj=Բm)59k-#>xڧbfyrF5bpqްe/L e'&Rj%pb㻅O}ʃx!jYvbk>3[h}衟KF1NRgqqY3P$zs$ƐAi` D=x{?NXQ?q+65ӮxU 1/_]&8.b;ޔ)X6 |*#^8=Yqm7tGtmc&pvfzAb&VÇ9u9F5#O-?{dQ 7)`q= v7%M{pEp5t>WUi6ݻus_rgW^{I-ӝKq Χ euBKY`:o.H^u^6̼%Q)TםB)ݔrDF0/]h]%D oM I)^Ҥ/IJwުڗ\J{٨t^z|k'%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/fj.png0000664000175000017500000001374612625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>$IDATxyřU}5 0H4Kc ,Xz16ς Z BZ[}_]UYYUg{23"2*_|Gy<R޿ cɤN#g?琂qڏ 45 ]7{ݍ3O[V sԦMG8l` 6|.V=t~k×ռ U%(DlĎh-f wN3Nav+*pc1P o8O&FyyPICKZs˸{{-jB!"KP}uy&F8RBKklXHVњd2woxrNId\ }/Y5uo>JЄLYt*[ʹ$,RA(QE 7IKfmD6"7-}"BYY-M0-*nʫ"0`PA2y&}}\N?ڹq7H).KjY ْUC){4w=E_]H)?q;ٸ(v`F2b^0Z6}xFvZ.cժjr)TWSTTJ(0MU\HqƗdwEbD"X  jOjX\DLQ:^6.Fy9ŧ~ XbIIe'F!G$ ]@$I b @ÌLPIyypef/q5PJWsdr8i"3 Xu0$YXHi8L41 #mAHH~}| H3\5Y)9Adǧs^:j @&fJA J ') T^x*TiPQ `D{hD)$`󯓡}e5(>SK0md<ѷ† =ԏ^MhG{@հ'Z?DQ:nz1>\aȳ!gRҚ?Rҗ# )Og(Qzxde {MAO b>8K#  bh) DtYRU^N("lY#u`vʥĶq2$ά(,LIP c`boTcAU b@D.P$u`&ԅ+U^~)++%ca}b᚟6gMڟB)j֯@=8HoKtvRl EcG 밴CIeeޫ2};=N~&^[ͣ6v"ьq_ڏ)}#L/Z(j@\Hv{#)R(_. 2@a088L(0<q9"DڳgX|?x?ؚJJV^Mϋ?N& |솂B\Bk!-!_wPzv .Ay''`u]aܸ70u0a7u{{ڲMp)_ukn)<|WGcrܰe-^~7Ƴf$jk#y&st<0-&Gfބ>q UBVPd` v% nۂ/{gU|ZA ɘ:I@ÀΪ;8j,Kvď~Dm'{ç\z_'I :;Gy.nmSM)Y yyB;kְ{g0 ]7kT `"89,VA`^.- tm7rt/\vM $@@"8լ<ۂ^$sij }Azp~X|VLAQqoS8.D'ƫ^IV"{xF8wsSZ z.mFzzp"AX _)o Wz"{wgLvjGx饵[9($ 6ѵy3bD W yՕ4 ^ IpJ)?b۶Nnu*sih`~@ʕ_y8DZ:ZТVPR\j{H~_RT\i\p B<>hMCg!R O|bfJiIVtNjʱ#eEh\gX鈷I`:ҋq4^aZ̜0x"N" $(xCá Iۃi㰻~7<Ʋ˙?q%2Z݅b{.J)ZU8<{I鹁V:D*╁(Ė,:b!VM&~l``yQ-d9Xaę|0CD4M$1O;hetXj"lYLNoGW2SKYRE)N"R\ C313ϨJk8[#_pUÈw'I]P_jWԑL 21 T!d бM̛9g>3E%O_?s?ͧޞ5Jj ߚi^ T[BSP$|)O|T_'vIBW3VxflCعWC hqH1{Q(l Q]8&2*`g^GU#ӋՒQĘC>]6Wi|'(#==Q֭ weheؽ2\` @ MW!ldOu`vy">i.Zsq7Gh&/@811Z"Pu`,?`#\)Ī{*Ɵ=O`ZКdA Sh,a ;.CSȌ*W9*:<b%tNGC; Û IvNvzRgcGtE5C!M$+.Gyaѹnt5$$!3Z,^~ǡ)[O7P4-LJbV4>#CN) a.3<`G18,͡Xz+~NxY]дi{."x}mh9% p{LP1ndrJbtSs:m#XN>1<u܉ Th\ץ=b=>xR>˧/(RHjv*!ۀd$Z9װIipN%dttt "ttt$h\1C}Q㼀<(v@O I. 7DZY6})_n#5 v` (]ڸN[6Ly}#jۓ1cp8.j`baIK.ršb K$ a:6 I& Dbw v~Co?`  n6 AI9&5v⬢DeDJZ Bʖ>~ h:|x)K̥ux@;$03|#H>GxRWYbA ZᏭG$I1a[TKT/tGX`< yΟ|f=P&%tEXtdate:create2012-09-26T20:26:40+01:00-%tEXtdate:modify2012-09-26T20:26:40+01:00p.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/brai.png0000664000175000017500000000367512625666430027461 0ustar fabiofabioPNG  IHDRP<[.bKGD̿ pHYsHHFk>IDAThoE?ݞ EEDs,bj !#GNV冄8EAG"*hE-XNCDZWw^uU'(hfߏo^*~ݦoz3( E@05%P B5kS(RqkY_A'}`@b )m7ytuTPZ9H[)iJO;_1ȅE#Js3Ui?wm-4""޽?~b͆W8W?_+bAΟ1,V?BR:( bюjcR0J o3zY\x>A&+i4 AWB*bvǫtZ7 1a't7W o_$[dbThjQU0 إۭybLDd:xOʗٜ]3yzgxpH I1y ܮ|ar9lO?f`"i]QԻ525.Ex%7Iڱ̷FyA9kdWxی.87T?+Vr6x 9=%AN8˕Q23A9^, kQb#˼h7^TM2Ufist Q osjUuڣWAb9!z1KFRc{]Ķ VMJJds̎\NiA׾& HAtWдw&R}Y1GuE^],x5˾n4'X3դ9BT_ Uɷa NsBH`bȇX(.2{O|CG:C=/V}"?xvf nhlh*,n8 HcAO|B؀u;REJ;2 0n2oۙa V9'$6qJgy|aZN mNK.ccȓN@GxZpe1c*s?L 6e-p!bJ$ a:M 5JiЂS.(L;A,(T6+s=8bWi 652bZa"dVur~G#i}GF5_@wH9BAGI/)Se8ոA. 3ŕe}m^8MVX$WK[2DV򃓓i 6(_p1[B( 6-Xe\IOgWt~XbAۗҳ=q86s6zCn'M :٧I}5(>ӞDa$k?tW"?ʖ݀nERd,0h}0zLtAyhGxf>Z&-:0ċ9np[UL?[ٵ5Tw;Z_% \-D3s(I[|1<*Cf.mYb Sc!NFasEzlf5:I$ 1U&-e@]z L8,dge[g,ڛQ&Oٿ[>P޹%tEXtdate:create2012-09-27T13:19:53+01:00Ei%tEXtdate:modify2012-09-27T13:19:53+01:00ѲIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mt.png0000664000175000017500000000532412625666430027155 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxϏGǿjgfx^{W!ʇH(+`GN97`@pA\R8!r5kcΏzkgv=Ԛ^ZർZ^PWAL&*F&J{B3NIӽ@C Pj\r;NzZl"ʅ(Ȓ_V x㏡>%i}ސSO/ufNx5*] 5Y _]t˗?Z__paaZ&SH`g,-A)% @4v}8p223Rf3IS#}hyyŋ񓕕Z\\DZE$D`§!kqed2I8r3GP}0` " O2cjٵkx'Q7>j%Uܹ7NPPCJ^ ҵX`3F{nې k Gnf6/;bdƃj"@R|IPG}/ i xZ{ Qy!X{O'#<߿sEB. 2>!4$Է%7)h)EH(nc]@`Bh,-ߝ3s&7aBHB"20Bj5 ØBf Q%d0 ./XUR{'v!1OQVX]]V"1&8ap~+ʁ76O L 5xuNn}yϋ&yrٹo |Iw@Qץ' mE jhU2ǣuɮc#m96cFwh5 s#GN1#p:ʹ tX{ATw"!m伲^=XSh.na:(=Z @`aUkrixFYZUƔ=i/ T*qTur\' B~h;; cx%`W@"-j 󷻒^2g, q.{y>vl.< c`D 53J y&] F"yo}3y›ൗhV n>h!HXew XLW#/ @,LK+c֡z22;Y3``ಋS1nP:q2J6Ӳ|ɟ\hVI]6vC,3"{djR RJo^f!$)-$] s$T  DTK!n|XHfk|?@ ,@nNý^(H)ӏ_<@8Fl9 L5 1ֆ<s>k6DžC3;t|-ut<"j}7o޽v:ufբg `.:p<!_l8Tzܺzz^… .//WՙIDATxy}?Us=EH%d@XYȀ燱9N$q{qHl?'91" Be#$!@ !!6 iՁv%5U{fggv'/z~]_WGQ-wtCѓHL&驓My[_b@̋K#SgqGdbV d 00 84]]w]C "4{6_:zm/DJh,ą|t}HO}U+1LɾyNE~TrAӮAsfL _XE,_*: jijMٱXlÐH)PJQ_? ҷs;2@!r%wA(W.Et6FΜ&xT~r W_Mec{;/ԏmbiO}N5(RIk-[:H@@ VnM]Lgih{صuXbKmodӓx &kǜRC aYFjjgʔ:**j0LIsK ˗!M<{a^v_<ѣgQrN 7$d.+i#%q ij+sXa_ ̺TMg$Mؽ믹O~y9'2aշ7e}cllvф" l>$p[IU,_2Wu4їzayOmc.@@.Ψpڔp40JKQ9fc5[׶) Kg*)C佣gё^Y˺U7pWb3>{n岵kٱ#G @Q0ͬ JWgajGDKT]KYƿ?}6С(v6V!mX&RMV6@"BQ^5/W"7xaUVzR D#-4ZZ6֤Μ%DŽ\cB煀[:& -6Bȩ$YApi&%2_E3*]\,meKƨQa%s.R6h gmݡ3aBȴWT[Gyi)`)sVy2i=y/Csp=yfZ) ):C@M@h߾0x!fe\Pi.NCul>6(N1$JNˮC,X@S10>J!KK.\Fs4& %3maǮΜ9LN8goЄH4n-IX+WW HO4!iIBLTَBkIIIefsKi/ u77owbqBv.!Yx,/gtVb[XE݅g{[l;L|`t̄uO|ѵ,Ce`F8sPJ@(dr啳җ)- O?GcTx##׮)g+SY~<Z#ïq,ZY߼#Cl:@ :9 pŊb6=C>x%OWu[l%K㳟%KgpTr ?N\]ZGb}(J pSLtII;/\\yo Λ@bht׈oRJw@[<6^~ iHYDgBҤz6f 0Nm Л1$,$O +/va|ly~,\4ߤoNh^2żΰ)Ce`w#I*`M/ӓV)\f|K P Lϕ_xjnAVUsd?à S" g#rh4HSSx JD667BII|jRMtbN '@/=LLRυ>~ұ=qkˋ|q/h!j2dE8:Gnٮ+H+Ór"F N+kۦv< g2_Ab -E`"F0*"Ž35F4H%"Z4z0 PЛw Jf@ D DvOϭ-/9RO[lZy)m|sFyNމO#l5yqʁki.@9@0+Pϑ@㟥f2Xf0s׃f睔h'eŒ)m|~Vɸ7Er׬5 Ya.(3җ*s# 8o+>nq *d̀F:yƓT[z=}sPUR;QEZIUu$v6e-2Q渭ׂ)y!ehmS]|yOmyE1L777ienqsO9oӳxϕcQ(=ۚSj8td9v_{C >;ocl5آoԈT"dDQcpO{1v +;ƽaQe;K XC ! 1QMZA[B+=q&EaO\/(R`<ےϾ`>~-O}x'>:֠By?UA4Ӊj[Sv+5fƁ=Bh{ck&'WXgމO ռyJLac?*|H{1YܼiӼ? m f aT|}|{+PHPZVї Yۧx|&+RsW{֒:V3bBбO5H(ѹ@O"˰DkM@ew}k-{ G<}˘qyǜ `v*@^w5v'9j,oSJ xT(sqvn\T l , |@ y!И"0dO5MMh3'FUb\ISCe|Z <BzZǣj[-ZPR"-j ɣuV]0sIJjIp"ИF0 ${WRCSnZJӳ0Iv?:niJeo52Y KO0rTRIYX Jtu"בJth &帋Q v? 2*C95xDŻ-+Q A:M0sQt #1uke|^Q5Ým`x݄w}9T U_ۮSK 3`HbƑK L%=8rtWq|p{5@YRvaxUX-s.%0?UN'4AeX@ EM0N'fZFؖ6c#C'z~8 #dIDATx{}?gc,^ ה ~ 55;!GLM,%-URGZ[iդjEuFiU"FlĎ"].e{ܙsǙ{f}Hd3~gfRT.KoǡV*& 2Y-\Q.>4iHq ֢tj2%Ri!B^Woy;[wRLwGZ~kWy}PyRJ9 *gv\81/ ^h%zoOɴ, 3{ ?F )<;vzYoCdVY J 9,t:ʪ=T/:`{ Z@:)fĪߗΎ^1kVT0@  ^Ji)zpnQ`U^mJ NHB@!T8d; C}Q*]4諯#XR[$8N O`Y TcqA>Ǜtu֫UU &LRZ(ĎJ$~SةDU!RضZS\X&l.y{mFb41Y R (N!͸ ` șXH4_E&hxUS^xn Ue)ŷ9zE@3ɛU ¨.@),^, NudKZh0ʋ;nu->_me؄cՙx_^VO@H9$Dz a/ٹU彯;07Bu%Cq4B4a^VPqDMSaP<LTL؄gU <#4Lӆ"EįFoypkvs$]^.T8MJ# 62~E Qd\nTNZ9)>~h*ŶHS~0E}5`,]2F:] 8Q+==@k `+Q*>XV޼ݑr?旯<.硽 ^N5oZVqPzu6H=@!ҵ6ݨ-\eaOuZ[3-k`To5Xf)ayWPo*1,Sj Hu]Q?HkECflKBsɄ˽FwT.Ƶீ*ā`};*̸!QJp@i :DyCsD- Rhs y%  gٹs=Dq+- c+<f׶0q Ɔ)%$wVD>P0 s-ne"Tgt8&s]뇸o4 U忁?@ VPzmlIA4``n>yjrT ?aqd *l/ʄ_w|_[?}'<0EdS]Q}! pMJk2{kXK2Zx~HC4H;].ZmFK%5㏽̂OL>.UZ{}V9pf])'( weg'*`\8Zv!J $̛ ccy`sML׵=$SU'sJ/GYR\BRjP p㍰x1^{_BWW'facMG.!@}HP/ Mжn `"md R&.Qw7ދcUy􍞣J{ٸ7R_#cB+4nKS+1C9!KxttSq Age6/ܩ+!{{kaf*o8Z5A*I}t7WR_E~ɾDp&h%2z>Y`2ؼ>Xfl y4l\K?.w|aW# ۏa!h}+Vٖ-Zug_h-AsOJ'O/2cfٽ$%ٞκ\3@5̜Y'^ŮzQޥBqh@˗TjV үg۱z UX-rrQ0b0;ȑaFq[k3 ls=4=^+zcle 2qS8s>Z*uzÏzX\RvJLV&+$0b(7đF9]9M)(7Xl> kRCɈ,*UXz|eA|Xżp7lVYe!R kZRI*nctcS8;̡G)g<3#kVTTF/=xU6JTpd!'cBl>J;=45JVD̛H$RTvm )NO3Z%35aÌF/Oj0klt X"'NroЁƣTǎ$7׭l[}g:"Աʕ~b ؔN3iOr8HaL>Cf*(qry! D!y,^?DlE0,J&/Ows:Լyuerv|5O-HG"0׉ve'BRqr4Q:Q +&( ˃m}@u>2TZW*@"M^6g5MN ysS:'{kNKI?Q/FC-&e뺈W/f(ou3!5+"fq-*f\iI%tEXtdate:create2012-09-26T20:26:34+01:00{%tEXtdate:modify2012-09-26T20:26:34+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/au.png0000664000175000017500000001270012625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyՙ^ݯnnEWAĈ 3*!B\΀35pT52FE,Mk~{՝?꽪nsb{NuWWu}[u:}N@n&ő ๯r< T@ '|sk/ (̒%oh4)!g҉^d,@'c[-%np&0hKB_x!^.-3b`'lK4ӿKDJ돔z0H&nPHD-< (1c3>7n+EAUMMc ¨뒆Ƒ0/0" !0$g;KN >'j )2fΤdRg>JJ|yaLƍxC'zo2@ zT|M<|XdOU7@ݸq̝7IqkٲEJ!QQP[SMMI-թt` &?-Hs]pzxy㍽TTT(6xѝ=ż\ιOUWQ7oӧsY=KbժMbI$ ~͇/RG ^[&d#[ _"p ͛G`x֯=ΦMB) NXf ~F""Q}U=drG^ _!{WklDmh&3us2x`n|ƌgѢ7ؽD@HFY0$p`3.?Gr jp8o`7im<۟ {u ӄ(BCF(`Ɖ"B(xwؼ[~tM^G| FɤI̘ ƍkWz9f@Jɨ10TN9;x];gO'/I<'*,-M!$R KU]wº_o/uiP >kKfLxU$E:]B(B(ڏZ u&&wޢB3!5yfGUR-{ =,2!(2oNc4+WRr4v7~ k@J.i!"%; \i_3L4RGʸ4uѾ [Rh8e&оQIQYYC P RBAz4Mqji)55؞Baȣtr& QKKDd'%ꔢ*_U+7 Ke'$"tK6OS>\?eS RPXVC7Ń(b@,V{{/F$KA( G6g|{b)hU+˄焐 -T|F*ҩSl/*]4UP^;rl6v;45QWkST#O~9e(@a('t#B@8硇  T͜`PBl%M[SǦT;e{t]܍KsAV4yÐ(`ڴYbӧ@ 9snf{CBFiM y]\{c,_p$At)3g̳,ϝk3g %~I#G4^BᔨA>{CG P **̛ww)@gVN[_M{A_3`:Aikx[nyݻ;jjӟҸp!Cz?*{Qci$gG!Y8 Ԕ?;L,3kQ8 utu(Jػ* 0)"Yjg׶b(*Ӧ1$pD:~^}%sOsbFY}xEs7UUA̞=RSW>)iD&O)Ct>0]wߍSsY:xw@TWd%f 0-\̚5LESG{/˗Ӿx1E;>cF(Ew>7pLiL$ &OɌ1o|c(˖ʕm_~TT_pv,ln`^d׎6eIzy*!'̢Ek31fRj˗++wH@+PÐԔRL5vK["~ObР '4O|U9X)/)nb=v [>YS.P]o@(\!]+t7zpU9j& a T!C*Ǔii 8 {d2~޽][hi+hPkʱWhJH'8|8B4H$ooG(.G$#HU:33MHsc[z>/%y۾T.g6+Ԡ" _㗿|k!CQU0$m%RiҤw;Ol9Ol ^)s .MA.ܼ6;o< ?+VcFlٲ{n~zֻ--]̜9 pug N8ُa9)!? !D‡F!M߶m?|9i.ZZ:H&c@o$w]^}0GED8|8=mZs<H DzڤL7FN$+ R {~abxV~oo/?u$ 6oއh;ZX%;gk\E铚7ty>sQ=R~\ @a2Yqs柿|ēOƐ!UQQRUaMZ~1dlIC@P 4:bA2P=;JDeQz`F_s$L}@3n9!zT1V*j(ul[]A .@bnȂZlq b18Dz eDrP>Ɣ:?q8_d`[ZYI:%ӏq:@`QH uH' %tEXtdate:create2012-09-26T20:26:33+01:00%tEXtdate:modify2012-09-26T20:26:33+01:00]==IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bl.png0000664000175000017500000000527312625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:17+01:00j%tEXtdate:modify2012-09-26T20:26:17+01:00y7SIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bh.png0000664000175000017500000000645312625666430027132 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> iIDATxowf~qpy B"DQ"}EHQ@yRU*o~TU[IB 8v⼝8~qfvwvc4Wy̹s~{=޹p.[<]{+"rNFpr)C:kƁ Cm/Z\>/#HB*( 99n QURIS(BOT>+(%KwMwftuO"V< Y|3 ::a )##?܎?P*WF<RKTݡB=]GN%[vx5u`]dӦ߮W,Y":::hjj" `0|+q]kWư^mىBTgJ'D@TE%U~R⸎61,_h^;Wl}O?iMb… rb؅spea 0j&d)Q+BD @c:y[T$J)$pe,Oכq8-,3tw'VnܓgP33>LS$GpQ!yz!D!kW <i,.䤔fsd+AE4?͏?9շROΑpݪń:h:PfY)lr`ގb9{ٽazˊ1\xVp2B|*%6<e=\-/  Li}^D&IK4 9:ݿ7Αq6M?X_4SYK-v4wK%(g;ci_ AJ=Rs+s9ht.i{v#αSXp!oLhAOJ7R@B^}7`vk$A_~i]{p ]nJY^olkȭ#k) UBs !W 6.K=7Q338'Nc[ߑ+>%Fs8I*5dD~.7pFN`9$XK e@s9sayc?NaVgQhj2 =x@Lj充l`Isa;X/i#]{axc8HBAL~xy!A*2wAD Mki} Xzpanݭ*ru#ʩj1ԡ *|7w5{WF- AKM?yK4:B ov~yc֎X;?ӹUdh'`ĵc$VOity.4\bqZ Re*d. `/oPؼշ4BF} E$yIu_y73 hS+S؇w"d4 Zd֨Ybb~ib\NnX6ަ6eGB}6g0CEJ+Wqc=q xX7Y} Vw٨.Kػcm؇w|}aqGgf3XaH%a9+ȩiJ{(N0oT{48]I1 J:3K@ѵsRNqfQ 6(OEaUҟp*E[+-?\GD_>xkۧX;#?/LS<#DZ܈=< Ucq-.-sCXv D@V,t7Iu#$z~Fav,kދ18OE4m)I.TUWٳ1d>7x/T]8$6x$V^9#+A Ͳp';ԫz$B(VRWe<$O7q>K4fpŅ&b$(b:$y:puQ7gƢN m?>B_^Ki}W,yNuu%KyI[d>LчxW8w$IJ`%JQ:h6ffdXIUF닛&D|dL($/-uS0rRO?ֶ샇cy~1VJ<QU I%y뺘R*8Ggha2S_jVgjB///O <qlFR_cL؂{JzrArq'*me](OM8(\n+?3= U* LDTjDʂvSCo9:SyIeUX@ (LdkC,9|g/]4 Ʃ[VM3ȝ95{pꘟX7XUh\^ @P̠"8x$"]gۿt__k[+Vlå{1l| ֯C ޼{ss/Q\10UI=ĞC@(I&eRk>xⶦƖ??| Pҁ\#| \nC9]0JCo(U\;W{aJ)f xGM6#/VcH1^X<p @Za:&@\]@u$fSAq*0+ٰG(|ӽ@ͣt 2@( &C]alƷM94۝nRΣ|hu)aL(o> U\^腎: CJ+"ZTe>BrCV%tEXtdate:create2012-09-26T20:26:18+01:00"%tEXtdate:modify2012-09-26T20:26:18+01:00nIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/no.png0000664000175000017500000000663112625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxO%Eǿ~켙.F!F$@bbG.&&==r01&z0'1 ]ٙݙfW]UfKz_uwU>U%ܒ_t &q'M4x,vҴIA=ﮮ޽Z@D {5sptH uyZ3)<{E!og+寔[N#lq1vΙoE4$oi{?P>a@3ZZJ>EQ`,ch9Cag/ʋأ=84c;- NQCrqMcfF,m2vί_v?&d'?.˝j (i, Z1VV0J3VVֱ\[X^ZEɱ@\ґ]lJ)p]0?%$xI@k}o=MF $;i"O-]pI8|*̻_w_пvCq7>śB^#VAvYNVs*_Y̔Gۆ̣cU*Is*RTJ>y| 2*bc]<7ӊhGwPOU SYw B)vo"]ʧ_V!I5@$ " DZag-Չ@)$U66j~R0 [߾`B!.kE)o 0^ C7`*:{.;5d9u~PgFaʽK:JkL?"zz+>gFf"u.4@bоN?do @B]%'"9; k@g5'u+;k֔ASqvnkZ_J&GyHߓ_e? tCpjH"NukF8τSҐM8]kBZafkjPhGHshK"(Vw͌v+ DGFkx2`AV5c Id&&3&k+XA6] >,zNAv0[y_7:5z<ރzrvC|UJcLmmAnnp}}[-L-4SL*h)~@= )S SzYF(Egl:33W [Rb=?@#ȠXeKdQt6v93I!o0@[!3,T-Hg {/8 l Q%xy`UWT} f{aʡzB.rl=Td}S:׽? ܂5@4Ż`9)ng}4inVRUI*] y&1BMUe)z I肫:?:rk9ƨ}s"y\Yϲ5R v}a]VV<;4u&epuhP-eX KJNNvzv5f{aLMm@f=@R#Y -mNYӮ:khlLMlcfnL@*]=8) s;c%73{V &KJRHR;藤2: 3(ޯk&i21UikNff$DJkH9 lX3J D0HNqjl#`NhNLh 0c@9f)[1Hkq Xh%h%cZqXkHRʉ5&WyŌ 3bfyB G+mXi!kd>5Fch9ԸE#8?JZFIulF9,FiJ ߓ *D*{{BA.pR/0>`` OWz%ΆApLJFmJOB #I]C,U& qnM@OpEk &{{o)>p U2v!9QZ1'1\`adžt0R5P#x>w84 )'RB|t:H?B 17 D#$Rh1򏭧&uD?>tYϊ9*WtM`PU-_!f%%tEXtdate:create2012-09-26T20:26:24+01:00 %tEXtdate:modify2012-09-26T20:26:24+01:00P-IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/uy.png0000664000175000017500000000756012625666430027176 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxKy絳C.r%i=h[Îvbqp(@ 19NC1 -eFeE bJIP)HKr>gwgry~`kOD>\(vw9dk\~%fsi6Mt4}ݕ{쮩[}/ej9zv- \SNO?q:fA #11!z)iҞ1}}ظP!nFҴF(ҵZmy裏ʋ/q/fW:p7 Vy^ /4E^E^8UmZaC(qC* c#J+F݃s8ZaX;??1,OLLYTve @kjXt))4F\BLj97_ÓY\}Q:i ۜ>D҇U+eSiwFlKκד߉8d}u]o%1h3>P㸮Vjνc$&"Xl| <'(XǹW*5: ϼt7޾ЧZzL9vYݲ%0y +ݦ׭ae@XM{*78w"ұ,f'|I!$֜D)O R]iѪq<-xJe]yet)<1:ku8iZpb)xZoZܾH|Σ F~㞡ޟ.ŊByp0" D1ŪY ԯS_jz  n/>7铧au @9$_znYtP>ƗL5#l݊yRVy~ Oagw`9l8iF F,hPby]{LFl,#N q5Nߗ^t4JحHŨx,λ)?%4H a%$\ÝUHI \ nb+夥"#"i th=fY3kqe՘˚N9Q7lt7ҏ8 q),$J.5Va T Pe~AZA8(Lo#wc.'>k,ax5_{#Gka !n)S&Wl_c"6|ڿ 卣TJq~9Knl 5-sob[o / Y,jpXi3ص3~Nzk&p܈-D7c]ǬCD'!؋ֽq@I,VjUTzT0 v!r+&UsQ>-(n´cKMŷ0_'ŧ)O~^JwUZ Z JɆ39 feGywH#jΟGyA̚.jI]4 } r[a֦V0.Bҭjz3ܔqdyoxm5_3"qNiz?v,5Fh/Ө:.h`'12j=:HoOnN} weztRrp(roXߜe8a:6\\6<* LH QQh,*jW*μD&<-q)\6ZD@I2+Q?2@ÜtZ8Is 8A,GQZwڅCTG\ МbN0 5:&鷺OO)F@n0TA88P ?s3}.ʺ& ,KGYkO&q{z5)P^ysϣw \u8lqDc|Ugl6Y[r!Kw6^0NkE=AN@Fvio+"Pj܏W= GʁGD:|X242)b ŽyxnY0PJ㮴6lC`W^)ރ;8::؁v ,Z7Lo6q&ϗ8҆( ipa,Ozg}̶0E'{Qutyx!X6ӴeZ%% ,-U8}:мP ]g6$S牃sy͘䓄r_c %(VLQ>s>n.aD$π("EoWjOe7RiIHF=3wouyuuqD{|E<\@tub:Uwz'6lqya.ΦùgqnҕT"(-Di`ePx5vE[T^waNR2K!X-X _7҉zl*\b$NH%Y^-w]TQpQ8-i8e<@_r&I !U㻟X 4.WBCDu6ZuLөK__Om{_9ߪKݾh5+_~=H ږ̀W.W]ޠ.im7=?ázr-cLOOh4n㶒$ YZ*qrCNHc1n:]moJ}q('r٫SE6ks }#gMJrc!C :bh֔7u?И4[Px1΁Xp40 rmQy :ċx\M@Kei*@EH"@[\;@Hq΂|ˆ#&ՄTM]z&m4muy+PڛűW7&,XJ ˟LFz:u Te K+$ЬgR / :ٺ&A(xpAM6ʙ΂8p"2v֛羉zsJin'ɺy6a ͸< @ o*h4*e%~ľ6jǴZҥ  i#~_R򕩩)Vm颺15fff/;vCp8` sKO=[V{xm /1"777vȑמ{K@/J$0y ӷONNNkK93?Ƙ3gμc8\HAf[q@%+uol7jRH+ IDATxۏV?sl&͵!i ))mUBUrQ% Jg@Cyav5( )΃SA$:6jvZmTumlLtX#JȚLXU{E=S5iw>aMYwS&o5&lt`9C8m%&) ~ܾpqE4*g|O]9Qst_y~%'giVFqٹ):Q)uPa{Ow>Nt|,懏TF[RP37~Z{4/rAObqn9 ,tBa::Q:|U{ +sJ!t*XJr*xw>l]iR3iRTu.8L;q9=rx 0!] h ZAk|F_?OD$IHQɐqZc&H n>eGLgG+pw@:y_ڴXG߈ԗUrTGښ7*R{KWVC8{'x^y>lM0f s>xXGw6uT-#G>lt:"qt!:jxev+F1 f@Lm S#1UD6F&r 6NW 7p 4zR$㚍%,s%iM{Dn[Va}H+a1XNPAiBs|騤J8;Gw,/:^sz~RʦX6)2R8WgO&pS3Dw O꠬_1> +A\q[|O! pk3~`+^Q 2zSsC{yNN )XcUFT+TDA~NSl{!{.\<ӫglOD8}."tRd}/LB(+zBa~>[mLgwL7J{5I*1}БVpiUu'3Cě76 8䵮K(~\o{= Dɬ, QrdԬ׬f rJ=(q5Mztv\w^P!l6hBAu!W_ J]N^RaH4աRKlw#!M7 ]q~{)ZIleZj&S}vqw {#2qbʚGbjꖌ 4+^M3x|&?buNK4cSjҨ>›ҝ҇K,a, /s~@\$LN1|mIsҽ1?ONZ^j-7ӬlN)7iU8 xR]f}I`%UoyX/Ρzc:="^3={\XYM|`q/.]c:tTzC2 ]_[ߺ66A3WΜ۲vB0x<R9\K=ȫh 4 _8.g 7EHV1USOOa jW3$ wnk@ѾϬ| aSAwN63fΦģWΞ^+ιSild b@ܓm;mTl g0eC%u9%U݋ ;m"*uY0X3H^)`lls')H [kʔl=Q$aa]ͪ}9VJ[kSk0eYI[j:OE/PI%tEXtdate:create2012-09-26T20:26:17+01:00j%tEXtdate:modify2012-09-26T20:26:17+01:00y7SIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/co.png0000664000175000017500000000476212625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 0IDATxͫ%GouGΌ׉bFw\I(sʅ[NAp!M!1FG̝{SS޾hSPtWW=UO4< O򞆠c25fF04Y$4GPiͯ>ue|bV&"P׹-ԹI_QWBOjܸiᏫ PP_{_)wټ67Jc9$7R* &S¨Xk9WѣݛO~Qefl>'~{򭋻rYfX RcLhh  6 pgv|,\{U\檏F}WRtqW^9wVْgv*bXu5c8&ZfdseO^}KZ]9G mm 9c R$)c#%2QQ$C )l˥Wn"plWK€PD2Dޜb$R3LK/ %XY` xҎ1zݨ*6r$ w/Et\F=MzLtI̩*6MMPYn)+A%7ZXUA)kϝz᜸%+eQ[kxt,9#s]nȗْ*ٷ!@ ݨWۑQ$'J,y;/=MOoȴu1R:X fJIhϵk A jgUqtน q*5 0cRK t 鉉뛘gQ\Qqk)O`TXK4'@9#SSta@)YK8m }c'KhkjdT ] 5Uo1llRoox;7]8، }/[G /Ax$VGCӖM+lF鎅7&.F,ee( dM퐤vLvFOֻ҂R,m) Ř# "ܸ/e&;;% (I5$\҆ V4X՝,w?ݛw;7_ڟ?)cmĔ֑6qcv3EṁEբ.`,f}UZ+OۨVy?|Sj3xj, J[n=ⴃ0="*Z6Z/zIGSᥕוmc>-s-M)I 571tj]} (ݹhy:.ө%}Gմݯ; />nG pYU.Ψ>RJg ;BH+XR51Tv 9>^wjK~i*DSH k  }VgxZ2t.Bͧ~BK+Gh}̣OA^tQUPa}کxo*)Be_9=~.{!OpaC=DvbVe2v/>r;W Uy/,a!KEXb*iwdVD4xo~M|4r*X]%s1ઽC^6o(-DډC3QآNNTrsZs=eLk30ĝѵ^88(ձ2Ĺ!x< ,|⴨u>pIJ,\pi 6ȕ+! 'YZ| l7 ŁlĢ{m(#ˣAxB|9prXr=w޺#<:c*>\[erCL{=czJN)p ToiuOmUVB)8* ࿉(m#ǒc^ a 8^?<ϝ;EP`!xV85p@cØpXYݢ|(N/*] [O^.n>) `ۑx ¯uNRx]G>@{ߑAsSC #>VG%S,ZL+S¡ C[Ӫ&4_e$cJi N"2Nx#%tEXtdate:create2012-09-26T20:26:36+01:008&%tEXtdate:modify2012-09-26T20:26:36+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/af.png0000664000175000017500000001127612625666430027126 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[WTUߦsx;/ِ A6D! J+,h%Xj%^x BȊ`dxHK|t}語 fKUuTկ99u>Oұ$:&H]|h*P@L)eܲ}R]B'=Whko4i/h(<,OGS7.uy9.%0f x&1M4^JH$2hR6 [ h!H6 6yQO 4ioڢ©:WĨxSa%BBoFQJ,ffp  @- j  CeX#ԆCPhv,/G s]niW_0O !B4q۶7E*,DlBkZȠD`p-tnm?BB  a )WCƯw?r+1!^`@mض]gvBHnPT\( t.۬@j0Q'Yi{<7vhqW!RuP4L*@0yB4>D6Ҽqe7-ТQ RZ5>Y Fdm ^)xE\=fS -,X)4oHN@IMn &5"x廸Y.jP>D;-fpu@}B꭮NԄ)2~ 8o+9p-@v07)`Jll:јv\öFtT`f[k–LJmX 5t+G^R6/x[ AX;*rZ0؁12* ׇxt7fǩ+/6:Po#6W^ukMH51R"3j Ӹ51 t*BXe*bqs2|]x~Fw `Z7`D8Qу(1i밼FILb#\E=Kynu N!7?P;6#w]ZB,/c#ΞEKD(۶'&R.~:q]x:#~ lo6ZS >: n!FG/b>6.%aX1ΞE_29ы{Rō[B:\arf7 񧟦g.'N`sr68xۮ]Ӷ33g$4k'~@SmCRe> <﫨y;x\pӔRģQ>EauءCDwJ9pѣP.}aobNLeSaP ӲI픳k|OA~"@ v?n29͢uLTO$O﷿g?SO 028t=0PK'DN7(B]H2Ilhwvopcp54$FG1LRh)aqɝOVDO"ϭܹRd7к憭R֚T*Ş={XYYIGZE+6 p]6Y&]y=$k~CE"HK8{E?Tt6iS6+k7wöm3dN&keăkkrL7knTT WjXĸzwex$$ YKu@ *lWo `P}e_7XE*⭷bᇙ>VC40fgGN׉,-(=J9? ~SKK /Riq"FT@(0riV_7ص}xd#AX$Ns&} {UVN&+B:]#{3Nj!ֈ)<ff|AVskk6+{n# Z#*ǀ^)wfg||T ϓ\Z™!wS, 8$wd'qƨ^(q{*RY\d',/M[UhbV33U6Z@-VMpA;Jh}]guL tvQ299I>'9{*bdy7oRL)b_'o!_x0Ы, {VW]x3g(̐S9ܨbj)~ *uUvkaM=/^d``<xcX}5BPdVd&><꥗8m뎃V a= | ERWׯQƑ [;pYn 0??ύ70L7 si-[\/߳;Q/bYʾC5#, $X ZYBkjfPܼyǏw%F%_2 Opk7FG|8le}-d}4PoDt -*1JA w?gqa0{wVa0w$@iOevb@2iuB`UȽ{).JqSz1֗Wxc8˫ @H aiF F7֦J?G,FmG} -Ĕɟl)@&s>-J&!u2bq5b5#>%tEXtdate:create2012-09-26T20:26:38+01:00{%tEXtdate:modify2012-09-26T20:26:38+01:00ZiIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/nc.png0000664000175000017500000000527312625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/dz.png0000664000175000017500000000741012625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>FIDATxku;[]VB@`#d [vDŽT\p\UvUIqšW%l SZL$]vG;3HZ)Dhvvtӧ\KWy~J&5w Щ4=r%fRi&J X_zVFPJPJ)e ?(&n" sr=|ctڳ0bP꽽7JY߿硇ܻwA-vx64ѯo)|t䚝.۱:F@,5tY>\w*CT2K,9tz˖-ÀT@ 8vgnZtqw3rd)8@MJQ"F .z̡4.@󬖖9m߿qFO~#H;Z[sykM`R4RZ TEbDi:hZkwRQ(@Cc%KA>݉r~k*ԬYKZt{-c' ,Qٕ6fCs(-$4\xrd0W.%Oa߸T]r5U K%b_x;ksLc!{eA>r |oQڹඛpو6IAhT(aO7up&$_ܪhnMu7_uY?5:vs0/Jqo܋X|' (}BGD%sZᎀ\;MwZSu?k Xq'aB/qTE*8 n!DF1P;ʎ,*B2^8羻~Q]Pm@8a*FᐢZXf;5LgNFxSQAc7#ș3X+F"G { } z_Hu|xV+L c,C \!;د{Sφ?`3Ba2mh~ʽ9%U7UM(oFR;=ߟ$6T_445R @JTj.eEIT!G/x`ۋ`[g --i“YueQsJs:/ď $6=K /c=l%waE t2.Zht?Q~D[ׇuޣO!'GBc'mH`̢m V-La]5e9HIubPs{62tgJ(0qg>z Vw!FQhc@LU'"񴉀U=Ԫq ʵOlmڂ?L|(Ibw,WA0W(eD dRn*L? oV+?>pcTOT@B5~QuqqlLefrZ$'RJ/jVg67.?jYAL-Q&mBFt J 1Igd!7b}TGufzeD)On@ PP"!| Jו4৛~lP-D2wn 3lփ)"®~B3: "$+ lf1$`CgZ+3}uV\wDa 8w2I(Be`_˞>GAӤ9 {RW5-+zlDArC+qnKGo!g)S4pc#Fõx)Faݩf"TobQ _Lh &þs?^ ZAG!Lk,\Q.z\taaŗ7Ppk\X \ݺٟ$+s4=!JϰbAF*#uaTm->|liın$ +%3 f#/jV3k6ߊ7W9#k㫏2e"^##O,̍dݙPaNn6J6"6oo8Ů.i1o*6NDCNBC3g Uj!8".+J,k K

M"E$J-mTl+ekWf|s-Si 052H+RőՁ"N& ~qEĢa{VaQ1EkgIR.ԂX\j9z5F 'Ƥݍp5WtTRi _ ]~Nq˝EIDATxyTՙ^izc7AQFhf$&9$:$N41qQIL&#Q]i 6Ѐ44޽{UuUwÜ9:sjy{WU*Mt!\$9'ɓ=\hʓ@F!.;V0j_BoM,^!0$B8!@kgpV'D&(Er+?{"eZeL/Oކˠŗ_N% I}} =6ǏDÑgh{^5Z;_Z[Zwjj&`e?ό}7o|^}FB0`JJbǴD$B :qPPEE'PVVѣ@BJi&NWc̱jZ>Jߊ .yWT  E(yvSJa1h;3@(?P\iϽۣ&M;o/(?s_Ũу,Z|rYD)/hYJK).7%%@Rx8.?5hYa(f>_OkK7ƒ%[ (--. +]Jı 0n?ZpaZ<+|SkXO3g3 t*#䴷qeDn&4vāvN9Oy(ַWVf:zj `VJc Xz нG#D"ta y4k6fWr~.?7aWҴFz~;7DYY w9ɓ裫ذaJi,@+t$ C Lbg571D_aջǎ_FKlgqq C8b^:T6D> 7txws\BH)4YoOÿsi[ʅ M3?o0` s.(&i,!8b$}sJv}v=/eNEIo)+H@霄V [;Z[hJyO#@ ,=I7ySϤ`0ϗBFwwɘng3hY&cH`ڴD@y~.9w -m#:z U3Σhl|ee-Oǹӑ1脠+>&'imks5HY}Iֆlޘ8krØ@޺2}u*޶v-m%ƒQy`si=R.d{=h%ȧ\4296@O!fw9P:B/(:뮛BUUX+iYQtΟZ`)0cKٽ ځ;N%Bkk$iN4 :Yq;"e>B@kAn9sNk_MEe!ix)._^F-p{⪑c&9g8Æ+-`L"}iZ|7ؑxؽgA$Dev-e*˝59J){vm#*Y\uDh_GKɥ~<wl<^s3fph≠2NwϮ<h`*yFL4c#(Mg{R i=9 te)B!W\1 /dԨJ{r8 _8OC>~!"YDvh*`׮z~Yn7y ^Nܹv O*Lj{ KX/BGGHݬ*uAҹF"&K~\.lyCȟ>{r%~@^\akwn䢤ćNs'km]XSҊrbe] 0 pXpץL4@Ν?0/H׬f}v~z(6&aC=}4p/vn~=/'iZƍuwv<#vlK;G08s0D :SA45F(eG;#C+8n;8#Xy4~Zy_ZA$瓞T{j3[+cV! lfGgoL$4p }?G~&͹̻~yԞAvأ) K[>@ - OXE0451Gx'zP*=Y 8Gm4> W_%4j$%yc}y5:hw.Rfhm *qwǶ{^b͚],X0cR4gy&Ѽy:VoFto/—8N;ٱ",6?\:d,jk>Α} t23Mx]nzbW}*)Nﰓ}{qW0aƲCG.'ӧ'.gyHjZzIJK)60< 5Srf񃜙Xu'!GY -*J|z2%dmPo[ 6ݙdp$&2kCڦmx'%t]'nEl06fw]uL!Mvy1" Lc--;6%%膽`zOHjM3rɨ\{~uJy'(ۥܼLt//{*8AO㓢S=^ l2=CTѕC],=k{V6$OkPBdúW>%4 y:GCel8p^MbVʤIĀ:Tpy+ݹ3<5ᘢX=7,A8SfJ *rիj+n"i(@#'OR Qm(%G n2v9m%.Bo0 AIq8 ,#"aٽ7%UEsxl0i`v&t5zkbctgY'|VìXRb Ԃf?Ĵ=9HBbICG1յu˦ !eҙd*P=z#xok$ ;mC$ &fi%M,/'ufQ%P8Fw{c0©ɤY=']!X}GcYVy4ɐrS4 4 isb 51 zYx,D')]n ý +'`纗x<˯~Fd<,KaiR')IHRfV1P⦅5,wL`{=nނfǶa $O}s[|mM%HoK͙iEh5SO7n'wB 3g`#MP( e?є>rJvM5J Ywbm] zqTSfn=RWMՁɅ?<=c8 05S^m Q |:Z)a)ۄ{Vl=ODְnv9716"իC !@+{o05k7?-H38Btㇵά@99c M=I0~|ΘTLކU"}F+ޫOC'e6տ@+׽i@P rC6͒-m,K׿#K[QZcy쫋s.0b>_ӆHdRӌnFwE[i @?0斌2_5qhw~"2u[SGj wZU;&+@m"K{XV h:\K䅕G"@eL.vmn$KZ~ ljb&mhQ/[$A2+e>7|F=!~YUGޡu;%tEXtdate:create2012-09-26T20:26:39+01:00p%tEXtdate:modify2012-09-26T20:26:39+01:00k-bsIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gf.png0000664000175000017500000000527312625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:26+01:00 %tEXtdate:modify2012-09-26T20:26:26+01:00QIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/vi.png0000664000175000017500000001373512625666430027160 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy}?kf> 7qD $hR Dɉ$JE*츜($D**+v%(Q "A2 )}µ]}sO/N dۯ{ni4M߆$rWSN9h+ZT@wm]kO`Fe15?/9qMS4!-\ķ~oKl#s7g6 acM2sb/|x^޻ /$6baOJ;lH?/)Hil;z4?eM&JA:Z:wϦ!~ ,nseƢ(@JhhX̆u גI$AJ-֮'R Kճf6A#`Q{;տ܉Iǣz 8γmؙL͘5?⣿2͞a )s۟y룛;';R7{̱cl>Wxs:t)ZPa8HI@C#55Z 켳- bT>8B_$[o] @%u+ Q4//X}m[; uj*ژJ-U]!,̹bF)SI!Nֶ-yϒxyBO=]ۏS[yᅓ:T; P7MtK{{ׅKYd&C#yQԦfzz#7Gxs;JUE>N8<`7!T4͇iE'Uuyd\+;C!4l޾erVv*CaqB_"|?JK[Es73Eu߹,@!V/D|=[+phil (jQhϓORHB^jUҲB N$Qdž ?D]dn)V(k L_sz!B$B"vՂ+^4߹^bY2CQjij&nb '|<((i@A8ǸܱmHT=J޽dy5sNATx"jLuFis&Xr@r~+oMAm圈fDLb( E!\c"s۩ 0Faw)r(R,;J5 O(PB5+My )sn5*{0YR P>sC(Q؄bPޮ7RS֮+WRWYI׋i3 D!Q:@dy/NŶm ̕'%ҲPD^3]X7!&L0WBJeVnb'XeGGQUUXpbXMaԀS$ϟ'!Ŀ~=N&MstólCS&Cc.PLO0N11^,rvXYyGkR)*{ȵ,_;;-4 E zu _ҝt,)Iwu1=p}1Sw=Hߵ nj,xB D9{[i0}CЗ,!v8w苀(J ,?Y*_FYi@C`6mMZa!9 fIrTĉ^R)kA, Gϒ|-RS@%A>I)fIRLl2aK|@ ڤ%"ҪR $}Y&K=+8=EMYVp+.GaQƯ> (戎<2hjkY!ȣG8u*3SUJNvv BQS H0)=$ugVo`{I4Hv%8Ob$b+l_qo.%м(P6"EUբL.EI2Hhn D*QRSO`ccq ̈,+b%7U.r qCp[c5L!m 5d?U]gP-_`n1&xC: vHPo1[uyO^&rOWÔIR!%ݪ;ľ1PJcX 3soFVqndS*2ac4/p;.-,\CUn\aK)RIW3lHW15FmJa6k[Vx6 ._{Y*F"G !RIao`TCҍ,|j2g\AU sdK-%aӠӗDVCf8aP,Xމ씂á\v5NCQnr#-WVRDR=xZ:ql%Z37Mcm,U[Qr}c:k߬"[=e13 r Xؕ]x.Ǖ궳V99C6i? ӗ;x S(E%IQ4(fUExye8ijRiK˯0]A,uMp+&gO]di&~&,&63eHU %'F6-Dqk}d,ǩl`)c:[un# g˕W~5 mVhƬ%b3e\'6Kh]ʊ ӤX2JTK)tՄ+ »rN Y뫅km` {~XaKHjD3ԯNqxrF5%>??z5O@4e1dyxq]8bՕR9{^)snvD2'XJIa_w+I%jZx,LSvc}ʻW*yZUYޒrmY +_^o Mq;.'S $P1MiKa.Siv܌PTGP[x ϝ,, UbV.sKS^%%&ikN4J3:eC}-J `5V@s̅rs ,mcNJom+㿂nl9l}+& Ik+jQ aXsެ.Ѯ[V"L(Žzz6ղmi'*և:~&bVs6tF:-Dfxwaq6QK@gf¹!:9\^KTXWyg.0Ya$TVH X2jEQX^rO,2E2%=EvLotQXhUNpatFbpΏ.Xhd^0 4Ua oƧI^yF5hABϞ5p0]4Rb YM7Zڏ`OMSUUEuM m3==̓o<Ϗ"j6? g{=|®WPa]8ĹdqyRJIj͆_=~FsN2!EQvM \%_?D*!oA/!+ ms*(כYZD2]!Acdp\p9Ҫ +!$ uzƐMFMFmjCw%~">v>n/K}Gذom##y+7V'ı <쬎߻TU>p?$ˮﱩQS?O<р|ʍ8әa\ݱ9~s!$-mLں{[w$O]Ao7 5Ĭ\=_b@ =i` df:?ŝѲȊ֐ʿ*6 ׶%wo>d hL:{6!bO.[yFQ|" &rO6lV hHE'TFl%lW׻r-yag7 _uB 6ǽa "6_1w>Bks*}/81>'F.i mn-w>KAW9s#ES|~?TE}`` # ׭P +)؜|S_ԑ ro-Ο4.L8xD&MKms E$v;7LjLOqgjvl t .^"C/H;ӌw 8ri+o)مpX!W~5o*>•W7eaWlx %9#YO%IÕ.6vXe| !C' cc dϣtJzzNXp;p%`ܡ@jǾyE&<)T24add3T vYR?e$ō&UBknj~z򮤳kQapt1$##d M sHѬr T<uH)D!oF9}v65ΕI6Šq7xu%`i$bǃ:M"ϖm&6tزƳnŝMuQ iϷ싦bYz3eI|>%KjM~xl$#&P4CLZuI1SnKU$4-a*uL_aN6UUNE 7*.BRY" PE2B")2S~y/Am;ޗLN^,7 oQ[.PY- TQoO "(0S @D!c4'Ps MfL^I@(|tglͻO<8^&]||gBCb˥\/tYsC/2$?M)'1 %tEXtdate:create2012-09-26T20:26:29+01:00Q%tEXtdate:modify2012-09-26T20:26:29+01:00bIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/rs.png0000664000175000017500000001073712625666430027165 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxiuٛCh䒴 ɖC08K@b'  +H 0(YH$ysI5{tˏ>{=h i=9}TwWyGޑwWWԛTfM!59~%frLr/A `fNLyl=RJ(~UɹlHGwsQ&דҟ1|Kg.4M ~D6~|zǧWzyhK]MD.tv KI$?9/C͖Zj۷/SKW Yh^nqFiXGFp4J)V]:@UJNw+I.n;=O%"Bn[4ONW?Z]Șzp;yON18َr=JVsʀ>9Qf0^оN ݊yyIw_q]-GOCOL_c Q9Y֘5VAOHtY_ޅRHjfIM͡f@%̪R|_yYYcHG*4)rr`1T(#h#@ ؊!tFnxu#WI39c24Z8D7 Wf#*yjF)}(B^;QQT4EfU9[$Er\yC)ČAm7>ڥS^BSTW`/]' Gc.Z-CGmld߱N%с8D'>vy}ˆX |N&-Y./ r 3?=.hkPJY(GLԈΞC=c2Σ9W-PSޮѰ!l#:z4ܴF@GBtEa/\&:T"hJ˧ci10yHcWlXe媨R;Um=jPDOo9ff8*``a0Υg1s%GiN,,.Ә)\¼v~X].ڽ)}=@\Ja62ykNb my [`R0O]Bmf}^rmW`&K1ibn٦]}Po&h!C0JMj 8þ. w[Ez?pvvҵ˽J-E{%h[6͖`*.+%W*ȤG(sٲl5 v0һŒnFYk,nT C֊1q\i74E[<^0F傥- sVry-Xo'.!tt3Gs܃` V%3;mJ֍ #vr8۩g{҆D~QG*Qd1(BBPj`EuЮ #J2fb&b lmnne /&C|2puyj70Mu )Ĭ0XET?Tʯ\rַ6im<# C`Lǥ0_gJ]=/ N72cEhf ĸQM* Ė͟A{cG0v8R5~mcYX]E vlJP:$9W<Ğ%h'D`~TJYT%"ZZA\IӨ:ū <ֵkDh*1F&'yvT_G {lvD'}md`"ݹ?ߐbbIQy)"AYʲPDzv56/_0FV;?PWY 妈䷑m]ؤ7^n$VYv0@dS_4ze>|/h"26ZyZac(xeLc>{A Mq0@( b &pj٥66SrJhm%U=w*q#*#Kx #?ʇo1q6.<#ưh)m -s;.tax#YZՕl}uINWUt{Fhc0&"jeGo= gfu\E64_<~A[8Ftrk|o>Q٤㘗_/9~~$+x;Ld\r:z㙽 enfZ( [fyڠ4  0_Ą!,ykllݲ ?oO|t" U+b ,;xIo;ĂTOގZ&\Dmi}r*܂mYV@cn2ՠctb^NJ`٤('9v?F>S$ss8x=C=Ҹ1g.pq8 PV ~dGbl'KPc" 2Uey9DXoxzn(#_ѭ sGqxJk51ʥcW}ه_ d7!K${Ÿ^Cӓh#ضzds8W9?Vjn= n(a2_'Yyq\'^ CTR_Յc(cy{[/ W 6ԧ?}%z`e C޾i܉q1{z!: 0 ˲=sq`@zaXgQniAR*{_lY$A@=Vt677F!+u,+[r%yQ>h4dssivSӗ\^F֙*X` pccK.Ϝ9HiC@B%NϞ}'T*UJW `ҥK}gΜ9π _D<ȷ}MLLTǹi!mYZZj<3O?cudžKl%5`my˲<n5QD)e(j]zŋ_>+vZ_*Fv5fo%tEXtdate:create2012-09-26T20:26:10+01:00͘a%tEXtdate:modify2012-09-26T20:26:10+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ht.png0000664000175000017500000000501712625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> MIDATx[n%9M$Lz@#V#0-5<KV,x<K< @HhCϱvU:RX>||\ܗr_o[j{ OiD#ujBXдӦ> hhf~R "`nr榿N-zlsffnu{b`Kh̗s㟵O{fR@3\ c"ÖÜcހh5Cnn}zg6610W7ß/vdc:/oWJvcl Vk$0-򙕞si˰5lР/Ь7Jtqg/'$T9j!5;VŶv-nQ@NG d)ы#@R/oX!R  4#)R Ѩ(y'Lf@U5R #.Л8cz;HC@I &/I1Ugh213VCxscBey;9Ɖ0eHۢgcHS1 +d!=5O b;ԣ7rBQG[4J:o, k5iT^!x+6:ٵjӻa `%F<ݰL09i1d)mw6xrR%d[+)7'BSzDLZ~Yc &K>U±VXYmZfWlRZ3L-+Koezf >L7=R^C)yi&t\#UKc L2[^NKQFI9FH ^Dyz 2= J⅋:HT4E#*Iom@vie=yh_"y5 fTVa"yчTarXyKYhooN/6˻i1x@3 ~Օ;La6Nm)GJL? OMz< xLk/61kPA|"(DW ./KVWO邴?^~ 9=T-3"(W%8͝5EKto| f ͌-3.tt^@F-wi L%$H 2ae@t=`%?Qv"ޗ(b 7gɱ,M/)gkL}?KJщo[^ hT7h"`O(j H~)x%zYP)LMLea6TRxrKrmh -xʮL/OqFxF:ʄyD822%ەaӋmkSˉGɌH3&<|9ٽ6l/A,q(N9013"իMZ*WHE{2)ɚ@@KtueF3/ JĜ=j+gO@ߙmc)Jj;R]Y%z90yHoD˳%z)_Imddlr3FG/5@[3crMh.nuqVk[0`~5+f fQg&*SBIV[4>m(hw>\FzQLj+k6`8%@h@S=ݗ_{=iߩ _ wz F` Ʃ"sÿ?N0gͯww>>hyWaBGF]>i] 3WH|l8x7%_u6Mo,U)sB @k`B>[]O|e\KS6ćC_5 H#Z/n 0z}i@#* k?Cx5փ$IVJ-&!ewpz_n!\%tEXtdate:create2012-09-26T20:26:29+01:00Q%tEXtdate:modify2012-09-26T20:26:29+01:00bIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/qa.png0000664000175000017500000000715612625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxsy=3 )Q2%Q-#ǎ]RrT92R伥 ycrUyU'[H(A.j^0W R~|;iSy*OqDu"۩$Z|'M)8Wv'L6BX6Sfݍ4]iRʠ} QnF>)c{!b)-ZٛrE{ X1J`ٷ[ogϝ}dWW4 쳙`q&{Hmj#tT fO#Ia2~msܚke@F  ?١C?v?{D__LfK8Z,Au,X cцF4V Cl_ +D'8vJgĒώtvk07\[VVlMf?!kv֋ئFn]D?R*vp] Dy}f( *=`Zfo.M8a.Z|sg#@u/B-qCqanwohfgt4C30]nlqXo$TMZk0>JLwo[WX7qJeet͛R7nO Ƙ:"D C0 v7vX7 G XVqSZKxIb}a6<rt㋦k ``_kQ>딶KQkV yDf@J0F/M_Fklά3L*kxfW˔r8y +5F>>?ټ>LRJ;'Vݜ"72*b)ed&b):X?~ 73:V;,-R߫"4 мE&-={^<9L_<u}d߻̱^fnLhrMypR&M1k/X8Q&?7@.רX[b8)6g<Dzu$B#}iP]ٸF` A`޸̥7.S+UYy~}guxT;tR\Wzf/"9#HIXf )V.aVx? a$npd{{aL_ k: @R`eč _9XHRNkG_LeZJ v30 >}gُg(,ncVH/&1'tiYu.5L_àZ%D z~ewcjHpոJFEA -@%vt7/7/g+L_gqV.Q.!wUAc{##)d կz`~͙5r7nτÙXK3(&>Wp3}iɆX[P^P=YOr楳8УUG"'x: L%׊ԓṝffB} "m ̏߇}c{~ w SpX ij?LHG б6g}8,S\)`֣56ғEl} rX}ă{%/hs`Ѣ;nXA`άlz KB$w w1pJvchَ&Ҏd3 <_9ϟ g7ɏL1Ci}rR2,Rh&D_HM͞jxu"F+Ü~,/~ō-]Pp*=/B3r}ecN:m HW&zM ⏟5IwDYCofÓFmk0 Ķm,²,t]R݅ܺ G,ݟgwcǶBSk2H碛F})H$j0mۘiﻏ=oV-F}.bZhEjk)MA63PCps}2ꨪ8? P3+j"z'$ި\EGL<-LٚYu'KJz%gaIL;HOL5M ܸN3\.W)~9fnLpwJz;YToCRhbQJH1 >cbhhhxppa¶m:zZVT+j[!΋\E U*6Np?_66&&e&׍@ dTs_jZ׮>88x,ɴ-)؜YcylJ xP{ Btkuݸqp:ZpL7{žj<R+)_T)O((sBk%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pg.png0000664000175000017500000000717712625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxu{߯ k0*ҚR#GF6UR9Jk"+imӴT.dj2W rqqK0~QS ~}s;w}rΞ93pSnM)Eާ7hL,14 4 !P">յjK,Dmnin\*ڂO5Uݾ:ccNO P4i%tJ%a uwsR0PXږSœӊ苳J}9bZZm1N@́lXJJt0 Dzzd\w`D, hTLMMCJV*NN;d1-gp-PF7^=`l}=a~LD*8!—E(B(:R"fYIK?/5V)QDJ%fE;D>`~~W+"ˀ"BVV `#fxXȭ$yV~WJg,]"PmJ Ƴ> Q>`V!"A̛OX `0:e.-"E ,zq0K ѷn60a! "<DE |sX. r ޗ[>??Iv;&aI1xL4MwoP7XߙK^ߡ,)MG60<M]<ExS'DXɊkqUML__O+Z P{_/=J>O4y|2fʻ.ap2KeIvS*vnmB~x/Z^zflʀoY/!YؖNq}X&0`۱(z.B6PkZsU͵/rWK}%^6H3-hm*?jvu]T2R_b{m =fz.` ۹MdL|֩k.\D'm#|ybׁqKIK/O?j?C:T7% K*ۀeh6>|A`0IpI u >p<^$(v샎2˖-k9uiU͒$`]n(D.RHo8 IO} 0 P|֯_ϓO>"bQZ*T\s+)lT#Cq8JBUN(jcJ\' .wդ۪*˗/gͬ_Kk.8ݻV" _4 +܉ %E~<^rVxK3 ?{vПjquV[R8we||A0d9rJB{"4*ɶo ۰]ƀ\*ܫp¿) a` uK7̑kj-)dll .022Җ?vUK0j!+\r\a¹[ <mNe(Q;5{=]8}K­Vl߾cǎvZmc۸ZKo*w%"SVla".uZKX}؝Y_PSVd~`޵t %l- ѣkN" Ö&*{S|EՎ Y3@S(U_zڵQYr-C?H$+c^+WR1ƴXZcbݩ.}\-|;3c4D$;^"+lW86WF]|+,˔e}Q* < jJ(bhhz;wrʕ%UWƕYSNPw^uYIWv3-aoui܆ غu+w}7ʚ5kxgٿdC<<{ャX^zpc{+a{qo=͞XMN )T[ H(AϫJ__rC1<<ܒwiUevv/j*Yt)gΜiqqU*S&ט֒mlB(|S'9TZLOO3>>ÇQU._LRi 3c8~8o6?z"Gx繲_I=^Cf{׍)fcTRāx" @V㩧ٳ,Z(R{T./A҅&T S{~Y-"Iq8;T** Νk,cccDQĥKLӂVr`{ê`5GjC5di#UjQ=o`pg]8- "c}@Ty]?֏Xy`zck6m8{mX^ֳr*X. r.A:ɑH槯\ .4"m60E1`3{nߟ&`ZIZeǎ.Kֳ+~|܎:N] JR2==ȟӬwZHDS; ,#3y݋qW:y-i HRYM_+8I.LVcݺuTUz.<8s*;T1NVdA vN`ɒ%l۶[onFFF8sLu>wU66HpI>$4عs'sA088c=ӧ/">]bU/t])_4f׮]k/22WUmUNͥ 78X$ɓ̛7RDVK֗uo}R;i5U6Ʊ_ @i+TJg._̢EA$O_k"DgǝK߂[!3؁n\ ܞqSs \!aG?Teف`?x5 I%BBfU{Ki3"/ɟ @Sm*RТRT[ĮYW@6!Tu(h,E1茶\uǁS90jEyrB5q/AWW]3_ v+u R_ vb Hi.\Z)=zQͻֹэaxŔJ`fR~6LZ`go̻3 Ap=W~˫FƜV*'(o,vI_n ¸ֿun4~b" lӘb;V4W%atI TIڂH5zgP {3U?[u ,)^a؍j}^XhDWdoTьMy?}n%tEXtdate:create2012-09-26T20:26:42+01:00,%tEXtdate:modify2012-09-26T20:26:42+01:00c?IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ci.png0000664000175000017500000000471412625666430027132 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxˏFǿ*cfvgvvgv!Y)<QxJ Dns)F.nC 80ڬ&`z2~\eWlwOzUjp#7r#7+4оςpcN8Y,4G@ {3b}C񹑤  _mY[k_z $ɇe:t\ ~n. 33ΧZgs{fzT b٧G#^3;gBanjp@OS- g|e@N6f™:<[΄h Od]kbYz~N?-*4 =1y+&t{MEdp5ptx fz).г@N"0/lg`.HRS?a3 +_/ݢexp E-1K)Jy1b_.s $drtLo_*+Nnv8cZB%" ֓ hh!lN ,p (ni8]6WhL)=z(88Shܖ.--4].X,@%*--[fv@\H,!` €N,ƅXyֹmgEȨn0F6jJn001lH%L3 e$zbXsW8chHO Ul(ّ.P7u HӔ;@]kdc.E 1LѾ9ra^15~m(w]`v?K[Sc c#9ܲk]͔smL'R/APSn 7A@`b}9D\4vtXq8E,2 4;;ʘS8ZvFk|sྍzͭtK*XJC=pp^1P+lfu#X_t?fu}![H"hz$w5uX H\rKe a,%5+DѤ0Qp_b.7;o+2i g&[Fk .Ӗ8Kk bl|IUDS^MP`fucgQh]aWօ 2pU 0&f,+tklnvyذrc^u] yX d?]7ا @bՅ+Bhyb!\pvǡ{!r>0;]b cW:ЅK: z N~;V\8 Xg^He_z͆v8 &ԭnY{7졸Q h-10 ۡ]ӅcUskم}C b` -yE.HZ5Y(O tE!%\IvO!~KA ll{D62V%[n7hM[o<%+m@sukx-X`"s\\IdV鍴 hmOo 3:l}@3׼+b[+z $mb  k.Vp0h*R:Qb֟s45ABbz++vpJ$6Mu* .URqEZƗByFW ykhVuY]Ćg9xYfNM\XJl,#zbwPNhXqOmhhO gS#(Fvft&_jᢸT;p SO4@#>yշӚv)^Q: 6<-NJmTM~?Bba ``Όo/F]8G~ yo*婼~?f%d؜ <̤|΋C)0ᲒY#p7Pp <_mXWOz1 ȅ 1P1.glaV%7>4.1&q{FiNϐ908pn84H t('!fL(c]dFEfIz;WY>+ {GV5dOp !IR%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/fm.png0000664000175000017500000000667612625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[YGNuuq $B A7x' ' D I$8={2gsCoU}z&H1Q[ugv]ookqhJ(1bФ'~+y@Ǿ;ƦqF''iɲ);QUn^c`>ӱm] c\ H\Յ7T9R !(Hg,6!Zt ^SN=Gȱ03XJ WW>~}?0@t- ԭ?˱?kM* EFzG*cXNi= )c RJxlm5??tK]K_1>ZEf Ҁ$L"dLVkn & @%fL^0 0?']3U@U , \*lsb(S`@CAKGp(Q!xTQ, @~p-{>,u31(Zɔxm*H8' 4q6oePQmĄ1݋Lpu9Bpfd`xHLQ<e0Hm-EEh 9tT"e+BQ3x>C]q^h3~9Dߓx6N_lG&.T S$ێt"Ԙ8дȂ&cjB ηq|Ms5 8SCm|pi Ihd_acVdNӆy#2 |W>3`N/^^[.Mo|)ǰtn}T[& cT]G&Xh, : Σ]s5'm<:*.;gpP_l'1])z%vݓ(ٿ)[(/^x/,\@w Ф1,v£`E\l#nwϷ@sm-)C/o \M4ݺ sy~,c Ku%۫6Y#qmoEۼ)QgۮGԶq8G}M̖R[HX<&UJ0;ܒb2'Y=2ɥ@.be1ccqުJJcx{181dtfZM?yRl`~RrʜLLkFtIa,6fֈdkоMgA8T$0Ufj8p`b椵7aL<Ɯh5C~?Q3h5\4a 8DN` V"T q9^Cŀ*}g],jZ:qM`X}1Vs3xL hOd䳤,PKLĨUfZ5^# KXl "[RpK'z:&-XJKGœ3 g bB)Atx8<@ZIꋧ7pb’ٛ&4EPE"v6n׎N&}_?6㷴Y//$2Ďu3x7nHƹ.dnys~ cdfjhT\Lf3QD O Ck5]MU˛ƙ.>]CgРWxC%W>x\x.[6cS된[T J%{rSU֚+ <j<ēҦk7 Z v᳍~%lt|MUqt cu[_BI!ҘhtW>f7*iP@PADI"3̫+|eA{ 4%-gMT2mu&Kհ]FfyH/l!L ;WKlb%*'2KE)gOɳۘl>,Drf TlANp 5Ԧ=$DHHnXS3īYu(k SU+ -(+Eٹ*ỡgƙfAK Pu$Z%9OClث?dƭ @m! AGyrsLԳ,[&2-kv`Z|hO2yx)F|Rzǽz鹎-dʋGд_6:JCUA2:ŀ2&\@f*`3hjazTO3J=Z0{~<  8>"lZ ':bI5#جNWP<0gOӗ"@ѳudu @n'$e1H 6=6wq'(kC6 [DYSQ0kZXw4|@w͏c"x`}/$P8[snX8_m74BA!|ϯj?H"aв"1, f2`R9 bNV1ϼAaI,(yYXE>2R )MGՖܰƶ\QHH^_&pp۽/1+ŕ!!2G |HC{BȌiOO=R'u#_0daOD}{7͡%7\nlU)"^{+Kg#C>3o}$զoJcrL8ȵE>{څs=`@ mxL8FqgmNH$jH%"$Ags 8"Ⱦ @@#q6:hA[Z؁HX`ˆPnDVayj1-B zb窿M"uBH_EDH_>TV/nʑϕr>9 "#FJ%tEXtdate:create2012-09-26T20:26:36+01:008&%tEXtdate:modify2012-09-26T20:26:36+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/kg.png0000664000175000017500000001103612625666430027133 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>\IDATxy$W}?tz׋m|ads.`0!&$ CD'" (R%E# , GpA>b]13;g?)8~W~ェs\OgC\)@]i 4:gN Kkir[ JEDqna4x/")b;N$p_o/ɮvoYp ˮh1q 3n0~󴖧`I){$d1D ^744>|d=t 5e_k->RjcNԠSc̴J'22j$0i "#o4[k/sV]="iƹ&+UQ]o([UlB`A=jNCW~Beԑ^SvWTxk.4EeZrB+.%׭%ӭgtTc4^I=/uce3h"a)Uҫʾ x:@@U+5*9*2SOr(n T.{"gĒƋP Ԁ"!2qC/""vt%LCvՓ.kS ޒN(l2wlMtqo5:SAg  5ddz^I.$}k+aeoZ`NcPviLjB~zR~~7l|ROW'*rAwPbtW{(WW zPȐMo^f NPdGQE4,ŮWT^4*c/,E]+4HnBa6I?.(L^7w(Q nbʵpU5.)_ Bu]]DXZ=8̉g=,G*ޏV߱nQyI#> /@Cu]36}em`/,;>x>3x?3,a!cknBg@w-t B |Őګtu9{1Y (G&:T+mͼ0I|fEEQE\XI }oic/xOg_M)[H@whܹſ۠H7Y}1Cbdf7M{F3eX˨qø4\T^G)GA;EO>AK@jxQq ՗`}eap58EmXy-`< ];[B}eX@ŪK1S Ć[[a qw 22bRjGT_: 8*+oP}y(* %NV<,.ͻ˜ - KsUtSsV2yM>+4 RvEo.chϒ-$Z2J"٣.y;7weg*(` M~;T ΃ELG\{).kK'v,м駺KGI1A 'ˬVLն|w/ 0]M"7ws}a+l( a/)_zQBCG8+!\"<_-, BmKy*!v={f.lFY.Y=츍1e3ȸ\!(tS]k֘74V%*G #>#>Z4kF]u`WCB].E@̀{f&f0܅ e$1% =E앐ҥ>K@ 08ea/H ,zU14]08m*y(SOrϷYt1$lChJxg-8]Hi7tXoXM?OLx)2ϴǀcX*Kk!$VlݱRǪ]C՗ AS]QP"RU م`-t~`z+jwiU1y[ ){Sv0?zN;T򳏤K3 рmpmOUU ՗gPZhSuO1.cnW i[<:\*QNTfơ9?> $n.8LD$$W承;KT_2`]M( 9Tam_/ J h\,1z Z@%:8uSWgpƎo'H&g;D4.1,zkJ$Q2ejXycYdm?U~cڬ"hjD}!A"(~JeNVgv8˴+`/ -$7?]YJ\?_L#ߚ8ūWw 8BW xVX{w#w@`[׸4-O8ҘK'?\g6X۴FkyxIz?d*'eXpʋ+f&屍1>U AOVf5> t}W {g5Jxg5g>V @)Cޖ3X,.Uz! &}JAOI; B9?Hwj-EGgm Er '?R^ 8᥋P$ !.,"s’ZR)̻94tqUX*ص淋q Z}EzUJ3JT$,w<qd{OQadD:~3ѩC.([kg-TApwPڰ O NZ(=$xy2np!Z̼b6䋞NDAu_!_E!lisn<%iʼL:<<d6E4z<QfNT8^M]zO7h{XX (&0.ɿn$sDE~YIm2G3B$xItW80!.[R^ kWr.v˰!;T?jèweȖ DIDATx]EǿϜw{R"C!mTSJ$'AcO0&?MP I%cVDj)ewn}y9wfΜsF{9s3gϼ9t5]MWo˔JH'FqN9EДqN^i @0sȎ/|MP(g 0g^6nii ̲i-ν޳OY @ B J>w`uw+ SRI!@D4"Y@Cc ǐ`J)tm\Y; /Cm ҺEP(j[lZ bF#9HC =k̀b+NTF6Co^Z!gNJ]6{dv+!T%xBfxL 5" :€QyB\{{x:GqLx&^DP,P-̔q]$Ncp%рba} N7?Nt'&D Ι0\E!(T*nO1~7Zu(ݢ mUB{+]܇1׺ @*˅bBg8:vW3 59Sϒhb;^yx_D]C8jй pv"^knzųz *.k~]x|{k@C^gzʱ^ݒ"x_XunV@vc2~5c US* dS'oPf839H;':uØ..}4>j/ c΅u^C` p |e#!<>itXkGQ,ƉgYʲ<@/WcU ea^7@\9w/e%H=frmT" !r5(Y`wUܲEY!:| e4&toc b.;XZBiFI7Vc{d`DŽ݌.0sٙ̀gOf"Q-xjʜ +q&8Hv R\go~Hß(7􃘱 aAJ2p~,w UnPƟ%^YهvTaxThn{Q^nڣ1@pq^pwRme0:'q3gއ < ɞ6s oXeߜK2Z&m(ڏy H8أ՗9sZ3:Aau-!δ7;d|s_׹8gsU/ 9MD o/f֖3zbi(?/~=4(`HQ]2\9PYk|sQ{s/߅<ݣ)jS :m1\*qw_).5mpF>̺Uo`"LH-|ϟw?j/`[]ԂmXh*ζ7᭵[qty?U$|K悳rju "=msB|,n t6lz: l,csLϠ",PXjoBZ\LCrDTk0y,z aNG@z#А#8-o]dT{Ye^P2MIfqATo@ol^OR:Vo(`ؽ ͅZO0-Y@u8f Zas(5(@s JYj[ԃhdmstb߮vKut`%Re,5~8grLCw,%Tܑ]n@BX7vh -ögJ F~+J^6ڪdij0 @SCvhe ۂ [M6:i_,RٓsK+jm$H {G)J+{‰1K iXC`/dݕSoG {  H?4&V $dE^~#=}̼<?Sݹ`(>fp˲~a~qO4y@( o޺2> B+H>)*(j\8;84Yd˄g1i+QQp9Khxka`ҽy J6f9$:4^!2@2?I!l:{:1 Rؕ(EQ(#<RD)Wj׫r_%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lv.png0000664000175000017500000000464512625666430027163 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[K=ӳj YHqt!o9>cr?!Al Y}vY9.ُ=x2t#b,V ]ӳ !GHS=!#M4mӣ6AwwgTb0? ̏9ȢVgI7;c`<53g4ˢ/{p~@+ H?f'G4GH,F.Rl)Dxl+s`fhȲ\/^şz|ǯDz$"+${6Lh-1R҈Kf5T852 MgA4YКY299..~`wyr,/-yy&wSfbjq ^U9jP咸bL`nȒDGo}cDr;3RR MQC̙K%:ť SUj`(fݞ+3T㬋1yg D R,*,N\+[j~+1`bk]| f("(*ŕtHi}nͣX. u.*µcg=]py(SˍGɴ3$].1+SxAEfU>RWβ`5 ^HHvt-J\MO({* n^ k.^ ޳=&WE4@AAUnV]]@Ϋx=Q὾iuq>\Usm򅲛РJ|=5|"̕ϑ{&l2[E&E]Y VCf@ _ޮGՐ9Ezv(2"#d @ܗ?UVC bnέZ (7Wz4b,BY  C&B\rv)XE1CyAFc4؎E+~Sܽsٝa6x QiedX"iʖ6rkwDū,F8[\BaGF?ƽ{01L, 5NNqp ?lEA( 'е줔|.z#l \W)[ה-kJc7Jhd$1DF֍i$WxPotZJEƅYtUr]Gze8/p~tu?>?]:T%+-;ʑ\h1#8L:34Bxxu5`t BW[N%. E]dXJm)Ɍ$zPJA^pe *bW3"-֨ȵ'N%\YA=/.7 ^ l+ l֤(OcS<[bHȡGh0rRU,k([2sLEpk\kE/ zS$>$ 3EdR%Y͑Z \٭s6l/UxɌKq7Y-Ltn@Wo.|9y*UMH!]u-Y_)6\jG'.vQ Go8U8'uǏO>p C00p$yl^:Gc$!b&Z-gߟ}QS|o\Hc&nX߶>]u6Di ^x% M#T0YRSD;m8XNYjH v5lBa5`H[lÇ9͇rئZW q,O%pTVCј/%tEXtdate:create2012-09-26T20:26:08+01:002֘%tEXtdate:modify2012-09-26T20:26:08+01:00Cn$IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ki.png0000664000175000017500000001457012625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyם?UUWs_!!J(Y-:,+/I{D@8 G$ȱA5dhvlZ[uP)cH9z/TuOwO7:;Ug|&"}}+7&YrM.K4UrM׫Z :=ղ#3B!! Xn4кpOsZk ZkWl~fsYjMm@䱎':qy.j7ELKJ)~!iC|ꄨݢ8) ͧOוV]Ok9ſoL^!e-U@2O:ȷqDz,(, R*,VҸkħ wh{ƦTw# mo\L#yY ^p+&x%֪Jͮh%dV-42}EZBmMB[ӯ54v*\ heJhK\ )+`Rd<u @|Ըȫ#C# j![-KDD",Av,j S\#J,x]ͱQZJcKI̐s9pݛh1_4RP*Jr0汱@DȔi`I]̑{u-HDĉcXH0aҁN V (MQzAzhhQ040Bj,S7i ,t{+ gc6H[ @h;OD#ԭI`d_ 䜯]MnqQW݀y$xS (7yQsvM٠kSimTzR/1d0 K9&S@/p.qsW\BB].鸞cW_+'j&,o9Y.YYT;.`bbY,+e"5Jwɟu# S*W/鮖%]j;72Op ,j7P :e1,&2BW!_{# \v+R[Q| ֏*Kj>ZkrL4w&.|)9BxUH9}eSlZ‪F0oS灨x]fzoSh vwCK s@]-A՛f-/ֻ>8HD\@^:ĕnFmwNo D+%aV> 040fÜg.-v nכoY/ɟ)DL M0ϐN l>8n[9sv0fB)+g5Wjt 10^A$$K$0Jdۂ+n2UBWoFL,Up)Xf_T9.1cJo)Bh[ |nOs0V`X$+"w<򐳪*oYͰ~x p&f@ Vm ""vYu2`aL` D2$5(ҔX+b̞"Q/aʟ>%eQ¾ܦ˚hώv\Q+F’YՀ!6Ǒ"wQAtYo +HNb@X`rT/R1&]گr<@ 8?Z6+%ѷx_e&bb. i穀x4*ah|;-7B?GnoBpdT^K@oфh]++/Եs΂.@$"_+M(1$A4EU( vD˖dq?v ,Vdʺ^(S Bʸ?b 5R&6X(0!#%Why%0ķj|5=1$qr2Wg\ wSkܥDV*( " (D:- &4D8)# CmyLOiҗ*_m7RRs D=ZtN]7:粈 2hZcOpͶ*)$S`DE?SS*'K@Z#QD4v*%UXM@6Yx"j DF9x5PB YF6N@Ns,s(`ȦFa'ѳ7n ,̠RȦv#^6 y`%[)|ȲMD6nHD<Ȝ [cڋ 3ڈl{ٸe9;r%{7|+ZsчNe ۻ{o^.Z'B Z}A$0, "wm&Gɾsr{@_إ+t޲0.ޱ{]=[ѸF߂._‚5]O!ii`r-;_?r\{!/>Nt#TkGxu!1~[wP?n&`C`g!1Fyc.޸Mj-ί@[0}c#nD=и̺JZ>~wQXsM[v`w$ӻI?"0Bfb6ǥ4ShĤΆ00,\љ yDF Ҝcl&m,ik`EWi@ѳ/C~ H[@Fľ4:g2ujLiImqD<} : puu*W#S6NjcUW3ɘO`.hm} 4t.y?I4$u|u2hHL RN\}͗7ǻ6-j>Ud&O7p~x_k"Ç+EL0 dRh`8ͣt,&g"n[mB`A 1GuֺGo &dylcASQi קfySw~F^)ͬru=sDE),c~v)ŁsC_´qәbn+Չ4ȹIhI0dW<^o\`2W _i$='/1:9C.'~c'4#x#$ZDang3F\H % l$p} ([:ټ:ld&1xI$JkL)ELlC3-[ãw-aՂQs$ȫ.2< bM¶Igb[}-}]_FK24SNru.Mkl! ()8CŸUs\& 1,gXYK&^ǣwRx:Ǜ'/\..CH u. XUu4 3x_$aGH; q.l_Zd_ad& DJ&=8II6`dm<7X΅+{Ww/Sɹ^D6}{[KWcyD?{_;FjĞ#Ogy EKSV W ϲo*W'd bYg#YĮYR_4jإs׎}ՉM$K;yr2}%DM=S/ p(S # [eoZ ;\:̾#'O^OX3 /jbYgSd N2 4&iaK:ؼ2qȹES],}t&Ac[S,h4$oz`h2)&fsX#`qkD]cb6Ggcw-j5'5ˋ{O M.ʓw/ldaK C 4%YД'~oqrpWئɚk#m#jXAGCbUi~q^wKSx~lX{VnlYZMyx9O!i,ĕQ:y6/[@Ĝoc,9|~.3iy +[DRZs(73dsǾs8g |wKTȸG;#h[ˎ szyde'KܳMܝ^l0R"z\(j9=l[EwSe0sjpO]ar!Dёz*⚺h߹6d4\cyY.N#eeVZ\ ֚:[]\(r(١ <pRͻ^nƨ=m1Kf]d=[w-Lߝ/2!!4B0 kԚd,ܿ]M( _Cg0< LI}&ft6O.I_,!i~@#'X)%h1^m]{-&ՇgyE&& 38s\8xn[v׭޵ј#D\3WW>bU,f8_oXŶ hJRScW?Ž͕l\uPZ?}l+OoYN܎P(egΦ$a]oq,Su<.Lw`C燘ÎV[zq^{iNL<_12a!^gT#)]Ý=-4&(u8w}qrp q'4@fuj?>\f(yݫn#3ίy;KQa3 IĐH)nBs&(L:dh c2ؔW!uBh /1'^<~>wnP/60XCrKv#Uڞ&- Uݮ/,/BkGed)o|JT5 ~LmO9G@`FC +X-  APu#)!׋R I5v) H9̽l*os/+ʾ/c^)H7QUtoC/q4Ղϭ%tEXtdate:create2012-09-26T20:26:15+01:00%tEXtdate:modify2012-09-26T20:26:15+01:00zIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/vg.png0000664000175000017500000001373512625666430027156 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy}?kf> 7qD $hR Dɉ$JE*츜($D**+v%(Q "A2 )}µ]}sO/N dۯ{ni4M߆$rWSN9h+ZT@wm]kO`Fe15?/9qMS4!-\ķ~oKl#s7g6 acM2sb/|x^޻ /$6baOJ;lH?/)Hil;z4?eM&JA:Z:wϦ!~ ,nseƢ(@JhhX̆u גI$AJ-֮'R Kճf6A#`Q{;տ܉Iǣz 8γmؙL͘5?⣿2͞a )s۟y룛;';R7{̱cl>Wxs:t)ZPa8HI@C#55Z 켳- bT>8B_$[o] @%u+ Q4//X}m[; uj*ژJ-U]!,̹bF)SI!Nֶ-yϒxyBO=]ۏS[yᅓ:T; P7MtK{{ׅKYd&C#yQԦfzz#7Gxs;JUE>N8<`7!T4͇iE'Uuyd\+;C!4l޾erVv*CaqB_"|?JK[Es73Eu߹,@!V/D|=[+phil (jQhϓORHB^jUҲB N$Qdž ?D]dn)V(k L_sz!B$B"vՂ+^4߹^bY2CQjij&nb '|<((i@A8ǸܱmHT=J޽dy5sNATx"jLuFis&Xr@r~+oMAm圈fDLb( E!\c"s۩ 0Faw)r(R,;J5 O(PB5+My )sn5*{0YR P>sC(Q؄bPޮ7RS֮+WRWYI׋i3 D!Q:@dy/NŶm ̕'%ҲPD^3]X7!&L0WBJeVnb'XeGGQUUXpbXMaԀS$ϟ'!Ŀ~=N&MstólCS&Cc.PLO0N11^,rvXYyGkR)*{ȵ,_;;-4 E zu _ҝt,)Iwu1=p}1Sw=Hߵ nj,xB D9{[i0}CЗ,!v8w苀(J ,?Y*_FYi@C`6mMZa!9 fIrTĉ^R)kA, Gϒ|-RS@%A>I)fIRLl2aK|@ ڤ%"ҪR $}Y&K=+8=EMYVp+.GaQƯ> (戎<2hjkY!ȣG8u*3SUJNvv BQS H0)=$ugVo`{I4Hv%8Ob$b+l_qo.%м(P6"EUբL.EI2Hhn D*QRSO`ccq ̈,+b%7U.r qCp[c5L!m 5d?U]gP-_`n1&xC: vHPo1[uyO^&rOWÔIR!%ݪ;ľ1PJcX 3soFVqndS*2ac4/p;.-,\CUn\aK)RIW3lHW15FmJa6k[Vx6 ._{Y*F"G !RIao`TCҍ,|j2g\AU sdK-%aӠӗDVCf8aP,Xމ씂á\v5NCQnr#-WVRDR=xZ:ql%Z37Mcm,U[Qr}c:k߬"[=e13 r Xؕ]x.Ǖ궳V99C6i? ӗ;x S(E%IQ4(fUExye8ijRiK˯0]A,uMp+&gO]di&~&,&63eHU %'F6-Dqk}d,ǩl`)c:[un# g˕W~5 mVhƬ%b3e\'6Kh]ʊ ӤX2JTK)tՄ+ »rN Y뫅km` {~XaKHjD3ԯNqxrF5%>??z5O@4e1dyxq]8bՕR9{^)snvD2'XJIa_w+I%jZx,LSvc}ʻW*yZUYޒrmY +_^o Mq;.'S $P1MiKa.Siv܌PTGP[x ϝ,, UbV.sKS^%%&ikN4J3:eC}-J `5V@s̅rs ,mcNJom+㿂nl9l}+& Ik+jQ aXsެ.Ѯ[V"L(Žzz6ղmi'*և:~&bVs6tF:-Dfxwaq6QK@gf¹!:9\^KTXWyg.0Ya$TVH X2jEQX^rO,2E2%=EvLotQXhUNpatFbpΏ.Xhd^0 4Ua oƧI^yF5hABϞ5p0]4Rb YM7Zڏ`OMSUUEuM m3==̓o<Ϗ"j6? g{=|®WPa]8ĹdqyRJIj͆_=~FsN2!EQvM \%_?D*!oA/!+ ms*(כYZD2]!Acdp\p9Ҫ +!$ uzƐMFMFmjCw%~">v>n/K}Gذom##y+7V'ı <쬎߻TU>p?$ˮﱩQS?O<р|ʍ8әa\ݱ9~s!$-mLں{[w$O]Ao7 5Ĭ\=_b@ =i` df:?ŝѲȊ֐ʿ*6 ׶%wo>d hL:{6!bO.[yFQ|" &rO6lV hHE'TFl%lW׻r-yag7 _uB 6ǽa "6_1w>Bks*}/81>'F.i mn-w>KAW9s#ES|~?TE}`` # ׭P +)؜|S_ԑ ro-Ο4.L8xD&MKms E$v;7LjLOqgjvl t .^"C/H;ӌw 8ri+o)مpX!W~5o*>•W7eaWlx %9#YO%IÕ.6vXe| !C' cc dϣtJzzNXp;p%`ܡ@jǾyE&<)T24add3T vYR?e$ō&UBknj~z򮤳kQapt1$##d M sHѬr T<uH)D!oF9}v65ΕI6Šq7xu%`i$bǃ:M"ϖm&6tزƳnŝMuQ iϷ싦bYz3eI|>%KjM~xl$#&P4CLZuI1SnKU$4-a*uL_aN6UUNE 7*.BRY" PE2B")2S~y/Am;ޗLN^,7 oQ[.PY- TQoO "(0S @D!c4'Ps MfL^I@(|tglͻO<8^&]||gBCb˥\/tYsC/2$?M)'1 %tEXtdate:create2012-09-26T20:26:02+01:00%tEXtdate:modify2012-09-26T20:26:02+01:001jIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sd.png0000664000175000017500000000524412625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx_Uߙ-l Ԧh?^ i""/&@I QPZ J[ {ws>=svfݻn̙3ߜ;wwΝrVYYAN&rM㤙ɒBNM  B.1#_]G͆!dDqnlɹ^U|Mv,9>9WMMՙ[Oލ"a :\+׆9:dױȠFjsbSU̶f"OXGoǁf2Õr\ТJJi}֧~9v;X99&y m^ `j}Cf]%ʈHHc+]#<[AR"*\f%E}Q `Y++A_)p2+L)MQ5wo. ]\[UU:V݋d2"aD E.qhG 0 [l2( B0~[c hAΝW_'i&Qca"13!},(}p7agAӨ`U[ }n{X~; %lrJ}iZ\x>C~z=4S"^E.I &Vc7[%V[VP}Ecs2R>JIA.-u|]K̫6[ŋ %ҽt~.Ɩ܌p)p"m ྽[O0ݞ^j>XMKNXq/sq,<|NL*2<'ŮM+ʓ/pزn F7t+悘́b ' 3 #fۦmZ|pNY c*1 67f*IBKIdzȂUpyd#\>vR3@ĝwN^U(\z/~ڹlIW\R5oa.h H#6Û71.[ 1 u2Ija˺-b۴ r"k*ƘxA] 1ۦyVmknc;8|N8uM^0j6:55%zԩL6 ES8d;މn^r<ٻVPv\tXtV`ZVY$dA_p `x_/UօRS>g/e}+nɹp"xBo X^]=iúkUֲ@r .DqY#X2EʻXe:UXe2|KA <|@筒.^V`$s$Q!2j)*d/D_Z 1s]ZҀta?лXB.D_?uFs S5˺[0j+w>`p017 /<'f.֪Gt,R鷾~hI(yVh_,+k~V]x{M`l(KB8,-(-0I Y`/Lc!u+o}ކiC+|”;_r- =,׋$pRU-3 ;>AFrx&A1KQ ""n3(zXёm۾*z#i pfhaiffY`cCSg=.C×ЬKq%̑4k4oGQ0u! ɶ%rQi (7M H(;:HNHRM0xV?C R"ܫ evP,q_"6Kc9TYR:[֗?(<R7r-ٟŐr6%tEXtdate:create2012-09-26T20:26:41+01:00#Z%tEXtdate:modify2012-09-26T20:26:41+01:00R% IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sx.png0000664000175000017500000001016012625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{yg}kË^;y5T6nH*%)H-JiҺIZ5JP@ZA 1)!/Xzm}}|fܻ^H={f;a-B(2Ts 0Kn!ګQI_*/,jWZҰ+X!/ TzH@,<8B+xɳ:Y6*q]vCiג7 h+yזEVU @(o}R.J[^4\|n 2֦x” si07g`."S?p"ԺSw| ҟQ]HdÔy*;y:.@'oÕaS{nmX \2*F ֗YHi2 &QܼMfz/"L6q &b@ѥY[J2=d,t *I" TI@SH9 &N0te2bOOViF.ɶl?BFk9ʲ YNm8{Pk .0\[OH\f۳H(EWXb-c#XC94Mr]Ҳ >'O&!frƪ%<l#IJG y\\0'`ĚGp$ ӖLȎ7anϣ ݸ e'ٳ$OaٲEY]R[TrL-g\7 .c%8.(Z ӼyI/㤹u$ύ?̆+L9ٓV~O.EiM8{&ުTCʽqr"sVs-sWx1N 3Қ0g{Vqjå䮽NL mmukhz1.Ei2rHOB/Nz^+/נ=C A&w!N,^DS{lg:7Zz'aVZ=in&_MN&ʳ7b& v`Lhӊc,\Z&ykqe;(k ! /8AJX+ "ˊQ:2ctXɓ')HhZrc!/Rцo_f{J_T*AUm{gYYJq1/ kl]DENd' ;=Mz|{&Hl2rSSRKZ4tlk{Xn #Dcq+ x:!omyQ[ b:$$l/Tn,3nw21?J)DWz nC^I޶OMbhѣGf4773::J" H!tʤhrlǶ7m| ;vo0To:`Z(U0Eh޺ "#8nxo>pwΝ]MGjjΠtwws 7Eoo/2\;c_!Ϛ?Z-?%8 n.zg?IGY>xehpRFVNNy^ǭՆ(\QV6}*+W\f,ҫY H~z~r{ \(?suʃĭm ee(K{i|7 bje=i8I;gt0ZXjm#d+yɽrh6eKPY|,w}[r*' 7ln?IJ~NSL:o;+5ض&meMwbY<PJ.U#^Zc"h0+ d1c#?So *6`@5eo}+Q q$ &%l%TiqjwNqo؝T.XVlDwn %wOO~54Ak{z~E^8Ȁf-58צRT^Ni.yGǔm g 7DI9U#sZ#ke3MږX8ضmh2@ 7wɳ/Sy-N`U 󿅓WmqE" 8y<\Zk6?=!ۥVWl%СқsJkF'q =rr27==-LFb1,d,mӽַhaa .⼄F,5 ڱ0_7Oam #"gyl6':wiA/"(LO4!cð]8t˵T:k^օWTitnn|`fj8p2P"h΅@" `8 MScRT Lw5FNP)3EdCUaP蚂%(|hƺ<hY*[W,IQ؅j}RBsDNGfCEPD|!|@%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pf.png0000664000175000017500000000711212625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxklu;3;Kr&ezS(ڒXr֊D65bQAQj 4@?Eq8ځd/Y$K-(_"\.5sOrERRU`g̜9s=nm-:VYA `ڌ%YtMV\i-h[)P Jr%01.sjO@!^"kG^U2 kmknOvmY0J ,UXtU~ (sa{+@`y嵉Zkc~7@YѪv;Լv⛭jll$ a¢+Ctp,Z3}rw?~!lNq>XwTRHAOD995bG nJ"r Woy|UkW}]ا  ET6CmNqJC@4CL7\9Ds Q"^[ [Zk}x*A(Lj"@WKx/pvC |j8]֭;FȧtR:"6ݱjlOhTY{)vUv7>OZ+>pV=3)`/n)RxUUfN@U{>Uꌪ|\zq2|,R^eRėZE7 /ECڬ{L ʴPǀtt^20>]q_K{bQR9Y>x֯454`lLXREo])e]ex"{+T.'"&K#ʏ'кc5];]Kmc(EKMhHnarx1f=kV@Y"8ŋYEkq]p]4^4f[7;3_}v!'?$Գ{7ѳCXkv1R՟,މ\SHmܡH:l9ߵ5vIN|s_ hP:dɼM'^0F 5r]"0:>: (V#>)zz d͡,Wk\X@k8 }tbvBQh?æsƑr#Ts1:z0;{kr)rR|"{~{UJ~m`}gJjCv;DqRNT;_,'XTT\ =3X:LH~̦ulވcr=ž8'^)$5U3@ѸDZ|PPv=&&бndz}gZW{=3oǞMZ B۟FO0٣/} c,:C|`nuĴюAkwcFH!KhӐ'ߑ~uwgɝH 8ܩs hyT3? 66Fq T$'c4zlGAh'?[ ڶIQHW4K-czdKN1y4kTA^k7݄ku's`QT$R0Z~|`z H&l&8xwG¨NщT{S>@e7q20Qt)jf)s9S)reY$69L { kW_nVq@)Pc7a L$Pn:#LɦnNMiVm(sVzwB;]ϣB[ Jqwh\ ë=4;;`b)66&y74M!c|t\N'ym_l/v [;l3;7gHL1c20g\룼Vav}ȗF;kZ͌|0'qC_nMkbL'2$<-(*_g9z59}Dlظ{58Ϭ敇bẊ4غv.ĺVW#Slh&h5l>ß99{3;C(یz8|m[D?A6JQ"^5ݝnj^:ɱˤZpN~jǗl/mP;80һ!uq >黌2?vLcE+z+/a;ks+#I>6ʿ_D.i{`Z0IkONGKr\)Z- ΰyGhSJ2ȢEx8шdhdt2ŧ3ÛnH󲩫z~$_eݪXŃ澞v|+q]!V/RAl`M{3i#>hyc\W k;Vll)"L_t$A)Ķ"Ҷ񻽤3.3igIz7+d$Z1Ddu__r'u2zJUU~fqVm%EVWW_a:jfs9.ϧH|Υa2ME0p*9BaO*Ғʨrǫ쨁Qk@Vd\-  fI Lr BEbm5UTW,TWRT-H,9gJ3D`t ^ @#hQh/QA yۊU5g+ DC>ȵ_Фu=e)2sPY*hh˩oRߢ4\ej 'ޒ s ja[-]nepWQhҾY)riA2"*_\@^cU;ԏ6!#!-HE**9=2!ҤG),U~%E@i/Z{+ꬵW%E; YRs2.nDhQl]ֿV@!-0.Jٴs*+tHϴ"۔n-(f<Ց7@@ KY( %M@'2] =m!KA niJk29ghd.!` JM CV Fl[ b>]R&IIDATxytEf#$f!$ QM@t38GFPCFTDgmVQQDT# (,"j@Fuo$Y_}W_UuC! >ٜL{PliJ3. aK3q&!ВZL^ܹcnࢋӦ X^^fqBYwx | %ʨԝߢ!xi0өS+2/:viiϛٶ(tJi2Һt)㾔RF0jB{zex4p޽˲?NJđ#?⮻MNN4%QJIrU x@JI܅hZpvt]}m!!h8~K˖mBDm ^^ = Tə~!Zܵ~gML:'@Ξ YY?Ǘy/QU!"U zV$%ss9׊4&NȀA-ϚL:^}c_H%;;-?y?D;gq@!mVO ַ\PW7Ig<3;ya< xfD2/t Ѻ@G:~%!A]q!6FEy>HB\ yBh?̝?E0q<$!δ_fӦ!uD| 4 u$ p̘q#›oƒ'8`{`P]e'vo95*^>׹*$Mzia&`_% ᦛ_32bbj|9̝ h)՗vau<{pp6TĉM71g2D=AUo}<ˆf90XکKB~>.}g^ /ﴛg܍wKf^6hnPH>CjEJj <UU]&!C0x0\v[=ae]Rؑ3*٫D櫪®]X[~gm~*;,/+HfXL.hEFz:^Tq0O<ݺǃt+TT@M \y~(mY;08c I5YmاiɌ_})_@FfK +"RUEAU/=p<9koz%"p>#]$,!/_v6\>|g]44VÝv3gnXnh *.5%9f R:HٗUY=t\KBv>s97R%$HMM%@]7%Ъ`rYv"nn`8#}{o9'^Fި1x2Ya%8)1Upl' :fVyh-P( k H"~%u{dh5*0 \ݵS G㕤-9fQ+WM$'c5,aHbٟ+MmvInN 2dz9+ EIGQS9*% z gjaBX ́]*))PP%%x;c<7g.|F 3>4]C|UM$vmE*q(d4uMxif6Wg!CjjFM;ɓI:{Ǹq}PUA  z8 躑p܉.W.盝o+ 11 Bh&mNnrJf\Byymx.cTX`na>̛̻7j.BÄb<[pL~=-ZxmNg 9E%Q:0V"ͣoH樲p֨(;z2w0Ϋ? +.c~Nh foM4} >_LBQy絨9QSCUF2-!򪶤$klz0\,u&7-\ZYSUwsY]M_#xfJl883@l6|A̹G5PTx1@4Ua֬TU5""q"xTtF)a̍+unQ]!5SFJE:W.!@4ر0kp2N q# Gxxtћ F϶*㞹b'>_ qSP1ci*<:E0 ZLpej=]lxȞܝje۩w/y~?,; Osm\tcTU0k02Nq`7f,CCj,cԪWv3iK|ޥ7<{3 (k_ۯbade QYYK/F${;9LuUCҀpgTR8yL| 6 IRرyxMqyϽ[aBg<&2’'17g\ Z=|!_<#W(FG|`嵨'Or8t!nUSy CS?>unl9chu#=˙34'$;z1eu8>I.ʬyw1 y&z\$NcBZ{%§"2?ţ)L eeğ͘7rNk={Gډs'E^7RSټ0'%ŃǣRU݀"yy-4N&@cבҏg D}/_U_(4@ @ K`U!C 0 4.3E TT!%9|j%ro@J4 0 `p4\T8 D~ٗ,aYU\9vy^!8\Nmblg#M2Atࢻ'lb&sS^" LZFg!YuipR7܍R^JR(C׽iR~v5 IdWBzzL?T(̿e2 zMY TLb6t?d TU0%BEIDHA)!##.ef ^ӧb$0hh<1 < oMY@:οu"؍S]O'Sax†)Ua^= fP!R*lOטR9l=  bB:iL¿|w}`ia "/epЉX%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/al.png0000664000175000017500000001105612625666430027130 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>lIDATxy%W]?۽ooޛd&CHdQX+l%P0`@ -R Xh)b e TkHC ! 3̛ݥs~}7`I̩}{醇ptCN.0cf !ht_ ?k 11H~䛔CwJcQ{j_jqRUivN@ M 6B[j?Np% c bFzA*[:<6U鱂#J:Gt*94u[Iz4[?98$ ̮߸0^nI!1` HijEӘ2bFs&o\<Piy q^m}N<3d +)Sɫp8P)"^l. nzLy\YDi>\c`S/,+'-bts*OSD8m}Ur3Ngm,z"Ʌ3xդ)' xҪr)튑kyK^'c2 'bZ@ōt@C} BS_/ÀS:>zϝѥà2ԡx ô ;Uo̔AOُ ZrbkyXVx)^Kl2aELEJ#\1T( ¥ׂt`4|&64ޔ `MʌdU599е)_3D  Q!q*߰cpupM'!$Qk<B:62u4gCo{ vA}??=BK-7u\|x3PqOl'BZps/'\+2' ܪ55qf=MSn|;`!ng|% tkqOfNyf‘LܟZy+!QbN,pSvDw~i<Z>wo$|h3>7,_ GBSbu|#ܗdl:勃ܓXnd$ -#P?X ϰxtl[z))m(y?St82MJ,_觼TBN[݄SB}K;!tSݔf5"x1b7|q0w -6 S内C J'ɢvN3`&҂qr;bB f9K 2N/gM-o_vfT"xL`xJupy`8zlyF-HeQZթ `O)%S# l }S/,GS;SrÉ~:se葩tO>p:>Mtg[xT {=t gnDqH#-x¡q 㻩ps|J*4/6R 8\ ϘkqUh9G[C3pT+ &͸4"|:rOddžyJ^8h|$7gq~;eŠ' #|5VHdV:~!΃v 9G…F~>ݔOvS:Fxt1e\!&OY>۳$7ٻ +} C#TfxBc|)2cu0V<@TGLZNe4 zep|<#B#aŽ)]1{%p:. ¸Ⱦ3; 3؊9T3&U<o:̱`%#\ uÍƮU<}-8Ru\yN-}"J)r C@V8>(WjgUA81X"r)󃌎1,K3n*0M̛a>\Nd}%3G[ިZVA H.+LiO0 isgZe$n\A`^`Q+LWw"A <89±͘ ش +c]-U(<~z2M3?,]Nf6c[#}ZB>یI]J,*$*{ϸ2k u\S[>aaU%63;V<~w}#X7/<_-޳RSWY+B5x'b'⽛1F>7o[ osuhx/×)s/sDž+B"OXƀ|7񚥈(v6f 5,VX{ ̕RpJs~_x]5mmqpW߮ ϟcl$NmP5*p{?\gTG>/DܸS"Q[_fN^g5)W$ŕv AJX9fGl>^n+"vxZǷbB(iG}*>S>pm=^ډxRW|l+ayB']5hc۴Tcre8)' +y}#E]c:L?c@>*4\zO9fqBm 2Bd!-5 qm3YTgZWHՁ{XJbZwtiI˞Ő;Z~v! 6b12,89|34&Iuv6#Lq̾!0俢>Oۊy݉.)}V `>Pqk7ߔRWfm+;!{L> 2>K#[1ڣ ˦*7lS&Mxӹ嬑 I7rUK3rG)&OSg,{é ʜڍy ͫ9)tӉ<.Sqj+C;%#|'̣<.J1aWDާáp$!AXUyg#m~|na:ey / xBeb޽kz,X*jҦ4{YTV^>V:^#y8  { OxB#]uPl)1|+N-ϟ#v<y߰?&L4 ?7eN\-})G?iQh*WZ\YO32rolyfc{#&**5˞@88/d\yCK:s7wS.!sM+٤GՙJn+I(E"YTy/[ `K#wc'\g#F|iq(xJ*xW/E,{y_/\Xd2<ݔ>rx2۴S9țܤ9LYYa^a/d*>({+mڀuy@`xRuX2bd4Nq[h;MWfa4ȁNY X{ׯyZM(uIZGUgֶؤ(ݩ!k (^»s+1HY{ig3g+X[6%%x"8OQqxN5Zl8oo%ʟڭ|zӳdS%RDow@i]#9㺢yp6s"$ɛ h#uOzGfUƪ}yw&_oyNS02Us$o,J2p]t,w_l, |eV!ug}*S9O"ȾgܙE_lGUZFovJ=s+of(= %كL (|+p]iGw97uv3PNɭ͛_a !d <}F@ i 9!^MG{q&=]x~Ty8$-@N'3>6CJNݵԐϝBm` 6 2 C59Z!}}]JZc뉔p;& sg=߰ Ж AdC߇ȽF%tEXtdate:create2012-09-26T20:26:22+01:00ݎ%tEXtdate:modify2012-09-26T20:26:22+01:006IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tm.png0000664000175000017500000001075512625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>+IDATxُuV>;g8$gJj(a[XRN A"?py!/~C 1l9X6۔MфmR\e8^uOga EU;=U|(ʇWtAt'6 `Bmf罟 Z{$qפ_pċ~*֮[/Qf0 F55Qզ}$"M{ҧ[Z_%y~/GvC\o?سD #25N&>W2S[FƅB\BkJ>@C:I_ ߭xUV2e{7=*5 @#8* _e5ی+S%=H097Od=`o`}u1XXn HiaceaZ5mndGK9ZXg_bߐ[KW!Fv%>3 ߡgr'P)0>7B> 21VA+;ʸVu |s=މ2N&=rPYb*N7x֒dY^((;v;Jys[O(V-ߥ( %<"GO~q>sd5LN xM0882"I9\q5 0ġ73,u6ơ<m,i??~ϦY qHb-UψUY˯!"+8Eū02k~t_1c0Rj1I!s$kj¬@C}q'qage,2<3_Ӝ>uʷWp?Ji!5^yC=<zU\5pS;yv zي>ܷ|^-&JH԰i:Sq8}BY߀}A1+83|:~X !6 >&TDpc[J'3>͙̭}k-Cū "Ky͆?Hغ֙E0a\Qk%\qĸPAO(@ *dRFMq 8 xFUbij',yћM1A$3 d80xUҔh4@G jiU]OSt4ᆔ MB~iǠ iP',Xs͗7HHӠ|sv,do#M*nw^d{~&a>E@$O yk(OWP_>Qݧc5w[sw߷>cCc|/:.o^W1~ Y!se ;Ġx($au, Uw(݁5iШetRXgn.[p{ 9;y/y˷Ε4Lj$&HevVسtЯB9+>\4>CNN/AG '҈V,Q "jq<$X!L± W~^uV6WI€2>@{ͱB,j"An(`lZBiDA%K833_~\}W)W1ۚ]8B݉oLPq))#BrLA)AePllC3 $CC<0]Yc|Dlт@fDQl5{376F= $T|hDʖCv2 i,4oAI 2|3CP>#q+ Rb! ZJˠے`#;vMDy$N5b 2(.ȍ;]2~ApAD/JR`5fE%tn'T_+XQ&tfy %Qb9G~Ga$JB\QK?Z2ݝM_p+:"hkXyez%e9e`r38h wʩ*WS\'l9/jkM,`Pa1z DGM\mHLtMqRZocEc.5VtMfkuʡPXQlUY(w-gJi~'9}*͹OdƔe/Ců4۬͝nm>I_7nX(}ى/ZUD&oFo' v.gצ@rAuea<(,AC1G!;~|ޡrSS{GDj7"y@ttЍC՜B.?W{S |j ~„[Y`[nL8ȶm"pwzJ)`^~hIU-"S  %\XT&Hvir4hrR}ZX(Br,܊[VJ\<,8NAe/f7ֆ60XӐGiw0>֕߹$|}<2 eUąԐR- dPfoEƳNOvoqT$ !Te>F BfY*)t` %=cjotPMZb%WT T2{y m w#QInEpK F+OAG(Uo3p7_Ƙ9J7CX}Ci͐VPJcZэ9]r@`_soyW)M9q*0CFAqPɃ&άLQ(53'?wܥ.'U< M04Ћ_,r/#L2I2}?QQRmohGз-p;X3-wF8,~C?DCQY?G?jQ&"Ҕ݋Nρ#YW 8Vng&/(MO%I=u0w޻{߹b|t \;txݘTK2o;m;LQB&9JMO!/G)ܼ?O a%ɀklY.xv^(X) @<6`i9+n*zU ,SV ǔl;m1NK*m ݧ)2, 7WTS'$J(J.kR[h7/m`\ղЁkpi| f/Ѽ&0$,=<<J={ѷ-xu=dd!pH|bxE+Vb uؘm q?y+nL4]g u֝\u *+Vt2WۼR[/9p UBIĊ:SpsJ7,P"ba+`AI?wH*ur_3ZL'NL/ SJ:`hqq>-ٙbxi`XYCfs?0Rۆ^W뼏p1xuVr*,+bz+P@$u+@Yá-tP[tM$"Ķv@JZmvgB}ax}u[:;C:|~PE;|~(_b%'s%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/kp.png0000664000175000017500000000722612625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx{G?UٵuuBl'G,8 .)%BH)H{aaNXBBHABĊJc{\kfwggTT̚ӴU5>Oߩ@F=HNL1hʨSQ< ;+߿X١E!B 9#/:ShkCny ufYZkZ#P+;S~karm,pֻ<~gJn(!`"I6pk08 }tT fE %@FZGZ\i6_r]yUzkPredtl?>W({epDT E!xB5/)$ 1։5 e5JiyE k/>JRYiMK)kA&RRH1A\1Оdb!S`ϋf5 `u(R(PS>W©V)oF(nق38ji="k^y3406~/a*\֐tPذчf#0kn>Q+α+<ʹsN47Z_o#]f/U￟!+gΰtD-/#+6uWh:K/YM41<s2lJ6=!ΜQ_*ۻ9?;K㭷h>?A&xD)L}Vf:. -&F0s'~T¡Chgg=ڵ}{y!tص$k9霉9`uQ1]{27cqAZ\̳'Yz㍰oP#B!Q*ᎎ2Cv^W4k"3Ȝi-u<0Tb˞Ǎ_d楗PVʈj5,%GysxO;ڳ=Ѻp4[Orz~ҚnΪ4d:ZV*&'C\eƾ4p{ߣu2q ZYaԩq'Tv ni??SM,-g*@lkWQ,y[Dݻztʒ̹q#5ƿ%j}P#RmMƵn&v14'95 w E Î8CCɮJbhmO=۴Z]VW*l <205ji ozݰMO>I7<(L# ff0ɂ)T3uy&7`J--8y2{eAhϣ~XGԼpʕJu>uW652Xx0հ«W)p{R 55e[u8rg錋- <"ݔvD!9şg(locv8'>Bмx}kט=x<S&ܭj}`;rޛ}K'N|${0P 3{7;~6rW^Ivc$wihh|ys2R2_p?l]c 7ٱ?AA^!ߗKL8{3Y-\J$Ifrɥ^e@)3oGV~uYOS6ǎφIxT2)ۺY}@iW%tzE >WxdEыJ[=ٟ+OMGYsye=쩁y3J]3TL&Kopk̳;6L k֏[Ԗ:W9ˆ g 3_Qo>͛hÿqo~CQ׭qvn$[A3u7tKTgʉ6FejNadT+P@G F-S?c\c=_ :(6 Āe+ɚ,<pfqSZZ*Qt]GsJ_֚:5-ߧlBF?iul򗙜dtt[X!#=/2oנ<ǕWW ɫ t t:3{ޙ{|o!Z9mB'*UW| 2B/]f[VzYhT"VA<Z< 2:~OiE#J%4B TsQO` 4Ac=0Lk[ 8Hu`nD-cCM(cPu#PX>w(4" jbCGB}tMue]n)B_֤6ILJq+b+ #w9"|9O?„kH %tEXtdate:create2012-09-26T20:26:22+01:00ݎ%tEXtdate:modify2012-09-26T20:26:22+01:006IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gd.png0000664000175000017500000000744112625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>_IDATx{%Y_w;3$&T 1D"#UVYV0PZ)c)ba"FDM)D1BLD^)G@;ݝٙ|qN>ݷo:}Owwpڝvi+ki3/,ݫ%Ќf\x!2@co }<2n " jߖ+ :On-Ь|uE:@CRAA⊵fE 1;yxVm:Dm\.>wmՕmQ3q|b^h}јѸz<.jmh4 X '"0 ⊌ v[ށb #SR>]ViEUc Qd^nϝ#[E(dbhm, =CcdfAнO 3 LfUmJ¹w  3.W8Wm  ]L3&EᆅHʷ"'5heawGEd`Ch{EC/|0%<} {vGXr= [aY)[@QuH :"ïc=TTn9o6j m@&'/+ v?"@Ŧ(v3HzVgXSh<}S-`ɔXY#peՍ@(LoQ3" .#|5pO5|2 V3力oDž=I'\XpA.n:hd2||kcq,{{l z5)hYS^`):p_v*CoٶD $\uPxx9USU,B148#(/8VF}c 5S̪Nk~3 jKa.kv*9Nt) /‚nfv0ܱcẝ.50~Vw7%e;>lXQ7wh׏ޡ s2]L7] Q&w2@4UAt'A鶢;/k;-<2u?J-5}j.S9P8a~~~Dth`D]6qJaX VWuWة5L̗Ju+`U(lv _RSyesCŨf-4 3 -—N'gTz4LQj(zo+jx1j>)O0VVõ@wxY= /lVTK &Gb=X 2p›.w+%6+W)z/*<ؤ_ 3b)'3CnLM뛍)(gaKx0VHAVҕ3+lK~ ?W%SyXxQ"&o}8kuJfcz`IWB8:3ml/R~o;󀇒=pq1pdʪ7--Tׄ:ӆUX*GҥaIl%Ir@wHIp\W&F8n٥\wHk!}Z^Oؚ:'kָfVT4'6\!-@]MyR—؆gΡgTBq}vmGH(c3hҫA&`ٍȃ9ώÎ\Bh}*5AT`|l+N sqFF3&& pv 3tH LJ/|غ- D25;C-O~k.2!fۊ_;϶m =C;*\}m`&!VoN*Uj|lioĮ{{mRH=hd(W$ΘX*5=|qrfQ6]oG1%R޹IsúB'i-{Fuf#Ezks=_ib̆oY{vC9b~AQβlπsݳHj \d>m\nNAk";ڬ}t }yߘ XnM܌@쓒REGK>0ac*/U b Tjn䐲u-z_t62w 54G_,~J`uQgIh`]BH N2#%݌%9뀙yvyGQN1`?f1v;z5]Hf*Mp\Mawɘr SS^R]E/iJjqyN`/A{t&; ۂ$A;|s% q0!^*xܽxxеnqIc`e"s ` zոN!} oNG} xW*|ozLo \&UxEuPdI8ji` D>Icd.Evup` 3.)[<,  0';)9pX0MYS7G[fYZ!VkY2,bsJs?&ϓH~GDpF㿾{jCT{ŞRóv BەVBu]`m`g Wm8⮉QZ @ǚqiHCƼ҉Oq"8>"n`:!Xq{Xw[a $[jWWKhk<F D;tMZ!.W3X[B[%=l%ҤdcHkUW4-Ձ!%תӒiJ{jt%tEXtdate:create2012-09-26T20:26:39+01:00p%tEXtdate:modify2012-09-26T20:26:39+01:00k-bsIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sn.png0000664000175000017500000000662512625666430027162 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx˯%Y_uǝ;Ċq N`!$+b]DĊD$!$l`-#y}x=XtWwUsQW߫~_os? E9I `:uf:['v ֏N&'qs"fo(3RgPJM$d"XVX Ldkc !\" )) Li[omS@&9KsVe(5e]D䆥,%*<#vE1Gdb=c0 uzj"2](`D0yhGf8< `Q־,TFǔLԇYO: QH"8Pֻd`NS^-Hc5jn;_j@H':9p0&5@ dPdO aZz+ָД $|fh͇ij g}V2Bi`YWya UqRu)pҜK+aZq2) ]EG}6vi&%l'ՁSS#qGc2+y^T/oM0 έ-N|79aFϸ~1붒ǻ邵רqc۴lϫCڦͿbA,iƛIoV#tl0\7|;'ٟL3SD{9WUSս?3|tzSb0Δ_!g8k^Jz@nF=x*(;jON\x`߼1uܼN&^:mș{k9j8[%7_~z7*1~i%iWO=<#o)~mhUk t1 ӘD+(lO={=?珿u[H UpbdԲ2:2-ׅ8<и%/Ѵ&Zmm6ssOjʙԴLQEzeD|`Wj)6.FQbl>H4&`|'⭼ 5XRYn{}43a@Nx/ =8AF#l'z , [q^4M\k>XKc RyVnr6zCw^|cʲ#O»<8-o[oc^v'?2/o~ۓ7G~#'9"6i++O}ݫu49a]Ջpgj૗'kW~bIޞsDW'/oKS>aZ`/}I&) @?AV%>1F,GVe ΂/`!]ߨtrm:zG*6B0k9׺ސ>`لay#sof\Af!Z 4jajcRU]g՛' $$4P4,0NN4wHrѶdbuLѱ6L; '.MBAYIB@4m +"\bN0h h?J .7btvl[tD40~~źH©;IRW^=&|\x5܍'Udj[]Qx;.ou9:>$E7@ߗQX눋 J4<ɯ>k1]χ8,qX}br.3ލԽ.5gAm5b:N\my}運8}WUZj=p7߂nPo5If[/nS_'pSTQH@}{Q︷D%eiY,TKBKX#!z,U;ק1Zl*du;K_Xt 7`j]dAepQՙ풪ˉӆ>M'U}#ms=qjrSLQOu@ILJ!pD 8)kxG"{ou/4忐g_mr' hayXr.,'ݠ7~l.0{9Js]c;Y  eoM>sOrs|xy|2ug,}H!Yvx1_yy6`oV>6L}C0dYOۣG&T_Xy̲q Af;tRź]k߾qtzYW5ȹ10jտ¯u>H?wsnEq׀Rn uLsQcWsX "] ϽoĦT!KT[}>j+`w@ ~XKA6Wn C%tEXtdate:create2012-09-26T20:26:38+01:00{%tEXtdate:modify2012-09-26T20:26:38+01:00ZiIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/id.png0000664000175000017500000000371412625666430027132 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxo5ǿϞlۤmԔTTqā+GTBEr*Q $@HL3?>ى~{# YBr|PynSfɡ)-OeV.@^:'^zF<]"e2~;W̬eck2`UJ~<~k$&Ѷ_-}&'}u`IE~nl6olMZ +z5#}@$fR0A'_LJ dg(gH~K>@nN-/@Q(47VXׅ1T:@(dy~\oy?̽콲J2 :y dCŹ#5ϱ:L5JJ `!":I\S.}u+K(Β|:%B'! at@3[L "JNO>*U :tDt+~8jOI"!R@e- 9*3T7K}<% $XW(2 ~,Fƴkgy=z}tZ>nƦҘk F߬QlFڀb` YkN7KeO*͌b:i/pu0mAOH_k!]sIh| y`Q]/'6 @ɏ#3m$N n]gͬO\Yk6U~Xʳ-[_zژPsOUu70Lb9*^K-!c?9>tݟ`sASna$dR}MZkhn buIImj8CW$ƺAiS _1 (7jaؙ곆2[qjj?恺 N1 6J_Ů(60sa Uci0PzˬU2>2L؈̔P?&+mcx[i!Z>3vHu10P6gHk\oS3 [ P)c,$r7k>.Uۋ/h{~'_5#}YgVYT л<.?߅/%  ^ZFë'O6RI0Æ#(Y`gׯK),,ET$G[[[[$y` _# A<NzZg hA:eh8lH\#u3>,ǝHr!Ì#$!Fu==B:EHIDATxyl}?ofo(Q$u:(-R$Gqĩ؉]$i4É&@ڤA4HvƵdD|H!-ry-;wIJVfg̛xy?RBi$&rN}K4rN7g4P2E~+R!! @nl×(L(Id.I .M‖ L#pp8v8](q.CQ nUXMpS8*/Zth M zI gP𾮃ђ-TEu"L{Mh2b{R2m?@f2 l76Mr{R'p*N`IZZ GÜm!.5}|uN~e.^DQҍ'n( ːפɼe~`0KKk щQvr˄Ǧ8|y);Iwl_bjy&Ǚvsu7'yi\I4//?wONؼt39O+=AYюc΋8';ܪ! 6;#.Y(mh$l\b$0ۈ4gJPF;n);q-F*c]=3\X]oqxh~LJ;@x#[\;&225Kհ_FULePߤYhs8+sq2tlT)}lqҙhjibmZ>aE~rʅܹe4pq{X^PIIq%rn3pW-\Gz?G.ۋuKJ?Af{\ſb[}pP]f*),[d.cS1VNE~ˊ:ƺ5Lv7TF}n6o{@~>:DFƉہ7?@NN"޳'v]#ԕQ]XCs"evOZۧiA"cxX_[O@#+I=)aKA]RTYRq4=4bSxS St_nc$@T:M5sx.2V8dB8NU76f( LK2{ ,DN&Е>[P^&?"K~ ;~OdL;j?W\O۱Pr^sz-[DŲUܲ^wGx@XôggHzJT ]h堰\!Ø䏰ͩ.)wv5WhG0PB0'Hd^Z Eeˈ t$K^\'%G|SRYy %eNYp:Aa`(jdMgffH l2aI bUe L u)) 9sǏH,g?C0'hޯ&0Vzf#~DU4nJriص@X4DJ p#VtbQY;xi^:4@J%֧9S5t u .'uws V[hau]G4 nRBj)*DC.ze>1.DR#HcU*{vhTX,躎P=>\aJ%D=32)veOZc2 % dT-Q&#OOJBq4]9wp|LLNp.B]P-Y+P#)lb;C<%iPyy!RkBbd)e/"2 _El rs)F#av9sMl/)cÒ xE.f\^K%4c2-xC=WT2u|HOR'5 ΋?g;/;yXԲEʙcY JYVM&w8PBs)+u^Ʀ&pNgxix#gC0x3A+Kɚ&wZrn@qJj}ތb[7rk|j-IKvpt[XKiV2}U_\t(J5Of6#I'R K&.d۵dMazK,m[JSv|E7<ͣO=\-kj+C4Wᶚ(v6?Mkahe01V)bV2jzUdҷTkR5u.qʚ@L&#Q#}|NZO 15!%Ƣ"!C,`A zfHt"q2˚>L˗} TtHKGLLk*-䌶ci5u>RNz?YZc_ -﮿eNַW ಉV*TNŅ"Tc!퉋l\Uf{o鳒T[i5bz6IdW ?K!cybŚYRtsaA`do[WX.5R%׻W)k#^2eّ/C+0#er3\oNB^>L3L Ԁy);N9GݚKu6k.\pIϑ!EͪKX'Y>8td?"a5)|>x|_rT3!pO$W\9s^D=cC;pPUuq׻"_Q*{C]a]:iZ' @N[A xM-&L{ 5q`cCs@bP7d"Nb!$ ' k y0BAya`1l] iR[Ƴ1I{}BͲ`Ȳe~c~%tEXtdate:create2012-09-26T20:26:06+01:00b%tEXtdate:modify2012-09-26T20:26:06+01:00yIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cz.png0000664000175000017500000000521512625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxKU=;;YFfV,$Xy( (܌e ȯ\r)'[eCadbb v`p,lcawg*ޮY/L(ԏn$nѵtDNqNNN4eSy+,pR%?zt_/_Tn4| h QkM%m; cc#o֧)|DUr_c yNoǿ`G6DF)E^W׮} v9{F2z@K},'_J^ҥ Y\}}J8[V]ebbKԧ'NuڵnxUBLJzˏ@E!z{+>,3>>qe T*Q.crTjY8J)7ĉ/Bx@ t}R44>'N|Rb`~K!:&Gj<ЇG.)7Y RDܹ۶gJRJǡT*I_{ AlH0K,* GݜS{o}|{NS$鐊rJH^/8NW^9Ȗ-Yi~1yzcyw>5@dR%=#BDmEĝr֗9K1_#lRgALCNj݄W,wÍ_o->a+iḧ́R\W`sŊMEl0!Ze֋,gK;lVip9lD$D2۳AGP.teWZ {3āg^>.@quC REͯ Dt \WC!>!۠ճ3+ "i޴C40]iTXEz~j(-!irR gfw!lsi@i2ZWϼ NIEk_Uh/a[CrH,S* fo'*m@̝ȭB}`Au ٍkmW- TaVP D렑0Oilb'`UaxcU62L?C76kD:x]YJ3!E[BqF1JT@<(\\p\7="s/<&7:'Q63 0]wF !2<56!V2뚧ylU"o[)2E=:OTG PC!+XRVpmzP`rGXa؎,4Rc3d:_s~z"lph?5DLIWNgУ=c'r̷\.Y"^AE| 7 `|T_P*gTF.3}JuθD6,2f+4Js'@u< DPhd7k/jd%hR>,jlbUz^\\!oy0gkUp Oƺ=0n\s?!0E%ES\ڳKFJ6uBCquegc*3zWERsm/XOͥ ;J E+)X1L[QУOU^F!:p(*\DkkhSxJ_-w}qKTJwуO*ξq_=EoE)puϓ5#T?*f"vfR'9K1=2]l(,vg➞Gb1nŸϱ=E-@PgOM}<enBt0/!ZXY HIDATxM%E}߻y3o` ~FMD](ĸsCѰЍAucܩ 05D Ȁ 3̛a㢺{xb~x27eYQ!>DJUV X UC1-|g㥵׀ dqu8;W]&gFEyUI<,m*syeW_[y$okg3-kÎ{`..kD9pN{ejS̀nKai\N1  F )Vp+)7~V3GW9?-.Z344U17 a:tV`Ͼ|ieyjݸnh{srQ굒GynYl⭾UMنfT]4hPSG ?Yz[0(wPJ)ںhSm)!@>3 E˥UcK;PaNDXO)Ry߆.;2Hl,ˠD vo*2\ZY21\OQ>c &^F[D& 7>@„չL |X7^, +e@ecw@I@aiuO5V(rߋʸ.z}F^r ,1cfs+]e~cǨg;Nmu{ݹՋ|aq\9'Y)^=]3jfĦKuRAm)ػaVA&":NaߺD+q^1tczjcq ^TM+J^BYGeylV&p\DU4^x-A֞N@gfc4Rؿ~yJW0fMNV r ږXavvN>`v{Е L pgLnj[^f`\(F'ƲxH[5xޤ/hKdajY7C`ZA&,2/_q Kt6o0GT@khP֬t$mx>ۅ1UK+XG=v k0^VFp|Z{lI+윍CkVMӇpk,h `cȍ2F(׽68hm8rex%R1Q4ϯn??l™7^ /RrL}>:{vAM$Jn ߼vT0\ꁶwn5o7 ,2S85o.*;<{4(ɤ5:`Ƹzz]`#ʸy׫^!#oޙk5|>حC d_w S 8wU_Q^'.)7^m*dW ߻Mxb= 7^rͬaH3dɄ:[ߒs5͒\!|E/Y ,J#6cٱѫ:}o;ȶ1mա7po1o3^z|6'7mgʁsoFVGp|"< wZnN \juhX&yEcxJxJ!WkY֭ ΅3_&uw{~S cRp/=FZcwMLQFV-tBK$x3߀Q)epRR6ECU@MtLe)ы<+.CGGh o y+Fi`(l/ R̳ ϸg8䩦=݇`cZdl}2x9Tn 7t[(P-O:FϚWX׳^~ ok}+YZx kY*֡SwT8Eyj?q`Q_@v<)l]TJqS `eeƯv,s3tF |Gqh6W3 KIDATx]Wk9sܛZZ-( Z-*REDDo BX-ZJԶ"jն4im~sfaqMNDca=3kke,\{/In10NeANkmB`zOt޸t """d6/dׂ6B5Y{սaZjV^#lO╿րHꪓ;@+n}q>7}1A -7630V1F C/Tk-8å#6o? &f뇣ٻ3}2?O90x^ZensBSԧuYEբU([>;Z_Q_MU3q߲C%N8!+W5XnY4eZ\2؇oWf(78]fm3"t#0N!:ݭ5a~,r⹩SMX[ aNdqQ~l lj Sff怕֖ey'h~b1 ,B E!4b'?]nCڽIT+M[W'\N=`&ƫ)tlzsqǡ<+Ԁ 9 >S˛KIEω :(Y8J%Wg;!D9VdX xZ͆t}G]"8:IOd;ϋ涒7K#rM=a +b%#ڵ;I3&uZ?<*CM8Ĝ;883n7Nwt1&*fpbN}v .')Ⅼy(ւUE$lH^e3KYF12,qCVN`:1Q &K1"u: uGc;=afCƀ|β6h2}X>+M,sw7bP",2`H(d#8+Dŋy1}훹>W.vM^Y:>xs<hjQb$KbMDmI7 k"ώnrB⺆Ջ]>{6n}<;Jv.xpNqdL$`րn6iP qjP TZi|f7um۹>_Ͼ4(&)n[}8`OtA_$L~y߆ 1I}|=7=wẫ{$IBN\`+.pqP6X_z^4Zع5˟Z+{ϕ7]3˗\daZayK;Ջ!m alD0Oܲ;8w޷miW+FYc`,7XbG;޳к0DF- X|/ >xa.NRRk!Iw\?5^8z;[ rYG\p1Z0;NbKyMr5Rr}u}"3d+YRwZevt}cn1nE0 ߓ] 5ezDv} 0ٕTgV P (O1Rňiv"ߐl(qej^aF&ЩXj8$0O.Ñ'vdL)QU ~g`;3m9zr(8Z@l% AS HkX9TSZ'NoDO.U $CR㑡'G y*K ; #GҕH*&ML (N%hQgܬGco/3:˿^I4̆Zkz\iTmV{6TwI,. uVۧy>gt ^8~.\ۓLmMϯ.n5QάXq }iR= kI0d^6ͻF$'⮨gܗT;b˜;7<}.k?8/ؚ$5vl0{_X /@ T❟J0 Gw~#s{g.0}yeGiTԻQp7 7[]|8=Sϭ-\{eMt,G9t|.;UD8絙7F-@ivTJ}LkɩH |z}Wر K <<<#nne5T'tCUZ&qŸ@jOk<yv}7V-RP 6FmjUKSk}'+nMD_x$oSyv. avv֗#{(#MXӘג5YG7 5r`0Cҷk<BD6Hŭ0NM_(gP^~jbH0t#tAJ]D)RI쬒tK 摻q,'ꞽ8s^EZc"њBC㦭:+ kqbF cul- /jO/,Shl6#0.B#BA9w%I14LY_My>d]cRaApt.Eo[56ȥțӋ @e  ӆ6s=GgnRwv߲nam3qi?rTtأh}H?wg'|pGd=_=0Pѧ"xaBu:pn 17oO즎rKHSQzh}72Ndž!BA *W4Lzpich#k'I p89pA0ٶ؂ΥY$pX[edž9(@Q%$9C`- p.áD0d]z,G&|!X,i6غ©tT/|&8 i_ r14: %tEXtdate:create2012-09-26T20:26:23+01:00r%tEXtdate:modify2012-09-26T20:26:23+01:00=IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gi.png0000664000175000017500000000734112625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[]Y9seO& $JC -"W!H  xx($AL ^ܸcg3眽w?c533sb|||_2ƘuQUBuSOs=wh5 ܿǏ=LLLP]&K Z?<[Lf:~_I91Xk͡CN<#_ygNP===}xdddoݦ,K,NdYFQmFGG<7q,Deu$!$gh EQ}c Y !LmCVz}K\9hq;ey\.=, }lG:yG?NopĉJWLb'G "8Jج90':m`qD=;ßb˗}!QzG*Z /z28 œ'`cko$߻O/p_}ݷ 2Xop%xV{7ҋ/5\P*U&3_=Lq.E{"w5G}2 "EQ+WQ1&YN4ESf9z**@z=tybbɣGcϱ-_S4}=I|thc?ByNta }:9I&t@ѣ<++}+9zd`ChPۊ)o4 mhP @ ^iwȊ$&77[Zm%J \ !wk zn_UԪ*%5 ]|&'~~|'W.$Z("Ҝ=w#UDBr HD 8R ,-Si#*= *%P@sP}7晪BU . ʅ n,H نiIVTQpjJ90ji|h9M.EA)-_ve g~q1(Dxſ;t(VL,!6(*?zoju:3T;dwَԷpۢJ^Uudk3b U}J%w)F$f}45hX0kQ荷yc,΁;)=$z_ac?@wW̏},6ҧJU6ǏSݻkN_w͉d6ӿw?eU @)P2T)B`:v!L QBL8Aq`, HgP49,i?d##]DLUQht`XT*i6u":Zk}HćzU579\$s/tiBuʰQHߦF ҇>SWoF60|U2ztnWx>km$ [QX`  q{$JKwet@TLL)Fg:씣uS\9W/vEb Kˆ).<>thl#bmIe!um\X<8݊R_qv\-,0\;uqkdHx }ZxhnU%*pOa~)@l$} K i*6p"@0k3, faBτÇϊݩח(V`Ȑw萿n' g%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gm.png0000664000175000017500000000467412625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏGǿwf?xBP"'nsH$rȉWn9 D"9#d)B2l5f-zwg{q^LI~Uӯ^Lr!r!_^sʻ }2Q3 4/XhIUzh BL~8}5!1"ZTMnW:Xu?>s<{~ݿ1 A Մde٫ϲ";yZ !1$uv4MR_`fhq>(/-@z@@~RD?Y[ͭ-F# M!]HMlp4^f@J]YL1O+x*zR^!MD[YKJ!+UG yI補EmM`6 s";ߕV47'Kc"EYj}[c"]} i.#lJ"!kP1B"kMR_(/Ҍ1C.n Prz!uj Yw MiM8*6^aA$&J[BBMx}2T,$x˫RuvI*z7A/}|ε6l@a66嗑kz[WzBD2_Tz3J5 [bA^wBnK~o2onB`)cӸ1̡ҳ<7k ,Oxw%% s3j @ENA{{D 2_mRl;ePJ/!4tVTeY]̛#x߽{#/Az%.. R[ Hԏޟ, }+2e9GQ ߻hyЃ$ 1DB5L{2 lLlqZk ZAg HIW#?38< AD /<󼶕9pbQOV7S`^z[J;ӱFt{ /_Ukx iS(_ [@h7{Hmx~;l)/&8xls|íTyu =ƽa 4lA!`uZݴ8Rƺ|(}1xnv!HA GCa0qhJ:<0Ք] wኵm /ݔفx>Wu&0v׵7 // b>v3 .Nh k=TH0CJC`x}\gsl>XH>_&N1`ӄDL􊽩>a5x,y2ADb1GC|x2zh}MZкDY|t$1- ͯ㭷nkbcc ]N֜: O1EQŋ W|wv&iw moo!@oUY(N0uHWyKbQ_x<p嬽{680 >pgENGZylԘ8|~OSH#|Ǡ4!PF A<܀jmga4k5zD*J(TUy팈{zjpvA_"P+G`zVg,O4^o\5Zp hk~ 3ͅ]BSa8^iq z t}Ǵ[L4 S]mʞ`1礏P=; =a:[PdWuଽ(+?fuiQ;*>qX D9Kԛ8A9Yk}=CKd* 'ֆ@FӿxطУ˰8pД>m<x9?}ۚxZ>``|"CSԮv^^b) PI,QavgS366q̆m_+ %v! %+# ƉORgx\bIDATxylyofr%%ZdYJܸvF61PjX\#h[4H0"mESAa 6\[MlǕز|$(")Q$;R.IY%ξy{7o >3L>RAd9`8,%hL˫B&`m߾=lcRBrI^Wm;wӏ?ɾ,^5ZUq}}H$ 0>rGwyL&###O?Kc]9U7777ٳ确oz|>-ZZMuuu+,}W_}ṋ,Y_`Ϟ=[ZZAEQ,o9]0 1:;;ٽ{ `p8޾ N9@]\׭7M(aeY: )<d ﺮn˻QJu]?(i6 kŌOYTUͅ`ȵ598jh J|fp]/GH\K52aahGgwh-Xe\/!H$;&@YA"qƦEkxROS-\Hl0ptm er9I eNʴ7kP]k<|~h}zg0?'&~i!`n@Ʊk:pXi4u3~17͢n/P75ΡL'm¹k^xΥimd`tuݿ\o[H~Y5 R`O1J[}h(e\K^< ޱU[Jyİh2l0\FkN/82ǘ=ɖno5K(ąӹ42Cd vptl7pBSV;h 7}WDK1E7tv CHؼq;ۡ|S8쨠;8 4v~Kxt;<>SsG^[g~|ƁcE ̰Ik&c* yOӟ11,NL4!kLz1R<.D. v/_+P`*R`*.S)`$y2&CLd B*s |>vy>&Ђ2+ᆰWߋԯiiqC]Wyq5~H/رҚpbB q[ 'N wC09\_\p$vaM$ts1~l2yZ[+l鷈=ǶI'qz僾,fçQjUR%6ߥR2p?K`f"mD0 dl+z9 8sI`gH'hFaDZ܆Uy%\9]ZبpIDXxw(;ILKyAD|@gk蹃u6c/Lb4~A8p5]Z_)D'lr:J&:dPda_ߗ"2QUBUNH<75ڎH#3f0Gxar|[d^a=HWId" -7ΆyW`!Kt/!ĽdUUc޼JW^6ZJUB,B c'Nnɰ:[@ly4o>o: C34IzmXLiuyL'ޅ(:JK7B\;iET&Flu۾CA2v:^M,SoQ=?;Kdjdsp"A(;K8?gb0ۙHmg&DC'jzWWKɒ/ƋpI{܅_e` kؕ?"9oFx/wy*X2V L3EW-&^V-t]2tfVř5QΝDB$*:w{9\ͮf$1wTp6˄NC龛tCęAB:b2Wх*IZE[G(8R dt JԂQR.k29oFaZ6ۻY=/4:O kބ-}A1Rܲn ;n g 1ׇOҴHG]rp%+arKeeTd[BkZ願 t{mOƚX S)f}=f69ZM]GV^K݂&T7Hgk3~dEDb4{D"33|yS[S-EJ͒y+bB]bNS*DXosӼg<{ZH6:ܪє{XBꢍTUU+.Wbxy$BOz#(ʦY#ᆪzMJB(*kfՑb\$QpOQca5.Cd6'ƺm_XD9b$2w<0 䨲ذ+M\]b&+ֺ s߈"(4wg&FcNy7Er@iꙿZFXJ^R&x^% @Yj@(L*˝n )lAH9,H"@]S E ݀):<[/.&Q$%/> ,Rj|ߨRk3@"%tEXtdate:create2012-09-26T20:26:10+01:00͘a%tEXtdate:modify2012-09-26T20:26:10+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/jp.png0000664000175000017500000000466212625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx_hǿgݍ&7nrZ= o|Ujp})>\(*E(nJKC\xjWsm$z$i439s}u;3f׺Cd~9x& @۷o9rdk&yAQs!$\RӽݟeDXܹSNݺu0 zJB\{СwwwPU=K$X?!9"4mv||_'NmK HBmk:&D ֶ4_ax<'Yvar…㌱y X_xppp{:K*"dx,?$I0 Cd23g0ND5HPR!  uvwwo+,76~(Tr=m"I(eY'D`d$X7 -U,!X@f_9}/f4D(! RDQ@B!1}6 J$hӚXOXy}\z Uv-›6!c›7CJ$ P9||Y$6<͛eb=9IlAb>;;KX]6<'Ob 4-un@\WQ~ CCXs(Ծ>şlf t"+J! ~o t]MNb1.\xαZ<> 6=wMOSx{QWΝLP0O>AyKi@Y>+ι! ΞŃӧ{Fw {U%7+u=k-V 4_XÓ'aW*d}%+SB]W`=GF@ᚢ$!" Wyk1@VaBxy&!cbE`j|^{5E+v[/+rlf5`-)+r^p1!uK961o຾TAwsUm`x.b6|&¦!L[41Lўb`ӍVy>oYFЪv90D➹,nP,WfW7rP2Dԟh4`i  e2qiKk<u@( =/X#`<x{HdlVV3#`?PYLzsB)~px͡F1o-+,AYnaAC,k17ogOUp56l@ۑ#V[yLۂ'ނ1&⯼b,&5*a@CךVʝ:vʌNBq㚒3 [{T]==X<,/a?mۚRs@b`?Fx NJL`)1Dw@Of|_~`В7 Ao/{ǡ޺=}9̝8x6kqrf[dї^BѣP!E yjF r#RZ9i}I"%qcIWiVYuΫ4M10@)K;cK /@4a @X!vꗋusEEQ@l6[Bu7/RJKg&t]".)m@䣣&''RT$I%7~,i&&&*:t } X8/]X,7ɐX,T󘘘/^<66v>^]1?44tVΝ;wuttEiYxnak׮0@Ga9 $ @,z{{7uuuRJB hzaB7Msqjjj|||iwA.!z6NH`iNAtiC0kcbC+P:XІGS `3X9T$?kXVUXQlׂa:E<*.?CwXZ#VaynH ٪"|~>F1 h]%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/fi.png0000664000175000017500000000522612625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx͋UZ%FrcJ0%@F6r1K s?`tHbtH lֲ$cw43]rՎV-VWW{> y"OWԂ>"u24]㾣, 48hx///=3<ܣV6mZ>gO3MSea#[^͛7nBA+@ҥKooϜ9ƱcNZ-YO>[FJ@xk\8XZF=wWϟ{s8XPnk_U&鞆ӧ9qU|8'Xj(Nw8majE0oeez-}ڵ?㝸4^]t:㬬Lx ,aOG ʹtRDZhi-1 CA~7.^˗?ĕOni6xGM5*%HJWk!T!"xGѠnv|L, `)c$nJB#Zk<}mY@(ĥѹP0 tṷ%}E)R0 @+f`U'[k~$5y^H$NKQG~L*-"J`[;a3+1DT` SĮ{teaN)wX(nY8R1"eEϏ, 9B: $ETK$p\蠬T,sjKY%[9lQ*5Y\%;cvޘFxocAQ(,CbK'  OgʦmlP^ IXx+i,nl`F={?p/Ek : Tv5Hݳ9s!<jE&a&1@k vd{Vtu ,T؝vؗV)(B;fzn*o8 +Rew&S{aqZ1OL;O%7&3\yK4=r:%7VcDPkS0'*W`xQX$YUӊyp1Zn<ƒ!`y%1E@⥃+S-Id9 nes:p½ܬ2DDB`.J7 ^nlK{yk) R^˫W3UR@E$fVbc ]^L|\ܧ jV^'-6*Nuac!Bhς+W͚9jɡz1@+dd{= &sP[yH=p,,]5杏,'/L,6 ^Z&"-f]؇%"tr{t8VJIDATxo? c#$8)RU ?(YC!͢ˮhe_j]e~&HED%V!U6#Q(AmxfN3|gޝ̜|gf|=MRݵ`ڤXL=ȖA}q c>o!"C v-k^չ?Umk[ba(n! %|`ppp}csԺ鯮qLqs", ë@At4l<{"!a``"@{h*۟ Zj,..~<自JU3 |MD}0$`QdUenJ/f>}GDHR`x?l*b~ 3 h.e)FDr\ӟ~@Z?lFu|vThHF0u-@(N\5tޟ+/ 0ɝiCb`Lo*®K\+*Ȯce7u1楥WqG;y֔c u0]]{UnҒ)6-TR1(p ϤNbgf%RU5Syv1p%EF.]lS׫UT7|8 i,ZZeK. p}ߧ+в~]䮘Q@s?@yZ&ʠT[w&NP})ȟ$4CWN_?ڨ*SWTzy߇,b[\wm7W+}ׯ_sq..D$xzc"qΞM*<;wi K V\t`w]\*vW1xEx5x}CV*aKEg*Z +[3I8{탩)xꩤig&wTUNkpR V"{X`q~Yq8z^}{L.xMK&9A,S^/@^ 6/ZXք`5j67ހC!Zz+>ś;vs00pY 8JS6I8p )O9DRn๎=xU`nּxȁ{\f)N}Ep۷JpMH*t3hYe}*׍*P1'hc F `Bٖ5cr}FU)a{I߉w [󷸺pUWZbPOƕw.q!^0]Tn6[ 9eH%XP"! K8.XE\\t -;} ۠*,^Fw3p"x R|YRQ'%[ hDej6Ⱥ/73gv0= @?UE3%Z۹WkTu6*(nf"vzON<E`_$W'.p6&q36D{=VTq8$'TǍRNW)WEp.&v py_efw@TVmV 0[~(".$t ifZbc}PsSkj f6/»{af+hkg_@GN&6:0=+J,$\ ;/ÿ'AI˘ș:ScuuO+Γ)0byRCDstf (L\;/Bo$; U37\|M]ړ*6PWQX5iI+\Q4k9.F@Xlp0?`eԻq%J&"JK﬏]hv Dނ^K,lX!$J&m}nx|I3baB:\11wVFQt O Si6 p̥l%g)Aӝ֐ev&ߔC RN&Q:q8KfֲI"aɇ\T:+ʳ!ՙ,תi @ ô/)%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sg.png0000664000175000017500000000536512625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 3IDATxKlEyv(O@B9,(S6J!{\ +Sh%.^"qH%RKPHyy'xf܏=tLOg!Ҹz'=IO\JI!d$OvYBh*Di `|tв\}RB@~iLu)mZjݸ-nA+e}Z+^Ĥg0<ۗ Z)80[ )AuCXRƽqcԕ>QF)8WwjlJ3a+?V+BA+C`k`w Ⱦ>Y3g^myQKZkC_"mf$&|'}$"@NBPzxXmnR}R~\Ӛ(oz5jv/L1i#І t O>v?,zgtüv;їTAaQT( !1J]B⏾VjZ)r[2pp#G"]MݸyX=Fnvo-(i5&+;Է$?:Mpi,0 ^xܶmgϲ[.5Z^} b0 D_ͦwŽ iG 먣ޗ/ *&t~PFK|q#іµ?D J%DLSc)mS#t._ynQ~]mr HD=&ГOec!@dO*fcETJ{ܜ?80w"?7_{ 9tRu;(Νk04*"+o`z{6&2n@nfgx8.񦧱/\Hnؗ.]Tp_Xujߩ.̼ݰq^&i_F&:"qTE`vu:w[F*+,Q( ,QA9q0jFaBW)]8j3>vCCk,, wƓ=lE##z#}^ 遍Ÿ_;9Inf`?]%-FG)> ֭Oܴ(Aq+WBNJ+Ipn p-z쬔ěvxKvi~ZP33[b=(Mx3FF= ƍ͑7JVTr^;I}Ol7'BHP ^^X`ich?g}Oq] N c-;U%ğZT_+ߋJ]*@CR>YR=;79IᩧQTF]s"s N#?嗸Wi'eAҗg: y/@R⎏|Ca v;cc9hm5ufO92W3,}qK@[kt 2H;>NO*rpc*O?MBfg m8[w/m)y~"dH%o Gbmߎi\ -)I])cܵ4kSt.G(7=M-X5֦Ѳ[xQtbIS J_'Jo}K5 ?[^I]Y6R@,rth 4￟K%JE0hy#3cnq虵"$VJazmSXX{_޽ :кucYFcmI o|n$ƵSC,)ItIfH'^vN؃-m"إv)=]J`إfH'3٭.;EcD)y qU3"=u]\0 w @u[ I*@pq Ft[JRضm8,:LJŮV^ ˲[+`:zj+Jц,!Du_oH)N0Byk&'''ELτaJP^EϋBj'"sN+kx%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ec.png0000664000175000017500000001305712625666430027126 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>mIDATxidus߫ez}z4..JT QL$l2 $A (H`' EVlH)+БEIZCq8}zަ-ǫKH޻wsނ~7ȏB<$[ԥm9Tٺ4[Io N61WBtJŴ7ǔ_6U4|-MLK_"a+j" ?Yw O|xLgg|G"cLĐ67RجMkHZx*Z^`Yoiz~+~ٗyl(>=<Пnbڵ@46mhmnh`6?6Ht?|w@*J_O9˓=".,uͥrGjj}ቑ Nښ`p}13}Lp`r`I%K&x1ƀȺ5K`4M GA (=ʾ(rVX"{ڂn4&8 L a[ q8:Pua6>+A Vƻ0IY9E}pB1?b܌q ${T^J=!'a􌀛$Pb[  UHcu0 $|a  ni(u5k0s),OW1T>WR~p鄰\죸Nva BXs,LxZcGa(tr6զfQD  > ZS_n YA\~ ο$JwP1N*gl+S0kY9QV1wsACQ%驰ph[j=VZ\7H_c^A%!=`! \*FDj+~ />R D.B u^kCcgV6mde"7Wb)pc ee["h5Ѡѻ F@) #&ah(qގSMu6&s[YcDsǙx =XwVe>gTU/C NT#1_hF5Jn3m밣5b6SbqUj xb\0Ie}x |Jz3R{!,.bUa1a߬mfsr̍U0@GrѡEb4(ۧ@0"1Gc,'X92>[v."!D4^S]b|GB ,.&\7~+ ^xm lt+1M$uVVpf/79u1$3v: _V2)b\A~˯0S8Ƶ3#IYZV&g´RNd8s%oP> g%>"a(IaMTtk%eI]+S߫Efx3%|[o}YƧ]L_"FoHٛdw1l2rǝY.0_bus\x+xnqL[Oxu0o /rp~P!AoAwC,g.;J2Mw@,V&z pϡ,;!,8ilR[ !* X5nD jI;y_3s2e 3yGxCjpg$ىcQ3>̓.pL y.>7 5m`$Alv4 [ ^D) v&`m%ͥ{Y{m5ܵqƇA`lg7]q؀dIa2߉jT, g2p#7B?aj ;R*W&Z_Vvi [͙Kfp cPXEُQ, >%ٷ=#i4x6Ng ƇSk`vbvOŪ7>J kK ]6imϬb p|"+Ku^∥'`,RJƹ@/+b#n~!2yϏ'C& nN`po(cp޿WdmyF$#+L CLK9ܥm'}Jj'K xO1u0X' au.K2 tXoĚLu>Vhn)(Nv[G16K< iX4,(I_&ʚE@0;{;ZA VIpþÅ%[, >E<O Lܦ UWU KhK;?XF{銗q\LtZ/,Y 2W[܉|1n/I!E$lVG6V+շ)nl|AQ(.|c!RkdbI xZep5)*j#/(}c%^+qlcZm8+6,bUn*X+`AT6WJM0+PUI*zjHh >;r!^>1LR}&H di;IgB h1ĀW%RuXRw^VVVo`}ϡwdshR|). +`LI j Re4Ǹt`-qB/뇈3N\!;CGɕhE-I|uI9EQ xEڴ6T'W%o]pjG%CH\zG^H;<;C"Vl,i7"ו5܅Y71k86Wk7qsp!Iv\!3E.d,eϐ-k*kkfFZok&Hf Tj6E0ԿJo*KkL,'#x/ =]ya:GK;>|^q u ߺޤ/K#OVԶ ׵5|>C,lgmT Fe%b5`P0a$ 񞇞'qp/|x %V33t5UI&с=tȿ,bjs† Z. upjAup 6ZT{'V)n,|辷1s w^)~9³8P=t|_|?So2G9d"%b+r=k/>Ο.(3=ZJn9 ~f߹JJ{ HW&cxS\ KF_j@nD@, ߵ7yV w""!niݜ>03).Hka0X/x/O߰oڷ(Y waB&gWf ~5R b -੒E ^;LZC LUҪ[\Iju+y̯SE( q; #x*cG/~v|B Iw;^GAchBÐ} XkyRw\p`Uk{yC p&Tl\am|gFô޵JrH |mf{KSˀ,◾ėaw!+\ˤV 6֧[B`C:GVhoO4=- ]6= X݇LrKg^"-rk`wTZ:L_7چrR_--,MaedhjotuZ2N)JFoUJ?'xU~9΋,D f}3Zܥ죭갡?D M[Xm9j\V/"P\ftfh\eynǠf_k$nPVSyB6sSzs˯k}hX-$:C}G抆ߦ2(+RFʢ< CUEQm[vhƇ(PF)慠&mW. .=t{Z9d[o\5*[^~[Fɣ\α]6dJC 8S@~r*Iw3 VZ oW jIDATxM7~$I(,@`S@T v,XذgAB@P% BT M!&sgc{<$R yxl9ksy.<ςhI&x&LAeqЬf(5cξZ}ec>9ADAs[W@mS(9iEkW_={Pm{骕n}KeXeWo}̘7?^x\cL!# z+G Y4,w CoC!R]Uv{_'9hu ַD^xc'D~D߸jbИZBp!P zK܅ &gXo؜Ƶ-h!dswkX|q!h1UӾ7n|X.VU,D#-N3(Զ ֠E1 6OŪEl@|Mys[[1SN<-3~ӸP'Jc;kLKB] 5)4Pi`b++ANܔWߵf|5pUp}!b2Әfyԙl%Y/! F-Di;8 _h (JMݼ`Pn!]E!@C}M~PsD$T#<烉fzdi [ kIf MN%t}11T[mB;0r;a:p}g+ʢgγ1tn؜XZĄ;MDmkaP&7֘# 2$D_5 2] q*A="C *\ C1٧1@s_"9J"]L]-P,mn̓Tu'o6EhŔecf1XM\hu0?-)X-NRY/ `}.]?ɒ)q25&E$,7 5^ϝFpP}a% 0aWK >T0T Zϗ45TU~F@FW50)g >i+<_@ȀEmkbv~/SbcBDl֥.>c kw Mؿ-Y`h];VA.~jF:m< ;CNX 40 b߭_rnqԛe5f"<"!C:`ש7u w>phbshM#j>eK;q^?o!PG&ı!ѕ۔Ƅ2LR7}g-&2} (Luis:Y9$d±|hFAT.M؊yC̉^,C~1,0nB!6IM7,ۦxjմEajY(vMՓ4aV `*xȔ 5|p7&.M0 ҿEq!^"6FR|Li_ɜPnjyQ!,HuL+ ")Mj@Bp!БN|03\1ɱF.h|a a#1@fK54i‡401$M)5r̚JLF]RxXX\9Z2XD ҬvILmz2b# +,`!YPIO=lX,Khg*bHOUn[O7u)e}`5|5r+BuO$DkA@%b2D(M Hq/@ [aum\v&Iνpڣ[rmT}ЛH`bkAI%]4;..; x%p{8QO65PJ?[~q^_zZ90#Rh١,;ypqD}{0?/~~uc+`'Wn[#s0mn"TsqQEߖz#`ll6>n/,OrvKƼ=F 8>]+@v#~dEXvvc߽ǺقcY N{\g[(e߁.l<@iTA uMd3$l|;1[erG=œ& Cس}NbD\YMӐ3 %tEXtdate:create2012-09-26T20:26:21+01:0056%tEXtdate:modify2012-09-26T20:26:21+01:00h,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lk.png0000664000175000017500000001040412625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>BIDATxY$YTzΌg3x 1&@ ĖKI!xA< ! bQp"q;!Yw2wZCm%|VW9kUam:~h2Ijc V-X}&t pGt~#\U P P(_`}T$B>y!y"wyO^t=!UX5@s8_LuW;Z'~YÉ%bN*/5 ^q_D00CX5'__ W?[1ꋳjg>qDBCugU T`.- \ L2V؋MM(/ޕX0%y(C A:S/>隷+0p)/<<4~f촋CϵIB/Qe`&"4lH5jL2f AJ_V/眯KᷩPQ6͆xw7 U @Z܊E@-o'ɝ12* X\Eh7E(6XZaĴ(hʠT KK҉U0K"wPIJ&DU Tfج246 6+euzQbrC4f}]avȣϮ""_%Öl6 $|il__d1=ͫ_:9Zk,_QRQx#2k7YԢ Om4"TھB&)^IY]w[тJvbQH[f4~D" #jR%u(mo2H9޳*GXCIRd$c,s`WeuU4cabvZ'=j:dI%q&fO@;OUxEe*NH{q@C{G]mp8h 4Kc_ZdjZ!'>M5v.%+f;xh)-;  gb76Xjk|N@bB#|rz];Mk5QH;Z+dLpڝwiL}kN"x|TX&7P݂ LAÙ1tS##}C=ظF ;.;O<3%ީy{,Ϳ>31; dU&V#rNܥߺ̍:>d5 NUtۤO:Tc`ge:M+X;#z5vAڍ>Ӛ 96`TN:Cov630ŘdQK̝Ő DQBWpjbCwmpv8u3!iPN΢GmjK'nB&򾼰րud[`_![AjM"pj|jcF.l߮s[<*͠zzcEW bTbl}>@ 89h+,+q,A{Уﲹ'_NQچZ;o}nҬ)Q]kNvHLOA8MϝG.셭#fk8ޮ>O8k`TqYKՇ|;+=;=r>,ZsCq`rT;XtZxƩE v ?եYpUP#ܺ^wm3A™~W4un}STLP ے2˕“`qU4(?Wkl@gs}-@emFoNsor_clz;$A/ Ҵ0 I&ich y]0, ~Zڈ\:#7Ю0ugZ,̟h/ hLD!b(PkGDغYǷ;6rassqog1dTz^)}D0en3Д\`O'ADњ y 9ӻ}U$x-2w6S>t ]3l_oTRC ol!D=W}#9t\fʀB}!`c#@2@2x5۪e\ +oNyTJ`! QDZ1c]ל0F0/`Ҵ4X.ٴ;P%u"<'/Klj%l]d)g<m HZ{<>Y>ۄfYn!4B:0 ?%. PyD:c=ΏmwqHLC^iI3\lg6Yzb;;mn{+W )U_Z @ CVn 0r 5)">7 !{ LG0篨[ٙȜ'hDc_LY9P LzJ /2PV#{1i[|?B! ҅kjU=,yQߵWQ:| ;bA စV _.~dS A6yH3qo煫 #>80<^n6z3G:@# iHD sƝ(Vȁ bG1ob@]`XzaZM1uPT|t@'{ CNdkb5?Bty/oĠ ? 1I|h+v/mFoy6H55~m2A %tEXtdate:create2012-09-26T20:26:17+01:00j%tEXtdate:modify2012-09-26T20:26:17+01:00y7SIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ga.png0000664000175000017500000000502012625666430027115 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> NIDATxo?o%[r5Ҡh r+9CE{,?(hszhApҸv-KKCrf8r%ڀUpf/{37Mzޤ$/eH:xefusH^uIB &5[Lߟ^13ADDAJ*'(}i_y+OzWuRۿ8rH`7>rc>ؚMf1 HM#3W!x%T#b#xݩEʭ_,Oo5ÿȀIm_Mv[7dggtAD0R ZH Ӑj-*2T9ō3sR}Vڷ;qvU~6mOi,J<Lc̵xcv)d\*/F TmO/?%a]nfW=3-3 YpjNL@_MN+ ̩"cTHi`]"`6dG~|x)pȹ*FD X,~%3 O"SEv!V-C OjP\kjYa)b0 Pz 4L fA7#`<4/4М*j7&Vmv"Q067cgJ&v#הKw!=`wMߤ)mI1 utkL}Sac"/ S}Y@_i %)$ʆwbxhLu*Q%]]ξ+zlU 'qo0:S!Z8&^y.ŁFEF D]8NB>mYQ?oH]^#:49ІRd !tM]K}U|NqURϗDZ3/g"4lb31LgBfU0(C !x ʷ1r[,Ow0#ۼdSD%MkZjq^ʏQYs^(j-g<'lͳ}6)b1yǝGOlP,#:X;l D'p|A‚"Rp-ͺl(EwhieKĚ Z[qqeE Bf%kЬڢ:^+xUtٴ}Ȫ75Ymhű]'hCK:GO#T*^zT[DXV̅[оSؾm%G:sީÕ"nݲԛwE'e"f߰^WO Ϸ*WMR= FӴc%bon!P~bBP@m ]XxEx}k=j?oiUHj;^]Fn;MF`=t\5_fZm:Z/z†`5&^[gUv: Xyz[^l{30xK뽓jiyȋLJ) ,>.'?[{dkQ{xJɑ1/t𳻋0E`s{oo{g׶Lf-9S3YLݏg$>6Jv;Y}ޭv˘J'O9}(($̀+}~sH‘t< cCk">ԺMʏ'5M-)I|l 4Ih>>W 8IDATxy]?>gzgzk{la{>v}n`7D@EB BA8q($%ducmc<3>U]s}z~ mwۻoM~34 3q7rBTp%!v{Ή<'ȷT`*t""u"FiiֺvrTٻ_̥\xlb"2bӈ|o2X$LGy{,͝QV;,)04-а>ͷ T:,`}_4>PP *X^K'ߜ8 Z-4HJ3fDzL4!Rma ' V.Í]&WŹ;0B`hkD WzCUiRuյ՞O4V/.j%یﰺ#$ۈGJ,@`p}ǹ<gƉP-kɐ*PU Yo4R|ngxo^zΫ@!`b oCQD&" LG`B V+ \,r@{b2C jC-avՁz@U`[i5f:n B m8p KYk{c\sk$J1iU'D},%@h4jcɡl?P~վ(@0@,`AK7h{߈DG SlxEvޗt/p2s6\1kF.C…9T%h:gҴQzvbZCTyRGhM6r"OnH,CN cQ_GT06 '^!ra '_2asa"ʵ(eI`37WruGl+9Y*/l!$0+DAjJo/۸:cm01oce }4~%VLw{bŵ=Q.skE؋7 ui6Ze=pJced?8?^Z qb{:ѮBVȿ>*ibx)85#)~Jb6Ov{ȹ""\se<|E׺f7uK@Ŷ> v{ޟ>F>+Y}Z]*$ub?|H",ha  ݍYy&؎ftS/yg(45BU#>P M I "S-B>0F~rrsw@cY:kB~6B"C) &rBH?՗oF~ d^2q2KD@}ئ4|3jF\hhik=M%o̍ݛJ AO7Ѯg Q#dzKG3Ȣ3ƽ?g@kbz0l@=TsxJ?F @c볹<ŝċ慭dH-c@88jO&)"Dc.~8,mG[ Xk3S ,oc&̬ ,9DڑxT"W+}0c]X] dLdⅅ 1\~hJM:'u7Kܬ`;rܠ3FMIP':`(W".M|s- k`n/@X`$"@Th;6ܟ40YMr/L3}1T%=c,5V_AUt)_[&uj/W"2dh'1g*2I7̚d^2K{\13bOJ7F6!OȦ*}JV&!,7XT-JQ6۫DRDG:PĽ_|}ghYH>Cۡ>|U UfSZιJ~Q.MD*^Jj1M]rE_=(r~n l%(#1&H#DGR`\ KvgZDVo&6!:Nz -ld-o*q ӏDxsDX uTzXUomVa]PUuPI-R:hLcI (]_ϱ-{:RF4SkA4FA98mMr|7,v7Ę! rv@@DLEQEaDG:V aT FF̄scśr(;>kCJ]C+0/`uDqJhNGzCQJv{ޥ^osa 1C='g;PI2[-SZ캮ܹ<?83BdHoC8y?4tY0562f+Cɯ=ՠ^=faH1L*g?op}Q. +C00jEq$h/6#qfvq݄r} :ܹa-TE"CǺXy&S l#b ِo(K]. h}Uuj[|ak8h{p 6k:4wHlwg31:TӃ) ; ;ClW2?aTuQHk_0SC8kȢ|c &jRDvQgNO0m?ɎC+fśt/XFHھM#IG}~.s5O R?] C\fv6/v^V$lBT$]xO<3<9ȻX`B5dzTfVȿzH%l Ĉ 䞿I{hGb,dJnʨK)XynIX, j:)m&jx䓧Q&Q)r *-[ƸA+doBb3|Di;WvOQ;MCXr?\ާ_Y( L?өFhMkH$=<}k'cFCEZoZ QLԩ!LDl+A&(\cߧfZ,8Lߜǽ_BK0gviX" r/L.exdUf[AGOAlPQ $Yܖ mV?re(hwPQ-+rݠxiv~bh'G{ib]ЮHڤ #lUp(]]r#Gy oܣtu S`X!c>NomAA/xȪjх3go.p/aQb;3QGAC7U$EC3'Q!Rn5m w`U*kuoa E8.\ya›s]r7D?'* Z [|zP\C>:? l(L}S|,r:Ӊ~,=O*:﫨Tg;sڱ-Up#o\]\-@JSPmF,F^J*!/8<( xh͆X;\끮P * x=l~P]s]!tZe)q NL\bۨV Ԁ)ܻKդ6 ,Aۋ|Q?t kCJtVIDATxϋEǿg2Mf5"BPAѓU,e)'W]iф0){ECS3?A$ʩwV?{! ͮsMug#+_ 8+S޴<3g03+NF~vto/{X}[<t\ NGHA4ڂ 57U.Vi`lh RDZ:}0AZO@xNg^ :Hޗ#Pm(XzuV Q?SR8eq+ߏ\sP{_s/<&;:!D# b:ǃ*۞…ؒ>^q(Z1 "# |ue/?Ӕ6d=!IDA !l`|Nh6 lfTH̐@K}7e25xS)iI$$B1  Qf(]YB^ pُyz P5=!Q(aZ2*y{nWl?wP<@ srDH22f* K*"mf/sc!ﲡU! "`jTmRM#xyj3|3Numai&'>\ϘEK|zBk'pl`JA`!gdEU\H$c܅fަ>@ffUji] P<$ W =RBY ì!œ-s[ę4ECo/wѻt u.HF ݅`'o&?MxEǗ 6Iʖ,.Œ%*82e"@^$%9e pNYS%&*XtamFĤ,vMJTb[kRb#4M$ $|b&IbAZi"cqx "4PtSJa<c<#x8rE!@J 4EE s!DьOjGGG; #ۉܺu7n|_x"p8boo_ml8LD|p/(^rōNLq͛?\v+"x6v[[[777]f Jtt_4 `Ȅ(!ϏsNޭh搎 /9!&l6ǡP\jv p ~ PD@䛮y]&u12h#+a'͇^E>-Q<$ ל ͤ9%tEXtdate:create2012-09-26T20:26:31+01:00)%tEXtdate:modify2012-09-26T20:26:31+01:00X,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/es.png0000664000175000017500000001141012625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>FIDATxi$W}UUܻ;^C؉6NC$I%"EʇD|S$@A–O^{;}WP3==3O]zGzW^nz7Md;u б2~Sny"a `!:{>h+F){xGI[_/s"E[.`̉sa L4@#nIϧR)Zk7 cP&;j/ȋ 0 [`̿<_|2.^dD;He}vZόӪX*8qJ)z^ljp50iCX2 1} teH /'}_cJJA)UmA<&IVꗠfTr7 "V m|ReY|7%AT& ci<aF| %]APtieR(ĥqݠU`|!tMɆ9DG$.)UM?bm:O'109I0~X6Bu ӯ=i9wI0%5V߻P]1q"Y⤓%olt7-VZ(-ኼ%!fK,gXaHa(_c^?Aq({jWE"J!8MBK[bZ\F쏼8P1t%,i㤫yMi1xޣT(HyBRi5(,,9sx/E-l67-naQF[MX A[7QPO+K2'<$4䜐^fZi N`AhYPCvC&Z+:(Q`zy 1Ft `2w3;TI4V z򽫯]̰%-:VvXDZj5N"\PsAQ8ax=eȾAD f}2<3Y 9{NP@o6x]/gKϥJ.UnQW "iX1uIhQVք"Px Yvhut@d[9`XO;;Wa1XK,u62qƚ_& 3'ePK 4fX98zON|o`9J$&QqX<}+KE6EG W^SFNu6ۨ `)1v-2-AM*fU815=hs: \CwC+9"9 *ˁqxsbŚ&e3eNS37Tfa(l+\R1tO[H D+^> {* … KU a PM{Ƣyá;')㉤YDɳ{߫"է:Xsad,\f^=34ー{A]hњYk (mAJ9t}:{/%EX VQV@Y}2_pEUAxrS͞0k-DI}O<O>Ip){qRB2%\nP}z7<@VR(&GC氕*T$p "fX*Piqj_M cH=NÄa)q"r/`׎vz rT Z. 3_]asL,rr]Lݞ] L2 7EŁ?dd1q < n:w3<U|dK,86R3 sCL1/|;%ŕNag[:L7$tAY Yxp׾zkBsWn`189U `W9)&rbs䍓)$ +h *,i%_~cO`[ofu_TQ'gkE\oCJ(&^x_'_CB2 uQc|')}zjK?zQ,Şx7*NԱA%e ?|73ciryc GAK [ 7,}`Q ܥ,@*5v8 X.(7u#.?1L&NwRx4w:LH稊|L (7O0_`:H\sH'@Xj0Lz}}ݼCd0Ab lH:1"Iq1?]G!uV3'"sv mNQ?ciA% HP;׉KTdrNluTA:u >"׆Odb[ ]1`@d {o*Ч {L(6;zop[( *vc+gxC49ųl8FiKMVs20m׏92 "M of@Vخv%a1Zֺ$&I`ENq~ ÏN2 YQ _g;e=u2$ T'˥M0۱'ީI@)ZV5~1P\L EAIZul{uTن&!ثbb}< óp 2&d8Pkwĸ u`X"z S%2#?69^B$k])>wziwԈn]0GDR!z`s]"a}l)%Io,{J_/ ބnHv`!$CM??5a %tEXtdate:create2012-09-26T20:26:41+01:00#Z%tEXtdate:modify2012-09-26T20:26:41+01:00R% IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pl.png0000664000175000017500000000342512625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>SIDATxoDǿ3clf[B8!ADV#' \W#@B-ʹ*q(UTA`O:cG"~Ͼ73z4Xc-S:vBbu 7βihاV. mmmuo޼q91/D"B!έ[~'Ovo߾G$ M]׿X__zܹ vsO݈J)F#uxxͭ[{s ȢoooYgˈ>gliNI^vE߸q|%h(t3r2WDz 97gPPϟ! ݗ,˝41A33 Giu.BYl7f !IDATxiucwgwvg,)J(,*"ǦN`Ñ X@bE$Fb @0; H$NE XbC)2A.Cɽwg^~tLOO3*S}{^O~y-=Z(Ԁs бcz"4;f@@(tg)mb+WJ)Tx"~^֩{vDv<~t"xbLpcW| H@Ц߶Qr9ZkjAl* b|[r[M&]-y8V\7ns7˯_dD;ȍ+]MMJJ8C7@ :1RpjTo 6-C}J]<hL@_GﷆO|H`e(QUv [oiCҽue%}[aT{tGim)KO)'rBEx^iC~EZ `~.DPѱ5p5Ơ[A^)Joب{q:h8@1e;eA@a:ETh~msC:iݗ(/ޞnkNlpm`(D, P9raTz67S]Ii0P1y/m(x>/qQGr@>b%4 uj/)o.R0 v=l3]C0so V>$v-‰a#%5[V\,mG/2W XF[yǺ:!a -IP3ASܳ5a,+jFhaF$lBUQ>-|T %[#w7xweVB4|ڿ0DNL0=& 7OL{\]E÷/689l󏷔9Wtxu[!0°b! wɆ"/U heYa8 ܱ%;S%n0q Iׂ! XaײwwsR[J0 BKp <6<(yZΪ^(sm 4b!cՄ<2=X|Lľ d|<`TA]:=V)Xh?mc<}C@>0< ~mB-/hDlY[(hj 8F(- Omeɇr}ᓧWy2Su )cnsnϻ< v )!Pcؽeq8|{ϝXaP5/U>8Wq+w[Egxtf+.kGyj(',q5r' U@eq $^7Dh(3ܼeXdVva7@aDK:7v0 KaދuYdײ T?\&>!0X)P[ܦJ{8F)[3E' <'T9WSR̵tfrޖAo[:y`$Q)$CJ \%bL\f^,ypJ;ǖt(?2BҀ0U%?Ue(3,,N0lY10`= a .zggjtcy5_'@"z$rCe _/gry|OMS$pl!bqCM%ֹ 5 ,01cVh B]H*-.Py3dD]Z]#ĞZ?xКt>$EbתJ|0nm=4Z$c~y/$%tEXtdate:create2012-09-26T20:26:09+01:00,%tEXtdate:modify2012-09-26T20:26:09+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cv.png0000664000175000017500000000761012625666430027145 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[GZUݧϙ3㙱kglgu˚8lUvC.J@a, !ľo  A Ʌbg}Mˌs~R}}Όgtu׿n9&N"吂fw&y_ h pmN5G}q7=T "3s"렔U$.*ďݪFKE@DgI&\¹GO2QU*#iu}'=PxWfqS[U(y)p9c(kv? ݊5)=4hvF~U4 V(Q$^=)_Y9X%bZ7#am[rc!/$~kz(kJT1Dd("1`:VJYbcQ2bHQR%:q#IA+1v|-f.\`K9Cr7+⇂a] Kl)vj%s]*F+/f  A){rǓn  !fRZ+ƀo ֹثHDg"QL2myytRVw#{n򵟹L7Q=Z"eeb 6c,Z|̈|kfU)e%6)c<=p_:xKOpfK^Zzd d(Z'vTZ{ A11_Fl1YlOăZ2EH159F.ؕ{"Pź='[y"06ul^bYn{tɚڹwBu({GYz׳ki7fhe(nJ9*??EhZ6 3"1Q@/839|;v066y8SQ :!C>h f;ȖT tREt:\|W_>~{mjh45򔣒Chj B٫Ja$ƠOPyzxЍxN;ݛer1˭+*(/kP0t/lƬ4ȎP寳jm :n-'ƋLnM#4=ofLj (A-}32[1k=A,8_`gw41Wͱu} 7o @=ƮvZf G8< #OonX LKqs02ly!6o_j|:!͵Dr `XV.Cu`ihgx 0uWc]?ZG'r'HCs7 = Мƽ/ fZ{oB8 ΖȕNZ~,Wf-4m,Ei˄-s{4}j'8 :W^ ÖQ.0?cA&J%'q1c=vDqfJg\fI-ᖝO%K}f <8BL2PQ${1+]M gwjz8bxt ~]_1t}{읺K )SbUrg<#Ae`E# "&TZT`ȖVvCm' <85˯:E1|zeO|I(JO*t?*K41In΄0Āk #%_SӌYn |~΂LNS$PrwJkuZ $Uz\Z,VTfa]eg>2Gv(4y]|9<ԥMxnm#SO$\}?xHHn$Ct*8'WZ%INLh`LPQRg x ?ܒǎXZNIdžU&0L;Ǚ޲uw I~N t`ҭQ]. 6U:P=ɵDk; tYp3o&d&@YGNwPH9`!g"܋G.[O/. KuA؏Iɦu_^YWlUsS#@DW|%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/us.png0000664000175000017500000001221712625666430027163 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy՝?ό1`6 W](# H+jv(jlP \=s3=}Q=H ?^z[~|*ʟO L.9'N;%,it pk{υ'mo?d-!*qi?|@8Ɔذv%wbO{'P .j104wXTG4Ue.4MaJ6]*`UDPHE(SM+5_,^)TLô'%V#<6?hӻƔ+F 7h'FOfi:DqЄq2;"*|S TlVЖ'A`Vp)Qʣ/_K:kqh7ɔn\í7oCG{ф@)e?S//nSyȘ6bZ 5|ݥtэ&|#MGW/%Mx:ªTE <7=-?kiumz,DyדDXb!шH]()3>rf0KJ 4bb2ۊk$SaZcO{'Dִu db2y דxR<)=c)7BLfF kt%٬qptayضH$SH:7A`YC#1И:u;Y^`x44¡ wrn}G{'!Xwq| ly}/C#1oŴl{F(@oX}4!t؇{9SoxZNM$' ԅSHY#8x7D2Cg0eX:LPMCzXc ư]PJLeRo+Ʊ&&StR(!pi2=J,('9e,Rfn25Jֶ^pOMy5ׯax,'r` !X|>5i?95AC]5\=:IeL@ A `m -ꯢmlzvP@4l/nd٢V+d6]~,o?^%23bM,1WJشS)i["J*=>q& q+L3Xa@(Y_1|d-;/ϑ~< Mfd,ΦXr(d-^!UacI¡ I{G?9rs_5߾׊'=aYH$ 3@!t؍h[lŎ P]K;{]Yx.~gٱOɈ(_˰lWA)E0`\rr}]z93k~?$X{x.l`".P3BX{QR^F|I+ׯ&1>rz?]n)޵ksr]er}Jɉ÷6Ø&bns=M:c?y( Oݼz@g^K1:m"5ZI&?*Vu4L1:m{파uy- @5>AlèyH'=?JVع(bwqh?=ݟg&~ RZ_lzuv>L"`V8o}",#mwК\w|klAl~! 2]>d,H(MsE4Ə>w LǒL&3 l"HS]&BxDXD*D2Cl2I<4m?cdMx2Cdd*뛌r\O<iL;+W]\m/Gs|"Prú RV7'ҸpFrԥ?g6BJ) #XpRN]'i;SPP=#\2/\6䳊+//HDIOrFͬJ|a<}.l 8zCaQZ!C~#~Oʭck4> GIҮ˨i`Xn"g¾5Ѷ3X8J墹f(B(\׌NmZn+ִ S,* Jz }2zrEvVٰ7fj hB+|T,GS>v4##Կz90 pņ\6'׀W!Y4$ ds$TSH% ^>`B`n%sAZl}7 Ňkf%o^vt:UTI>a0-F%tEXtdate:create2012-09-26T20:26:42+01:00,%tEXtdate:modify2012-09-26T20:26:42+01:00c?IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ug.png0000664000175000017500000000676312625666430027160 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 1IDATx]uv~ѲVK<@D&C, E>*U 8~H*P}_0<]Vo}c2"P ML~0 mD"| 2bȌy*"TdjjJB@7V-0Ml~EfIE_Qz^Tj}XD>#">aA0~ X)7=;P0]-"BAg Am7""ǒa`UeR Qd1ⶥBb-tSҼ48mJJw1l~I`K'jc)`X%:շ:1NurT0$ #x?KqhL,v&x^n8ԂUmtyjM-&Sa"?anVHn{|\ : |W[r@hC:0.j^,ap=ǗM<{B;)d~2U0%IzS.m~ȕ}T q@}vzdSEmHd*D$F+x+x!Ƨ/$ǹ Te[ypHۋ"'^6gb$AH)]P%"%+e"*vG>Έ#,v9,,ə:qP Z%R!,kmhTspGL&F4)hlaϳc::Y~c< {;|YD'J$yXI0+w_D'lCUz-L_Mml.;+Y1YPb~0{_"wxgl\%iɷ0߫s!x Q;-|ߢ63ө } ǞH̀]@EBE)PqSMY!lH{~L:X׹Pz;^qgh֭wbe@ ` T T ˦e sRn֠𵗾?1Fբ=YہFúv mR 5+{*]DRà/Ib cZɸt`3ɯ<}1.\k-sܱ:'v&Wˋn%^(i\Rù6{5 Bڕ[ʨTbnSnYݮdnPʯNgꝜdbl|;.z]jSxx7RA Y0)"G(XM)čiFя<-3^]e붭߿sxSO61MG>ʉEΝ;_ MUMⅺ?Dz@+3}]N8λwrλXXXs,3[q1{9^uLY "0TUzC8=\X;BUτIɐX^޿E>'c߾},,gٳQ38 /piJWGu~2aPv4olW*0l3z$֫Bb;Ue6|N^oFVcqq#Gx, ůqq=HAjfuM ?证?O>P*8N/1To478cCKKsy+Rʪr/Kk)pfyd~T%6a@?sʯ_> kRUJLOKX>*l*6I$9߽*X3?w ]5Qh_Jyx^Aܰl_N oa(yEJL: Ήe| tEZ6,R a]!8>;5@)/k*h~C]`j"s_(a"ŶPie@M,~N\{>1P_ k?4z92T'VMeePp'[bI&q~`1)R6~{%1 5rUD=ɘNʶΕei3iVxXe%JkX@(6`D"YbVh&Fv嶪 oTs˲j m)RO5= g.+/^*l]m`˞jp6˓B֖[|ʿRbkYXm꧛zxyEᱫktשׁ8$شp159|j͞E@SϗoXm}pDguzkGڷ:!φEj40 {wW ZF,.wέ4ފ,^d@M@ܒ[un4SV?.%1φJL$K<׵Akuqd(}cyK)z4i PF2#Mk(g%tEXtdate:create2012-09-26T20:26:10+01:00͘a%tEXtdate:modify2012-09-26T20:26:10+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/km.png0000664000175000017500000001000712625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>EIDATxku{fvjW-V(0*6!C0LLA(7 UT*Jl\1qT9b*eLcbWZ 5;{gzzzfv%a3ӷyo7|A_%Hr48'jp3Pc6h^e[ᵫ: MEc ""%yǪ " ~UŻU?#~J%&]޾Pɓg?s?fp@L/oYߝH߿^عy͐3bE@nq6<*\b򫽩*yF֯<#?|5P1 l ԑZk*xsG/Non8WWN(F Fq]jv\\~6S`0==H:d~;NyE50оՖjIJ5M0HUXۑAT-߼id=ki _Yۢz8e%(~Ęr-Қ$IJ8bpXBAO,X]5iM&61J"6ZlI!ZZde8g0Vg@ІeX-ܵɩAVd^=Y寽6rmH$aZx>0)  R: -ڪal`5'=o_]b5pR@MEgQ jIţ+ɩܽ۳S-uk$4yc}jeJՙD(6/ ,牷K)4ʨ/kG6*PF+/Ŋ-d+O>X'3-X\Ksi+)Qlѭ%ZؿCc xd)nY!iժ(ϕ_u< ^ 0`ڇΨbPPKVڽgogyR5 jm.灗¸jHŒ.Xl`Qr Oph}ҳe7KL8 ޭy50z=;{_bYK7G~xn KXjՇF mVbܿ<?ٷG˃3UP0E^ @ v?}-|kvt]j*HaRziLCG뷕4Ob):q;&`4;AR㻿}_}\pPMc7=~pvRɫs@U_V#C-YNd rpJpd[MBa8ˎco(^u7.[.o&5\d [o*˻^I-y.5r[pU+!`+p`cqF sMTGRjƯsMWOk\?x`J4 9 b_<~ܰڒ?Ur dz-dt3$aٞQ-&=HJ@6IWyocLb,v)nx/C:g07D]B-ָ0pa@+yAUЄ(yj6`"/Y[9<1?OvcОjC@z#`;l{u vTZq"`xAYjR"Q&s fy %ʒ.\?X"l^]Ȼfr+7#ʢ"8+ d1ZV OUu=ZkTFBr9 pFRDxeMt{DشJ_w;OA2-m͌e'9aԔ/_U~W&Yy,}I*LxhL+8Q؀4#>9MvXw; Mwsp8}=2NXҼޅ+?zעC? t)n|uJ&EJ`6/'/i]*xArZ@}3]?LfG6âB0dɎ+A$@…fIV <@#0fMjD'V7'c@{Ԫ_"I_VD)A&i٬\-+g!={2SȠ u zqQ'|1;8PBiQ ~5s`5Qro'w[,6¥R'=C?z5 Vpm;X>D9@?^?'b0acVf|>р_-/ x\H"pA|1.}'86> @Pqg YCޮmT1Κ%h d E uq0c\kK+b4,Vfh^#WjMUc67OgTID_<ζ:kɹ)OزT9 񫜙ш f :8YmE7IHH(_;@^I5$|.1 ' ZScsWb)QWcC@E(v-4]太j 4Q|\(hE ZGWQ0M#+iTޤK4qb$ɽ+!~qFȼ4U.`PGWK|+i>k~%ljs(d$_޽ ?(QQD aSci Eb\\]0"p0q0ESw=)j]ф֯%`o"N.J@q j\]M3Y6͉DcIM.ep3E ^eSWzu5_B[QNLf~J qN@Nަƈi%=KjIHf>O{BZ-&43zlAj|pTurf/$N-izym~4ߒN9BWM138ȩ=/⽪c %`mWw.ޘNwDh, We'NOO u]w=!IDATxyl}?CIݗ:-mvq4W(ZN) mWMh6MR8%ʒ%ђ%ْ,A)K53133]uy;fgy+|T>*E034 @F >tN5! On~WX?~N+ [&R"? E73&Ƙr 7=W c2uNWLW<@ՂX뛈\2ј\jԢꩧ'Qz}pa49g@ !Ba'~`w5lnT{t1 GԿ^=ӥ'XX@zUƞ鮼;;o~_{dwK\99%-AgE^΅" n@{1h$;%7Z7g~Vf (c&# >٘L!Hӛ/p`c/cC/Z簨u3fײX1FjFJטpZ(Pu7O8(ᢄWsQx(y8S0Np0wU] J 4՜gm->q\E;l]" zLz mq?[enf9'h:%6(Q(PCi3n존g-ά.*=vPFF;:Se{[mƵf9;צw7[h%{26g\CAIuWho9%6g;ٻa.BJ2d)9 !$ ߧLlŗlEse\;bKwi"phϠ+`;Ua5@;!mC:qG-ERHR1msKʦGi޸j:*XL-$9Yp-{ I4s;("Av'*G0J\ZPk InސNmHY+FZ-2 %x3adlh`Ϡ5Vl֥>@xiW`b9^9P_,',[h5*$'_h ^[D`j*歙 ޲f"))ؖN0߶8]T|p6 0 ƀ(^$Ecf9dwli7ͩq&1vL6@}FWD!V6߄=v_N3yC& 0OYMY hOVwJڗD1E.@U( nf~l^r';`E:-Pps*cBU&c.>t-&|ut$-mj8vUm*YR"eЋUy@S"jgټ|K>F[B^ҨJQ[4V@ (Ѯ yŮMxcTF ظ^HVfEfJ@( ^,ul,{57cXJ]70"“F64JL)0\1l ilAj(FxW'ZB{VvvYԎJ~:K5a ^p!B?@HԌ(Q6l%$KCg4{ Hj@L-pd%z2Ü5_=~6҉r9'6*PcE}1o|"!I Y!X"b'33('|à'H$-d <ҡc| [eFSt\$b0ԖlI[+M{4m-fKuM|3ru2"d VmظxMrN g ϸKUdW\y FS&fTbo%DJ ?q3'fImƍf1 6$To5hILS*b'泠y=[|ݝu-`/Prhnt.0ʄ}z]lL(&+1)󊃌@li! oN(kl46FM((߅=6 NmufPpsdK#ک2 6, : @qĜ &-)VKp8ܻka޼'1+,}Ń`ưy5fM{%m8ZP.&1IӘjELQ@ IeU}Ӱ:$!L+*/{Dt[vˢvJ^0*{[Q:Df 0emR$SXamqsҢSxOuDV X1w'3_"m\͋vqsˢK,3Fr * eIlUf-ؖPS(6l^/H&gp:&Ju+ yiɥ,iYˮسlsW*unW4fb ׄh"N?R;e34r׺cY;D@2SU|(8 2q šGsj.]+eM%.C)Mi<*'7ĊqOZܦ ˰R\4Jσ ai#jU` }ShG8r4rej 2Bԃi±U>EF-zl\#ZܽG *OKs-huoKBz4|2wcF߫y \L[i;~oPZT d=8sƊ )֭̓cVOP7`( l@>hxAȻϹL8c-+iY`,+04˙(1ܷ༫HWrZ_>mNPSZQ$Vj8$70Va>\C ]#cS:Nכ-CaRƔ'\b[E͆j.&54QUB@m^Uw.K/;|n,?@ʓ GYp#NԙzGk ?phApm?ͦ QW Z^DO@j LF~>#0 M4b1S3pMN0hڄ dW $0 ?V_ڂ=n{ex1U&[ ~F`VV $Cã쥀z蕗)r3|+uoEgkf0&ı\0ȫ*(|Oss%/Ƣa:x9 `/}p[A4]^Sz睡L8RꃫV`Tm0g 0@RTC :RTAfxA%`!v%H^Vc`8Ɂs?`mRXHkQG]OoD*pDQ;-񞫹swacIԖv8D ^|.@J^_`D"{GR]: >%ǣrn?gK8>Otw^o^10Xߏ 3Z@mִ -n2dS> N@S aVtHgOFsin| 69wa$`wj{ R;A4wsgxKe{,gw_iQmpA2i3mO*c[fp"' ?zOZٖKfHmǠaQZ7v×^`ϳ- ʏCs"}Ժԩ:"&8Lmuj|KTpAm_\Lx8n?o|"q'G9 SFr&Zu6N0k!"S^5E1MM$@~<הfZ.wX7QJޅh%Dz.MˑMWs:A.!yEu:NbE05מbo+~05+:s]i;1";Xq&(.os\=DQ/<]1EJ5^8!XdpyXZ Tw\Er方|ħA4.o_ GsɖFsl fꦙ0!g"/u~pX Kڌ ;G-s[R?Λ7i:y׎1^7b1`F||c3W hO$˖-[Ұt^*NHXCUDjQ*e+CJKZX `c`A\IK>%: YƁa` 2Z&# |PtB KeE¤ R$5?Ƣ}D֓2©. "ëV"[L`:b<T~MCABnN%tEXtdate:create2012-09-26T20:26:02+01:00%tEXtdate:modify2012-09-26T20:26:02+01:001jIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sk.png0000664000175000017500000000655612625666430027162 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx{]E3޻ݥ-DhcF !UQh 5M0HDQD(lij+-Ŗؖ}l޽33s9/s̙sgᤜrR޼^3ABw Б<}odL$@@(2W_}-Ͷ{Ru! t+]_r1R4ws=ܹ1VdW\1+ܹswwwnkkZiw."1bhGwիJLB=-[桡TOOlz(`MKX.9L榫J]uxn=Vږ-[6<00W͚5.2^3]EkM\_dɒヘ$r;::Nioo'83|!"8C6sxߌQ^ t8lq26+Ə9usQ)׊(PJnh @$2'3 pHd "S1h|VOERIIeP,BR h^O::˽N^ddB0Q@JA{;<8f!1pnY^kF.@9pyZ…|c;G, &σBΥ ^%)@09Nmc`遽{c_#ik4Mb&i[39 o_-j_I+MH#ٿ1hkq"¥>pE ]v e ,\]]q> 6C/Rp`h3b\;&TkKv$|o(x^bq|~m:Gx ZwT&-2ՠ5j 8Lߗ)3u])w O d߿#h 21 NRDcc yGZqɇkw80{vT'8qT DmSXJj-Ajm%h))6QOP>wyl #sS}- 51!`ռ+DJ.PS4pd2 |~2oQd*x%Nhk\\J T!=88 FbtYՕE}xN ?!}}oD+9su|:wv@kߝ? bRh^IFY)Ddw믯5]i ՚ohjT LL\_#Egz `[IMV|֫*vMjF#ij"f"@gG@P*ֺJ(E|- a~p<#/L|XgՃk?Z9R1‹ًҠ@! Q p?q=G65yw{8EƎ5c _?\6d:qÎ5ZD3k0̜+ z/7x1B lyTdo󔽵h }D71NnJx.s8w `֭Xp&\V|aX>l=IJO+Oi x|/̹2HF g3xVNQb#UmS~)pfJ%<8_8R cNi$ɾ0Z͓c>WJ=l8rs~]2PSJx6 5,k&E,EJ. , n:&\w2ڕJ=I"A2DaLőcSbf^t&uQ"(1hh;:{#hcP升ɵ\w-lg/x'CgHN|Ea"}}0U)GJ(Dl)ZϰyrrDkG}M>b h*sg%*PVmK0|A0xL0N|Kest+MNvGACSnY~n?tqiJ\9XYŬq1]dճե؟& Rs*y/@ !/İ O8ME(g 9_".۾(G 8Lă;[=Oh9;ap~[Uء88fѯMDk,!̪@#`PD6  .<=w\8uMlyBl8>w%q z3_Z K8ʤ|폨>KpnUQyvk7qO.gCWٱh t"YQ|ٺ5&^mjjbF6jCSbu ##ȭbCn_^_7/h |<_Ckm[h|ZX|&5yI Cϧ.S\~?p!ccMz굫FSEZR4 /eӨ0 r*lhԊ+T:aZfG5 /IWDEj= "'LHs4LE$8lOٴ V *z :jn} |mT"00:@Gq0Hufh )0aCl9; K@!`pjHMp_!b}]Z𰵩.&Çѳb3U$bSWtP)י*r=)xI%tEXtdate:create2012-09-26T20:26:39+01:00p%tEXtdate:modify2012-09-26T20:26:39+01:00k-bsIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/nr.png0000664000175000017500000000667112625666430027162 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[YZcbsc氰Ɂ HP"x%@!Cy@!/B9J0,`_`쵽^]=]{v=qi{8[Ζl-t= r8d4żzuҵW#DDMQ#TCC[=sWN9:g0P3`Z6O w O.]3!>`r{}q{Z_W*B:OB!91s(PI_^ȯHiAjF$2S^b5f vKq@KAfy+@^Cy׽`P<W,$ pʌ` m]tex* XI&H$Vֽ"\s.8m;ڨhHZsU\ Bdtgک3`u*c%M aBOEc횻JGVKVc(e!-mǖ@i0m3ӈS XY5֖Ph+d3FzZC{[wn%*lRMƹĢGۓo=[sPfY4h"N+ۗl3 . K5aj] L$AH Di6DFGf Z=a^sJT.M^ yH. RR+Wrr.d;*}/Lċv')DRf)eljOUִQnpnmdH3M[ƎYxͪcڵXÂZ@^IWdu M;U-Tq@ %f Ǝwg 3AƢ!I%nvܨ+b-fN=KGLq_bG '$<"x}K#(%=h3=fF!HB0ho/*:*'(q.wlL=Ccz1 ̌HGp"zџ=6P2TO<vAjG.do.3Ҁi+U{311o:V+ò%aj084 R *kXM4>\'G =Ce9tjɄf$(z 4!^ھ~ZHftձlq'g>|pxXh>ICJl1g+cOKi.uv\4n:C>}>{* |M}篾7l'o_q{RO}v.4V#)g&mPb=tr{W[6bמ8 7P 6X1yl4W,T13NOڊɩ΍ vpO⍝"P8i)pQ"x-RDϖ:\kC9-pbEŝXz6^6^X,wv9o?|#[*qCH/p*z(0K{"̜fH`ei`"ԪϣV`,\0yNGDi7yl@ P!dJU8IkB(-{y9e;Oĕ_;➎li!V\}f\fy (5Q\cQLjS%vx.*bsɌz7 Уrٺm}h w9[ T/5@p\͒xFA?zOm݁G!<ŝ Yb1KEӳw#~u D8ϵ\ %y±)Oabb (=>08Z~81Slˋ&R.f08$@'SԛE.>y)@ Ml3H̞^> %8! 7y$HjA8qID)(O֯e=Gz#) Esx l3*rdI>-͹9?/@${Ɖ``Qb'CY`aB,٣I_m dž8J.Վ`!rŧ&Bx0 09PA\tZLQ90i Ddž*SĠV * c2N$s*q[oLDd뚈@AufP$IvҧP;2TV(L-{_>$ ~)%tEXtdate:create2012-09-26T20:26:24+01:00 %tEXtdate:modify2012-09-26T20:26:24+01:00P-IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/zm.png0000664000175000017500000000667212625666430027172 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[%Gvns&3d&&j"Q DAAEA| > o_ >"wQ1o1(1'sIΜ˾tW->g"qj8ӽWkZU ʽr+EK E7H֜\<4xhFxP};狷# ""H-퉈mk_QU߯Aj+]Tgo꺙`rs5` L7~p'gӋ鸐X#zSXfT^^:yS [k-媲ՍU_W_;ku X9q`LoW[3g2PE KA*+N>ѦF]C +X-m ֠Bx[_S,HNnFj{Fɧm;˪UBDu@ornZQyp:Rӻp( aU^*R9R"o\r`FP12=A>P»`l dot)ICɲD&P(F2߫^N 7QGeZ=#a,Jor}#;WN=QTZ#XbחSY`.6Ue UCӬ"M)~N=:k}wP ضd [X#(nZ 4$gEt\knԼjb"*^V֏-X %V𴅿WaeXU$.$ʲ9xL( 0k1k,uU3%X]蒜 -}Yy%Dž iu#>q㾯 QXch>x t6$ԏHf>[& zZ؜{`-bp~&&Uȫ>MH PkI4/<5iM邎l \ȧ2]y6L0`9aSv>P TYd 3Ыk~Q8lPj`puo4H!t23u 4ZLw?LH֖M]0iΔ@--[jʚ%y: s\8Q1< ,L;}^_}ܸ$E OTyXXN j;?dovjAnNY'˳ڦ(6L pv Bme[c!y SƢfrhuN+KN78OZ4%Z[Wemlo1V"Iz&7 XF)mݿe^<3 J+SoO'B+VZZ!Z9 _[\|<fULFqi[uU[V߼DJ}J\+zmT%fB&VR9aSVNV54zG+z$: 4tKm0L'"yV (:#Y ^By W K_>{|s+`r0~g̊'uRTFIDATxmyܷj{~ 1( !4%iJ "i6Q)ʇV)i$jUZUT*E|FIDҐPAQ $!16z]޽/s<0/ܹwX{3y9ϙ{^[PFjk Vwr@3V 3;0qryJ)RJBAp!L W~9x-]H=[ վU "i?Esx5ͯxެ- }}-zXJk"ТZF9ZZ)\Ws9qz&k7=xNZjl\9X=áV dDBc\$-a[C2W4ܣ-}鹕1 @ -[>],arZ44֊;g85J;r]zjo>:Urn'-Vib$hOXb03^v3:bt$<6Eoz~HKd5Q ޤwr*W(cQ4(Vʢ )p[wh||']hbe83[T_G?0ɏ-O03WTd]1F|A!j+=@ \!s=~[7_RnU~1|_7@[ $^!s4'VhzzpbjFe>P.4=zٹ*Mϰ̹uR[i}1Fu$`DxD;%+PЊV\Xf怒3V(@uFр,𔂑gx<2Mu/Ru4>|0T%/P* .w޼gVM{FP (U&e# CUW(d(ęnj`9Y$0kO:X4y4 5nq )L[/m\+'O㺊dr2Nl}MPΰA")兤=!Vx !6aws"."N[#():Xderъbkc|b|Ww0{gPZsN]euZ7έ[66#ĕ@@J%xiEa'O*Y*huG<ɟ|y ͈NLK[Ȃ%ԃ"kr[f(i*Mrfwܴ3s+ <>xneqzrx;3s߬umHTJ/"cIӰhz*"ž'_qxvU.; En9JCkŞ2 :׾O<;(] .ՍH[cÀ 3FTҁe Y(#>b]Ŏpaq_7dtz+xh &GWT:m7;<S3k#WJAʴ٘Ċxv՛"PE:5zr^kVu5W'muha|kZ!& P;&箽 LBE,Qk$KJ;ҽte`A :eZkͧ^G)0s87b7﹚UO Gk٨x3ӷORy4=w&wYQ(%."J-2 l,M0dps7Wa,D;oMg^AZkj[]@eJ#*|#h <*z|Z|N3|_ȹYK%j$[ui ,6f6Qa+f9hgJ`s^֫N-!`$3jBg^!wȹ#t8L8Ia"Xg;D+5 u''+ ؈p`yzi~(_٨8wZ)Z1@ggVxS+k%)/&dSFKv@{o8y{/7|^ o8Aio-ƻsѰYl+ns%hy=<Zo." #ͺiH;>BL6y#n!B݈Tx]כ߬ 9jTiN].b$ #TpeVO7[smW7g^_,jQEplsJ0pM,[@]`o#kѷ*SvwpdEƱٕ'UNdži<%#v`8{w ٚqѼ{qbӛ9_40̇@6ltRx"λ ȁ!H,mdžQ ꮄ]T"@-` 8t8n@{#V$"_ ~C`<?6ƽ|;߻E%B뺢 n-^y;X0"du%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mz.png0000664000175000017500000000656112625666430027167 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxiUdfH4l- eY, UbS(()BTK ! FI #00[Gww_w%/3tۧ۰_~/"(ALpDЌfTY p8dmR^A @ tzKW ﻪO몪%3lm_' 2|)|os̝i_pE7%}xgq#wS_t7Uk:Rah@( nX}IygE / ΨzA%YOש#!~P8(@0FZS(zPt7ӵb+p;8vk =o }/!ylb8@`m[o\ U1(vR>fu nW2yVb):w__X- N9{=YU}+N* & B3 @Ij 8S4g60JAo ` } |Կ-m 5O󐖍PJ>@.L.+&)(>O7oo[i1CpppJb2EaZ;l!dPj:|8I8ls<: X#k5k$n%G;fTN0* cw5M^jLT֓AǼRyw.2y2NYrOnDr5̪*cc 4+YΒ {|0cTxx*qӄ P6o/5-کjQv1P 0aFlKzi?EoDpu=\8<HTX{{ Kl}96cLñxXS fzZo$SL0{7nVei$,xilPc-#Kr[a!_U=>]2o>#Yp2vۖb?v-7j3q+TA Dr&+[$,pI> `$dfsZvᵢOShMLvqLc KO$ag 1 4 0K`9p:PĚB!-%u/&Ҵ ݀4h"AgZN|pudԔ-ujtO>šqiiƪ. Qͣ4s\  iv  g LRk ]96]֗/(&sn\\&pbxG524Y^78!@Oi=d3߷xwe~OEEt2Q':4uS߭-%R>6L`9}ٓϜq^wۑDP/mvLl}y`JlCLB3@_õP?4J U`(7N s@ \jϝ@/FCC#!MhwIJLcaIhM"I6x H*yo'ꢚJ%tEXtdate:create2012-09-26T20:26:34+01:00{%tEXtdate:modify2012-09-26T20:26:34+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ro.png0000664000175000017500000000602012625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> NIDATx[Iz_>1L.g0( @_D#(>|c> xySTd!ILNs}CVݺ{d`)اZu,/ohPN͸8hZi[_ZA ufyϩϐjDD?|Bh&|oˮ7ۿle/s ħ?^cxengl#ۓ`f4_{g] % ?J)@$ye?КpCӕuK4ry|H7YQh0,0aWgm;m{S7WO.h-H@`E\c:::r" $"Fcȍ̈́7[u븲ޒ3)7h} Їō ViAXj3 kt]lj;+W+UV!@f8>MXsZ6NNkb͸D)hH"XxRuLe*JMt me20DPC Ca~ٹɂl@hf8pfQ:` V,{e)W#ar,2G*aRd._RуW֖MD`n\4 ,f/#rZZ_ LJ{IхyXx*m13S>9.D[REq uTJ;kz{9*_'J)K]"(ũG 9d 7vR6(J fR퓚,KD1!opU3uWM8\3^ 15s،qADeU@3D٭RvP~ {k>31A̢?=*@lV2'p3jF0lts` -/xV/78SA & .QY pLo:-ZpQ q/F7dq&S֑S)D+Z-w)ڜ,`V1/~9P&~;yӒ$@vVDŭ &~ >HlwX{Qg60gE-\9,F/*y]%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mp.png0000664000175000017500000001414312625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyp\}?ͅ`@ xR<$KmYEGvyڪWU*quj+(:a)YExxH}ǜc7dVz5 u޷_#|>JI3Ðl~$fN O%,4 :h.\Y)k{@! X D0/T췷W{<@e~>G)P ,#a^lEz X tx^_'>Pw!ҟ"y5l5?<8 *mۘFN'0Ͻ@*Q/qo Jk> T.ɟ>zb0)DFM{P)sE._dZ&Qь(Inl00YxkwuVVDytUn՜kϜ'hb^Cܝ}y3>G;B5hEuҴ?srq8@YrČLS!./($l x2lVwADGZ8 RJixT &1=l.5=hh?JxJj\qr>t^ZjG3=lh܈fFXr;_"]?9 8[e!^<1 Mݚl UZ S(%3P4ΖOr'lZq H?#џE`bf=[P8[v7{\EIUGvF;9ì/'T=EN.kXz%rG~IٰRӊK,YByO%']o}=1c/k~l#F GF ~==s<լ0A.&,l)ތuNfg,ZOs|xkq/J3W?t=9 < TNfLr?T3f91y3WiE\6\}4e%H7(xMG! ]n!uj?IqY+awS1s?mH/G7Ⳇr;ifjed~[XӶT-qSsq҃Pi/MI[m],0xFBu!,B 1y\)f|뇿fh$ =v`?Zg,瑹 ۶rtjrsM#8jax=^m^9[/B]PBܘ \.465b&F*I8q﬇J7 N7} 1zj%ĶJs1-(U*N: -x\1*@Y[%$'|{M Ow({;V%Jj`D& lۦ6TKUUha kA,,7-|yc1^uQ^3 #\vôX5'ؖڕl4Po `q sMHmB23-f \=Mnߝ sa Mɽl" hllDA(r;L{ J@ap@1`Hh~4Vݻn`GC5N9ZV8nLAjũQN₦y:'44N*cG0i\X[/Ė2M6:K(2FXY}۷>~jPORN.0n`O #eUYհfb?Lf7[ B)lS `jxb\VS?eL|@reB'8gn{p3q Oc6)v@i?PL*UW-[9t5hxdM%Ģf+iYli[B4%J9_VOPK`&V KAO#T[GBceIS4 T-}%foJ,X+}S9I&ڼkeg fwJ .'SwEkk+/ ٳp8O:Vf,[Q2W[WơcbܕzS|G r϶#{LY,}6a?rn4T569n~?~xC1*:=tۼ587;n?˗QJ++cǒ*t6X)lڶ ϝ|ɯ'rqiSlNp%ld|SJ'Um_d]Dh=s?vZ*HffDNe>h^>^y>8nMp$ r'J}B=V7G9rya]9gA޹^< c+R4m6|-g=U\;q-xu1ۿʠ ?=ۈthqM͍s q)mHaY Q}$qHq\ʫsrwj@Hl`;5 ZD \WUfW5ITfHZk/~Zjl2FRcnip¤ H$JQsS]S7_`hY lm*{pTXftPNx d (no=>9G_{v}z8{E@D~^t"L13x"0$Hoqf1N<Ѭ]'H~ſ޾h9].Z3cqR:cF:q4+'9ʊd@,7_(kz`ts1bpB H‚A'QVn~ݜa|j}|^cfnM)#WRdpx{~@ǑC[{;YS3ZtK)I)eVh{&"Qf 3Oʲ͔k`L*Rqǫ cpg/@T頕h<~1al0A4eHª1W,eӖwG))HёQ"]Ϥ\J`7\Y.N,(нiu[ WhFΣ$i`MY$26 Gpg'SpX $PjT]1km~%Y-NJ!lSi>~ NIDATx]%GSޙ;3&baԇ`D" |!)A0b C _ an$nvfCꟙY{OU}uΩSu|:Χ7ɻT\H:8̀˩fk6B1/V>h+C "ҒQﱫJuZ;Tyj~U䥟&g9g@>mυ+_Ą#\?5?ՈJOgUEy+sVFU%=EN䑇~lD9 b.LudIa4a 8jGKHêsu;UUyڄh0N`z`>`*O9 `}/=bd-1MR()W;ofZ7ݔvUQFb ˖9 ~4x)\'w"(1Q)Y1fі$-wE'Hab(1LE*XnqZ b6g<j1,|dW~JTΔ:lJ_!ERKQtlRP!} w]$]р:Zg^\q<R/ʨ=10TpM"EjrNӔj&rqbЬ.+b\ v^&%WjL\ªt=F]ҕ %ָYN䕽W9׌qlRC B );Ȋ>hzH6+vihLxCBpe VN< P^딡î*@.POȚ˰mnw Z?ϔ3a s›Ҍw$Rut5xxF &Z^`.O5ura#`h]kՀ(OFH~<,˫^VuUbH %!"XkU 'Jtd;kd_C#OӾ\5EȾ>fp]B49ٞ<do5vG#% mGcetcT)0Rbg$t֯ݘb)YSu,;.Kա\ސ,yr#E, >1CFA}[o䐽,WlԺO1)ƟB&z@%<.|/W+oOѷBi_eMT{R֔ـ{g)AX7;ǂܻUg@_*5H+yO&/IѸVȅ83a%_FOIZbDOԀV0 #ŬjIq #ck {R7e2zlہp[Rfdτ$Tal]b ߘ>gXg\\@ka4ΓCfOI4*ň3F+,Tk楀n m|$q+n*fߙbO87ji_2 ir7nham 'BXYbGg`ªW '/]s}9g"iXLဪta3|(H1'G\f&Ԁo`yvpT EPYX|dZ*ͯ@Ca&ͷ`H>*Ǫnba`?q9}aJ5}UJx$\\w [?oJJL a ܍$xtǖͅ怬vW w37% IDATxmSUs?)L3X4 dLd *L|E⊂"1Q]E!Q 3vӏ?n;mogP#˛{{ܾpUU*".ӹW9IpLLug@3leFh1iҤITUugB 4MF8k˭4L&ӧOܸqSN ;At*p-X kwݒ$Imx41 D"aDѮP(sÆ':ddDAo͓ Uuat}ar6>g:gTREYC=ixϛ7sDuu.i![(k9߳YnFf}3{DTjH$&|uu}5Mp;d1TU4Pe9ٍQ`tq0&k|9e #,7R@*ՌS#Tah>E$i`ϓlHYyh# CfY!悳#0 (,i^ TX](P}]LO<}ʊ7=9N^V}vٲw1ztNɹ[ADCv4~7?Bd?3F;YN} g[CNóĎg4ade4IK |~ޔͱPWz.EB3!(hJ daY3 S`bA4u MKLU`[ NƏB!oIk aX}bsLt]'Lq֭~o/ 2 ~ۭ"I/r Z[cr?F C"0p RQ5ý9X.ynV<ٳg5+qiN~)Ν=8cJqPL6n J (98˰hWT&ߒ|a/A*erL>k]D:w#ط/BoN430`p6թRRmsY'}w cjU;)^yݶ,K/#<\3gzX SY$T-yEFٳ}yg56tq\'ÂLrg,X8 m0`^& L<\ xV}}:~̂uE6͢E ]IKM̚a޼\~XxMx-[#4 ^3~]٩䓍Օh V˄ .x~c¼yuzfU@9G'OG޽}E YsӫXj2ձkWW Tw }{ 6e?^xfin]dŊ&AK*I$W 1ɛo^ ]v4MA?jU;^U݂vm2)vF_O5[F3#Q.UArXAf˖ Bĉ*7ٳo(<BQw."I0m/槾~Xn3fTs Ia͚)xGX453M9ՕGXXq66TyF,ǏǸ?;vt3qhjiXw5jkkWzP(V̔)JWQ+}Og8u* /}̝[p2[bŊ&|qYM`6aF'pnnm.H@-cTU i!Uf0cFUc'BXZ6[" D<~<{P[I(tfegzJK&3hZ(==cE<X>ljG~ SYQH_b)YV"j?Б,'dV:ѻaf`J~`af}~e*`y~|9ye6JGPOo W{Jl> :^񗢯88ݺm;OI4TzѸX2Z#Gk|9>PamͰS4Є)1˵QF#35L3i&MM4͙RD vv|Zۍ$)HRi`@Gr70L C#H;B;:;D'-ϯ_s&Mr b ĕV4 4M#sl;:NȂC'7 wg{֬Y ^UUXxvT=z{~a-*i%~ hV?4555˲N_M!z# ~u .A&ʀ' 1ֹ fF= ) t@W^fu{P]8,w$0 Drqb ˽Ȭš +we{rb낈zL,>R/#StLp:GpY"Wr:fQd%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/de.png0000664000175000017500000000416212625666430027124 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏF?ٙ& IqBCn\r!.H8r? 8 @A9ʢ-,ٙ8S]Se $ˮǯlw\۵]۵U0ͩ$e/dy6zyˏMJcb1"']?^5[OUכVo־[ݦi΀hbXebY?4Ƽ\.ei1b[ s~ޜzCmUUD"򓺮Urb "LQ?!łј.on`q||jAQ4͏UWtѷW+"=,뚪.93&"N9)S:Ͳ,#"EF]o)7DeY*jó's!u!Sr"0s"\-15C "FD6`ŜS/V 42!p8k}vRD2vͭ综x`K`cq$Rv{9GK B ϡ.^m& 3g /rfT}Vzz FrQ@dٶzn3W8h姺Z/{Rg7Ln! ;ggkeN%%= e "ѺkgD |SRcQ;t"_t=t%sTm4V$uK7A7ƽ;nbd +P_cF=r#EtZڗSU [wc*9 ,]ƺNc͝DN¨E*p 6-ШRwwEusdS@*Mb{;A†91bes"גssF/uzy0E]N7>1g}Vc .՗p^X8"}9fXLޘIetr"6[8:=ġG/Qjȷ8buh &Xrzn!捁SnPN77lqρ !!5s5A:&*-C 9>ވoٽ]Xbw;ZMƶz1X9@GPߓߤ$&k{>N_k]uzJp}F"1([;YH".FXmmP=NG;n ,[0*Jdp|$,QtXYwGc(!gmfրN@xyZ ̪ d Y0 `hb?o=jxFh̠͠ЋC鳵oi@@]CaZ!~es~C0\:MnvvG¶Eb Hu2|ڦ7= M ]-m$٠u~nDi75 jg蓓ͥ }U;ʇzۇ1]7.c82.BS?=  ͷ􂕃6<ywzJ7q6pz|d*޼= ck`~XU{zmn.Jmo;YׅkGk_fK?j,6}]n~:*+G_/`Ous 36 O|lM;O}1RsvjMMScQņJ;P: tPD;]!s+cxq4@^n E%#95sZhOXl}鉰/ܒ͵bW5^xۥ! $WR m_"%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/as.png0000664000175000017500000001242012625666430027133 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>NIDATxY\YܭޫvvLM!L(!DJ@!/D"x@bHD !$@6L^րZh@w!0[q!#BC6=Ku%ڼi@!44<ƍڄGF cMj\/=y̙U nWm9a)XiRJ [6o[[miFl%/ LRkRZii`f8}yħ1,.]}o?Mm"[X0>"DL˲h +$\Vl؄ԄޤHYᑗU2Cp1eT4f? >:Ӎuz8w[>{<7 91vD ӌU7U p{x;VM`6Bt$? Ĥ] ƌ=JF{Li0Ml)҇fOta$'Oko<ηm}_۹NcBflunnn4(5 Uƌ"cDR  A&f.Kl$YS8}ZTN 2I$=p*rco/BJ(RVXÓrf <ш8BF~Yf(0n7䤏%yZ6-\P?əIRfIN1F Xȹ<)~3.LӔVi Jo( [_dZ[^X 492 ʌH,BQ+,̶3SfHL ֚U$Wp;OsY:+gMejG^}߷'b;5"&hc3i(u MDO9cB:FQYdTX*Ph6%I"5wya(8/rLG:Asn.TZCD90q [ 7&-eYMȍ\LXmQ.*q ;(Î $=7Kbm:?B]|:XBaD!@H|p$H1JFRahljro1L UF*f ĠQ!gB7[@ = w@q拟D\y۲b=- )S\% 4hI.( >|=*}0'\FUfef:톏#u3o ʶr$GH&}]1iHt;kJRҖ/r gOe9&胃AD2.Î_cb.}IRJL-@6V{!Rs3̑}Ɂ^\楳t4(z.@#&nh\M v Д]/G[gWxaG򑁏lң#d$a3{1Vժ< {"%$$d-pUihanBkjfꑟ_myju/~WcÃta_ǎ>0Iy)Lc% ZE2lY_ij&)5YTgW-*ZZFT"H7_eHf2 anՓ_pKMs pč5L)I1AjRcIvKc 0"O#?|{9ߣ{`"BP((Jq {-:/FI {!nUQ~i՛!aZ5| EWs˓A),'mQ7ڸ|2jʏƏAVW|* ҤϠ2@c )U_c )4)@SR3I4QJyz} 91Y´E<|>O&}5q.$[/Xw59KSBZkmahQ#OɓIml<ݹfZk._LZ\.fXE6%mG>Y&2Xh:cr5Ʃ!Tr]ZS`/~`Ur~}d'xu|of}ʸ#է3g=;Mlj;Lbba??0M,q2^OVuRiYfNJkK RRQΝ4FV曫'^hUX/a{IL>v9؝/)P!mmh16_e(?R>ˋ?G;Xv7_Kկҕ$ZY8va$ۆ'Xqe۱֧,1lxd2/psO`$8}I=]~sam:7'NГrE#rIglfh i˟dmXQ!:G{8 -ǏYd,#D-z\hoD#s$h@-;I`gӔ%*$:eЙ%\ Up%U, CU8Tu*/-02y|ueR oT0LƌyFR!s Ob$B'I#}kF~ekL ρQ,Q(+=ԥCU uVqYԫ%h Fg"Dk45mqK8簄&-PDKt`V>{7#>@iynsXU*+B08`zN9F4SmDmP-9>hLִ-^S^Y7>x<əI2 Me7r(7]wcn iNg>+Lɤ֩wP|("5@ZcFjz`H>YePYP(2ՅsǛ1tf#diS@Qboc'Ƭ[òN~cKӘmxG m/xV`+Fd{un])/!^Y ]XgtCRr6z-Rh/Orf*T9hF"CbjCfcuoƋJKm+v؀X[JAk+tMSwݠm7_!PXW O]G^8`7 ]NY)SY8s'ô)Mbr,ɃGѰ ||ϧVQ,XZ\,ϟF7vkuL _޷(IB(I YcH9`:dur:\#8vOS$'DZz!,-.O'8\,J@0`C~۫N~شƢC "% BUDL#/^[xbx ^ J@70`P<6%eb+m`)4 |FmR*к~ܨW^ց:-n@GO;kyY@k7 U&koǵrܥq Ԁ6˝~P8و4,ZEcXpe="l$A%tEXtdate:create2012-09-26T20:26:42+01:00,%tEXtdate:modify2012-09-26T20:26:42+01:00c?IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ai.png0000664000175000017500000001177712625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>=IDATxy՝?V 7tC 6 "(J7Dq4x!QO93.Ɍ'9L蘸0t$qt A & o;ԫ͜wNo[_W|%Ee՗/^ @d~,hJ h>FkVr˙%gg W_&^}uR 4M"R=]AiA~e$4G]:;!PRq.aX>>AAwENp"1,YAJ|J+B̈ef?5͖n  @y+n"hύ)Pq1ÇWS^^ʕ;ikF%B$EE9;wB/(@&$ ]gؘ1TMQQ It,{rգwלyQ6n,;ɓ)V&OD]VGs=wYaĬXAB8| %T(0dml?v;5[>C|. ,`)_͜9tlzRV^\KYY9^:ee()w`pq\Y~ ?%s+wKq`P>9`7MkӴQ4B)5rrƜd'&^>>]GSisp%8b+^K7rx R ,B,5 nC!5P8.899a.u.ͮbZe9 3ŋЪbC,;lt0Rf`=osXoF!g#DT 1&Mz09Ȕ3mY1Rjņ]ö'$ x9.cZ.^L7h 6wMWWhYxLJ\s֨!,$뱟Pu9Sӟ?2䙇W+[ijL\&Az:,/6 !t ;ؕ0@-|s( %3 !tۣ|N(Ρzhyu:l!TR0s,K/K~IZVD(B(f(0r͋4?$V,Ơ;dC:ăջDdp@R p_ f"&.w/PD8':)M4'=@ WW 1"t=P \~!=={yXm-%7ļyQ ܯH"@!,ňaE,W^&x|iieYnTT&' 47w@S0ɶd`quLKxB _~Xm:BBRj TXEv6i*G&*G0xpmΠ|"Ȫ;,E3|JL[[v`fNwN;r TK'IذA4*( EXG&#hszHD^@^A;)ˢy :l!\پH-`"4 /h~ҿ+X%9@n;vKIDJR"@Hbi;v'IiOmJ;6)ɬ#3]lH1τOSb,eHlnjm^&`KQ&#Z~>"¡aM#ih~ 2ewXfs KK`}`tEђ' 1KbXrF BA~Y H2iYÉD[{/y3fZZ:u4B 5JHR}XJ;X"5pυ` 3|Zigզ:JKa@^p]R"c?nh*lmE&BKJ@JatB>b srUUmO5#*D#u]6>`zb@W_A5{XNiyHJ r p$x={):7m"kPJΩ)>0p2G&Ǩm欳ʒK?1(59iM#+V|D{[$Y6s,2S wZ@8MʰPJCɅ~Ϡtf[M˖qڼ-{<3FQZK˛oz1HIr~3HAY1&M"0Nu*-j&̙α'k[dei4s4Q*Lne:vEg:bCpE,^< 4ܸS>J5䌪'6ag&~m<{^ M+!w$״ZEOR9yxq^|#;Ut4o:=IF-K!`1wOkkiXgEbݱȜ[x|M-/<2͍HW 糶o?ʽ`ʝ|qtyӦs4.[LL\)7@YV~ 4$fSYY­^ʂQ9q|G{vN>VίGvl%&JK|ܩdG+WdӦ̛w LOS4.-g_r;L3˶ڝV20w+48{8nq"t~֧ P:G^?|@Wg]l 5B>誅j( Cg+ c Cf֢zO<d1q?_;ϛ:Ggr M;a02\dYP n-‰5@n,+}Eu Dc? fcfgߓ 4gPbhCg! o;tw>v j䩈gL858 w;x76}ǎňF-p-v$V~I(wKNYD&y_ {׊MߖnG9=](4hn>nv\c 2A@f< y46q`J; ، Jg⩢p-32?Ç{gЙB|uy0h24no;vѣBȴi1U>㝜׼DL}h57]{o|J'Iv7fGcZyttdޗ=>1~zAsZ,2+宦9S.FՅRQyZY I7i\p.'}B@nϾŎqT ø0LVۻD`ڎ=?uX#8|/3n nRHe!Bȑ&l}/a&f : ^ 9;vڜ;sOʭ?ឯ}>}`QWW;8w竄T4QTqXGQ8מv&ogy)9f_ =u>9 x )']QF#MZg%ПrF LH={/ N2_i>!Ч am&GϜ[ @MPt`A T%D jS*Tdw`zLo4|w4 hkf~8BtBI@vp(j4CWXj>B_w !ŰJ٥4nc<| /hPJx-7,(l|B4-֥M#o6&/cPR4M)-e0Y|4TԲ:uw1Mp KuaG m/N5 M@=А^^6t@aS%jםDz.0-dBxu@(߬Xؾ. Zi;=1y@s_f;qt_>2|~% _bu%tEXtdate:create2012-09-26T20:26:20+01:00CB%tEXtdate:modify2012-09-26T20:26:20+01:002'>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ml.png0000664000175000017500000000507012625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> vIDATxKF{o?2=3LR " ؂kY3C{l" vH DA@"LFa:(]u|\Ч]\vr)r)Bt \rM#&(3/f2ە@S @67.f/QE+""+u~ |*Jk#3Ý}szf?3blyXmne^c4wѶgN,oןS\3.c`r|y+ 7_V#YfEp F6 , NYZlƾ2ѯ~A5͇X1]^~BG+:99bh0l{p=Ps7n^pe1`M<#5P6FwMSު~XUi|5ڷ^A舨>,lցI(!pPiXp-Y@E$@0;BD0uѫ߯߼4b) }K\aK[l֩'N%l-@Pd)x) _pL,Z D8"]a7pWȐ,4%h22L ,eaHisE/JS m*Hi%Ru6=҄1tg608=Ua}axbJTae\` yRHpxXx %AYe.(S@w>/ 9;"b@@'Mae dbqW9EL= F)C`iCx* I+ PvFDch S_3pA 5CP9 2}zha@gŒCp#`b)HdN5"wN :W()Թ_h]`ӷSJEB) @J e҄(.4"#5JRoӎr:  =\O1H(2-,@(H?F&ho2epC4` ȹ%L`]Ȗx=7@2u4ۉFF3l``ȌWA(67`2x8?kݸ7rX91f}&HMI9Sе)LLW, \g!*&J(L+EcҴE)`MĔL=ʉ*z+^P eZa R#3축PBL=>W ^$W`O -HKH&RMCWM,Yf0pJT>`nm2 fPNޖme9v>@y+7Qpءq d`EO$(Hpϫ)d'v'sF;0v ]k vl6iKҬӋzkӇdaη<ڐ)>)knׄǏ[-*IYR =z^*woxkGtz^95 l4|^sv~ZKXB5-=dpVY)xܿ?"1PXqoGѪ|=js l_Vފ2SqGɛu߮׿¸2PCpnWUÛ/s+S hq |Yx36Z{ڶ u 7!D `ఃx[plO@LL6nWpAu eS$pӹPH 5;5>?bdQJ$:W=E>eJ`Pb '֗r_Ԟy6"%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/hk.png0000664000175000017500000000744612625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>dIDATx_lƙCrhwNlNT$8F{NMྶ=zHE (A҃SA&ėXmٲ$%,K%9%E+G>49@fHt;N$nQCM -AN>47@@Da B{>o&AtI)PR{[ʔ^8i#W ؀D<`Yw6#d;iYa>וA +[&C{uڶTLs'l۶jdy$Bt7⟺R)I$"0V}AN$*__Vn`ͶR烦)8ο9Rٍ֗!ģ9!DβH6FZ\YZkZZ)qCrEM[#M7Ѹrt^)J Q"UO(5MJkMU6b5Y΅Cq]$` ARBv 1 ݸ `hs0(B:0j߸b@J (#ʬw}"L^~w/`Hi` Fi`$8> U $-t.!r>pV8@&ArC"@DU5QuU׳p2u&ј863[asd L] '2)Na7__HH 0VcFa]3 c3'OrX{}֕ee5,)XU !qHmo|#B.%nH>nA2^յRm/%f.Ƕ'0R)Oy я"5o 6<=wn^|m˖eط`Kld=RdÑ#)>~7j6Uď:wٝ; Hf`dwwearH) 7|]}a @ujK*HUЌ@p]2۷cf2Z {"ve!k5ri+!xT U R@hR d}tػ#a70 D"[.ພvɬ ,OQxaF*S*Q|^c嗩NO{ u~ak ̅oT 5xD/8<`f2Tgf+\}9Րr#:5cǘ9q}}#~zf/SO1-1n%FrG}``M b q#B},('sL> kאRbZ CC\wXr{cĞ:3<,V>.L޽S<8šKsgM(}ԤpvM.'Z@Wy>yRw^H$1v(ŷ/~ܮ]yTLq;u'z[k_L#2N^|VӴ` ΥmtlPt~eOt ?9˗^Jitm Rx{aDOBdo/~E=HY%3"ռ&)օ# 67^}KmJzf{ \hk ;0@%я/`rmm\}YgV*' ڴiQ&'{iCC8Qj6hj@N!_ !B a :+{C%s~d86f.GP`idW^aixDg'˓"n\Q_{=ڸaHfahw}_|4B[ Б@J_]wX*x-0ݐei%^'y lW0R)2۷S`idmOJ~><$m۶./y㍕>kt֪ܻ6 4kmkUPQ<}{vN]]XWJB7c,1rLj<i`Z@;еyO)Rb㗾Dn.J3+F*EP:=@nn׹o!m[]o-PB5Yں4} #խ2i?=af2*"F&}W4 Yv 2u+K##ޒ܂ .J6~i] ܢw b7Ɇ#G0s90 tDOn=?O剉 0@D_whNtl1(F *)e嵴mGg?U]y  ''i۱KΑ۽;YXpaO\E>uΚtc\Xw.XG+.,={H O8>Kv`aY\oY:X +Rf F:Ҁg+ǪFg_5BPg"YHld@QI'>vFj6n Nb?->~Ç1Y:zSS 7z;40[8©K%V Tw67|avF<懞f7[_EMt7A'kS]b}} \eUz/5v-)5[֡i,+64^f`ōWY`#uf>]- B̏Uv: t%o0r`nr4OjP*_?L&o'kɵ}AL YhYOU*ǁyq~6ԽL@4ͻhk4͆w}pxK7\wb\>8e`Vhmuh'· ,0 h߇H<ӝCMd0'?,EV^D@Ƴ4Oa`eV~^6$?v6+a>?V`5ɘt+buԀ%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/et.png0000664000175000017500000000777712625666430027163 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>=IDATx{y̽w}x^Ć$-F -IA}BmVU[E}DOWR)HKF<ƀk{w}<3w cߝ3~ssfowBLtq{9ЌfTY p\y,7-;תHADAD@covA(gujTtڼlO>[}m|4/ bZ{6 -Q߽[s8">V܆ꃎ_V@e%OQ+M#C4tc|s>R;1 w;z\.qK8R$:YDZjڠCtނ3zͦ3tSa+Rˣ94:,4uZtԲ o7טa[Mya5Q  }9u1eg&ҕ>^ 9yIϫfuiVhYD6zRֲxR *K?5P$/H\ qc9y$8bVmk˂'M0bJ (XMy!h 1h8F*pq@q<@XY: } QzFbEǽ$XYڲ(1WG^X\ J V< I$↌Db㒀XVjT[ec40Օ]cEpγڷ8Ț))eC˴n[mɤ\yI-`Y`v Ö*ժiY[pkV+cwYk]wO^CgEQpԕYfXC{zkn3@>S^orgh<؜ZHzz0or&<2gaUT ~oeiaD -|Gi*5{3xÅJ*7Pt-ܱWM24ȭ_GP;@QgSK5=<#R&F)Ʌua.{y 9F͸|>>8tͽj|,y Muhۧq2@"kۤ-\m}8b[)reu'fz/?:43xrOAb^QsKl)]/rr~{Nq:E^z^FB–;n(iئطKB@Es!N.`T◷/W3KC=l,r \ /Q79&08K3[9xk Axb|7GffY@P\ F xM:!P<_x:.: KY+\n k#н66^7X,ܜCp4܁ wSs+ C*_$ON]rCYHnv;cw v**u,6 Gl6LfV<<<-J,c?vbJVjNxz: D8(3=qyqƖ.z$eb>q.ktmvUQp,>_]#n QiK^᩵yr-2X.ygLr|r%v-&Fݶ#puiwKFvZ0v+Rڰbq0^M7OFS|n̻,z^rs^" [ Md\[Zb%} JI]C,0Pp ,m9̸\ .ve[3eQ;3jt߲b V.Xvk4²SH dr7giR p)/SSw[5u2픝H`r&vVU^@<4BF* FckVP&4]wB^i穯d@Epm]`LI먠$/ پg L*:zCJ}Md:<*dXjK iPW+ yKv>ӭnP[`MBEUfŹt1c o)FNWO+ +s;QyٗCp&oxķz2FCj̓}󩍞^GazMb EmGkd|p T2|Pu}y/xؘO,TUR>6LP2Fs;6ZZsH>Qzh%jٳ G=; . k64.@\~_\i I`*2h(@Mr@NWPցJ`X8M"Y Ͻ+7޳u |h5Z[,)xw7N,VJ0Dތ\wC2WjЌ IM&%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sa.png0000664000175000017500000001114112625666430027132 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{G}?3{{wtd! YVd[6?`\T$TJxR EH%PE r*b* 68&Oe[,uzN{s;:vg7_z(o7oQCы$&9'qosz@X7ml07ICĄB!As,Dy9hj~M䕏5{FtSGG`va ̎ƫ͏Gh"RApTQ|*(WJ]w[يRRŹXiB}w)+s PQ(+e1L$A YJHH|:"U`= z|\<)T(Jeڵx!k[g/xh !" ɢ˲ʷt\#k=t͟B 0)4Vc֋r=dX/",%XvL `TX(}P7V7Z 1[O"S89Lc`Swy>}xvq?n^};Ysܲ},_3[ΝliFWc0QCRsC6ћc47̦[f47r-i! Ztot&{.f-M3d,?];uidSVbFyIIv<) ~vfgfOJ*fL89}$a&ɞXKld"?Fs4ŊD.ұK7{vuLN}n?~PZ܇KOttӴKY,1L)û~'㦞wym^}t][6KRrɞJllL܌sV3vw3n_{' 3A!~4},U"bD31k:\n^Iw%=D'J<;Z_A2Ib2Vnֵ Ԛe{_Z{t*E 83;=>Da -{i>oyoE'J qevbFllwD.54^Ÿl+4^ҊO|5MDdQ#FA|b[+,e)NqcM@< s}6DLgv,a gT 5 ޔ>C|j?s gwyzS}U\ I{V/<z'\Q"8o1XNv<;8- Ҷť\y#q#daq=]'#>%b=4;:gKUxS3MbG|6, _#ϰȽ TfA! @SW^o B W)8_KlmoX{GgcHq_;M2Gd'=p: sG顟18wS98熮־/fak[y~&cli+CǾ3C”i~5;uq@xJf7/xZԝ}ȆXP&a|k W\_%t d}w6U<8ZZy`pֽ`M#ޤWd\=& Ԉ a GByTmTJ^آ L_\8zO1 szWʀK`+ihTkn]Y EfYEpQZ;ìk–d\Q#Ǎ2o>D"?iY;2W2/b͌ < yFR4y;GəJ!1e38Q#DGm%JD.s./}*!`籕-Hy^<V>u•_لN [s̾YԋqMT"]fU^R(/yYr**4Fع, fG` 7櫍/rkլ>Sü/O>Ǭi)y;O.PREJJb7 a`SDDSF1z9;5@"z} Iw}_W@]83LNkQ>]~_~Sh*YJynP5%20c XzD9oSJŽQbM*C@FjuUit: IDATxk]WuϹ{<3vL1 NA4Z(*l-BR}HU m%@PQ?H&2(! GTv&{㜳Wsp=M;؏o{m)7ܔ"W07ɀk˒B9/ GuԫD|o1y9Zv+ <7S*J4IU 6ֳh.B *շՎx{m^]lկc$t)@D==szo4Ex+ ;3UMtJ)DJ; vkyތ|뭥O>Z}h' s:%{3:j)* &fA|O:s^.Yց6= \8n~9^{"^`}fpT0LUjw4OD w1GH"Tň8^z^U1F*Vi]o}ߣ+82-sU13U1xA2&eߚ }Uhڧ ֦v0≥*1Y .@SJ 0b$7(&]vN9AHx"x"F)`Xr̊ Fh5s]gѴL ^KZj \LI6m|s>1)N:]&S:B+ez$k@Xb!݋ZK{qCٓ_yse9j`` -(%w/KOU }n!f-\yV+~`Nyxh&hqZ׸:粊iz$~󻴮,4_@|g/y.7 a|ns}WO'_凟/`760LH!(RBpⳌvA֟[?) s-l9tUUe<(K_.˟"uyƅKlw3}DZ?2[s5Nȩs3&?FNߚӄ\0v6w F羈h2~T-Հ,-Wm⹴P9DRt0GQ{O r#?q3w^Wliמxlfk&Xw:ם aU=Ti]tlU}iC[ hs}/rw(Sm~gsߜUawmm][0lZbsZVL?S Qs@Sa|pܯnLh!!x"S?pKhS+aYk@͆ej4p <-wy54{ݿOvqX@ Wz_7趮K4~Ep XJ@\e=`4XO>$:n;>%!Qi\x; liJk *%۝^Elpr"@MZ7B kݫP, lV ?͇^Wu>=$8=\_\+Ǣ#N%tEXtdate:create2012-09-26T20:26:20+01:00CB%tEXtdate:modify2012-09-26T20:26:20+01:002'>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/py.png0000664000175000017500000000550512625666430027166 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx]U3sݻ{wnK]l * Q AMxA&'_`JFP -m.c>aޝ;;s?v̝;g?E(墼EBI$isO S<^:4 ax"4X}R3k&Er 5N:MI>X1& 7` Sz_d혦$F^@OHȘDD@_<\Dgb{r!&i&Ъ6{IxlL`@ Z_(&[8:g&njJSx(%($F$ccFJ%>pR> I(ihndedBx7,nN{I:ZKJ6sRޒ?8QT14)ϘntSt6 El1xBm@w/[I^F_*@upQ*hjzq/"A2ammHEOd]H8!j{=[7hFތ2ϓՁ0MMNFGM 7/LMdDRR, /+c/SS22:J.E%$bq\ :oޭq"YTց+>~p6 ,,.2:(,2,[''O/g{B`B{붯+ VkqxQt,f(:sM,Y\\il_tckRхk0tvCdnk֏ISqj/yW| N=~Wȶq˖ 揽/'\>5昚o<@8/g_d驃Ns6kܭKK'~[8ɰ} R [k12Lss1ˆ[G>pʳS.7:5>)d,%&2,ݿbx =%Fmn[kjCR -dJ;wߣG8r Ue ES۰lx& #7_2v>Ў'nbҧXR\WW`Gq0ot7`>g vbO~mgKvKۻ#")p3!F'ڱ}p_}Ndul⛯?B͑Nܱc @B]ƛc?8_>J\r =07:ʕCGb^of>##, ]?w "uU}.[g\sXŸ2M66&(DPΪ' €HJ88JeNw3{ 䪒 (APMiF 6~[[0Q%Wk5Fk/QrҊƭGBduΣ%f m27hi[ӤIj3F)IR٣$NmM_=)f5'"YɄ/mZf҈Me鬌lK&܏i `P1fu'M$_N_KpWt ύݯ,,w&J}UָEp{`0.o__+u Y$p$5kbw hM}Db7$iWDkguC=3` |c<4. 1SHO&O;iYP_rjӸƤ7D `|Sv|]3ZDtH*FBtL'MT{eEqкf|SvIyNѢ+̜m= Q!Xzƾ16. Ŋ{c$T=2Sv>1(5H,1xVU\hJ+ϗk3o0 T [ܫȐ`P{U h+(U6iQ`,#Kv2@h,Dhеszv#!Zb@ ȇf`:++P?, bL͆h(0{&%a$uP ,ÚJ$ "tMum%S}MPֹjn^ l3,|+vz_]Џk IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:39+01:00p%tEXtdate:modify2012-09-26T20:26:39+01:00k-bsIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ar.png0000664000175000017500000000646512625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> sIDATxMuW󳳳\ʤ,)R@őEK-Ylqr0l%r%FC ;e%ȲYEK$R%ܿٙz9lOOϒe=]]U7_M)7FuoQ*ɐ2L*όpeiC<]L>ջͮ[Pdש䥕*JɈ( [_jױj†?pg~Ā˃|}/'Ad1H=T^_cVU{O6}/o+~@1ȹR|dׇ4Sj) YF҆I75Z3akydG.'4lg/3.^nUl{_>=2:-AF\$4AO6 22^EW K_i*+bco(22w˓_G29]L챦RMst^6dқ΁.z)}h~-;#6W6cb{Pݷ T[ڧ}H4%* hnR}ݴOu30Vds] #mUG^~6Z1Vsv מX@CF>wUF<9V@!iD hG9Kq7Y !'y^:2GoVc APHzګgQ~9FSo;h,r%vIK+ $+ |5A~+&Fëc)_.{{\*=R1 R`C_gmfKGK1YT \8T[x s0= Ţ&S1븅`k8rhtD1@mdlex߱s_x aR}!m#Z8\"ppoVyT7P5ǯ?w6^& ;Q.UU;6;G(K=yB3|^q9{htؗT.\@-"+/ދDG cmVl: _ O@|<߅qX] S U (qsc7'f*"3nuh ;#!n| =CJPtDv0Q>~5!DoByԮ)O%.Lt0f7ԟA㋸aLvz(P_c?gpht?Dx.>w & C ןLJീ X+,_0T ,l"1q_S Svޅ]LS$> ~4Dj2*:U&~ooòF0v 5$S76@c& ũ^`2 Fױ`uy8zPcQOa^@_u4A;1A|170`sLZj^"ZLbgVm`>-o;20OÃhuC^i<*ED1; (b;=dt's D>7 z` HCXkRC>Aֿ^n%'1"`kC7^EaK{I[^k3"vyėo4^`?-1HXYF'Aщc?@ʇ(U:6_)egCpMeE&8rrsk"aR}`fvKOiQ|,Ss f"\)bLl-wvxGk9k BXuO %I` G!u q׹ֈq[ㅸʴ~v%?$k| {1Se>]4<.]U!H$Zo uTBEygk.Oc\н, I> 11/Q5I_%' g/&&hHw)g?@:ɊO1" *+w"αFU+L~M6Oӗϫ" H}&u $/'@ *=o=7=$fud i[?H36ZU_ӳ%Ʀc >Ҽz]p0NByо\fkկZ@pPgK]"ݬ_^_cEC5eޖL|k'c^bKf>C 3|uuBkcjk288D6ګ1jcMcp-tG-L\`ʄp9[yȑlwrՃqGmc-QW !(&ֶnx%2׿us hԕZ*/KV:=1IWn{eA  _.W_E.syM"<|V}&!1o^]?wP Ja\w}i:f_6l,(^nA- $m!b8 "@${%J$Zy.y{œ"hM6?䓯ߊݨזv/tҐɰM-ޔq& =^y%tEXtdate:create2012-09-26T20:26:20+01:00CB%tEXtdate:modify2012-09-26T20:26:20+01:002'>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tl.png0000664000175000017500000001017712625666430027156 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[ye $ $E"&(ŶT%JW[RRAGũ/zIty(8;" $.+9 .t/=sgv@||Oӧi*{9$LqN._N 48?h;x/kzWlP\2C8~PD UH(x.Uǟ=\XN~hw99' ݉>PA?+y_m/&kF O$i ؼ4mAj' 㽵ZZ)>ɓ@-\Ţ'W 7-`-,KKT]d]vFĪl*,jeQϥ@ChЛ6:z79ۼ^y=]:[:)P"W׫1WQ, BJ_S@8#݆X&FWڻ %PmVJHy6Š^+B/G}=t b4ƈR:}5Y (#^H; AaAnQz8;j5VG%It 3H,#LԄ=o[=Zh(k1Â\IMSIt9ĥ$܆RQ;6}֏b#Jg @n2Dh0"(¡c庫 yBj) N$ ΁ڹ!- @^#/OUJYUJmz5Ƙ_7ƈ /'?TJZ!Fvx7fؔiDk J7@erp߅B0\*lQ`zeyyzGUظ<* A͛?>gŊs=Ǟ={0`m"ʷ[?ϜB%:j1BiһD~ |d 'OyH])Uj V{zzO:3<$&(O*G  ZbL)лTxB8FsK.HGT> f͚-͛ϭ/~1 `y\U7~~w.R4Nh[ Ӗ|?RohsGL+D7o7ncݺu斵rQzjąl~`9;Z; MXȱ h]Zpahb[xYE  ppBzzz1c144Ν;N83h1:c VZ믿ɓ'[ N͇^xN',ڨ`.Eu|P8O™az-]c*DkH ȠOa2>>y7ؾ};R 6pq^~e2s׳l2vhǧ]#h%yW ~&RD?w!ZX)¹W c \a:˨ѩWNޅ̪%/ /cǎԘ/[}~s>˦V[aB [\Cqnbi"et1HDcǎO311P[6}#_.0{9 DKvo 8){7:Bqg!] aEa۪ϵZX2_-p>/Ā* #a').8Q];}#FrRVuU^^6\6 o6a'B8iYm@K)*oҹ\U*t^v˪Sg-wMEf_q$8L::Ñ8--uxYUQ] ZfadʒЅ3ʌ{<Ȝe%O o+nTĶAm_y]ď%y^~7Q+]pd_h`pfw"U}n5}ȚV+ +{Z[Ʀ![/2LSMD. NOK{ 3K4rWa|_^3bj7&"SuKнk _q| TF;dck\k QB7/?T #ۅJ$;t:Ka/??Q9~@[ uk^-0}vNyU()؉؄5Et^ 5*$h%[ocNJ c LN,+<~_߹ɧ6 $: nm|N\l>9{S cӢa?BSb[I zPxMVԲn(fxh V6}EKm/m.(~=/hCMIUFC+zV,uw`'%zs!gJg*C>?Qw!-?6:k&p0 .0_k+QLL/ B h2/S1Ȫ 1`cxcɷD c0p8ëq<%jx9f y3[H;GD qNA$!9U_/ Ey._I?MD{d#ئ%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bq.png0000664000175000017500000000427112625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[A$7'3&aEV\ Z$N pHH\8rFpwp.@!EI ` dw{zfǡ*eWUOmjg e/{WpBq+OxEC#$קG/;%_FDA2Mԏof6 WA`D>^..dPK@ {яEKY6ҔDu(_V7q0]]wU30޶vـr`m=GQ3mDx*Ҿ5f?+@/k儯oHʷ[/c: KS$I ؊Q/g!U/OJ(X.wٌ_kFFQ<)ㇸѲAK\H()cRg.d-]D{qdO'pK Rn){=[ʞ-eO`"|%JN `v]HZ^>X9%. ,Cր7ҵTFh)y|g縸\GG >_d4;%)DfM:j\o|x]l=V}E~S|?7'pz@H z(PED![4kt(!<׭QB}Z,P>ϊ1PZH2(‘\ uQo%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mw.png0000664000175000017500000000652012625666430027157 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxMGVSg88$gfb3iF Gh[4#gK$6lX~Vla4,#Vl e̐ LLϋwa]}v})ƥׯ9NUwã<*"܇qN%pI;>%BӤNV] @A}>"C) q PӢ@ ~yp-(OL vx; V0v· ~+f$wg(m ,{eٟodz,\ X5)0Dxu8-Lma Ɠ59"2x_nXf˴jZTyƯld<3\Aiiv48mfbS8J t$t+c\{Fcvm7Dx xw c'G3+:OCg0+u ]Y ~%x;(?5l-˹2zSwm ja 0Bʓ{mW"<9` g`|}-|‰9' /{i ~0n(b/y'cnj2olx7\#岐-pLqЀDݑ5 [ºpU _BIDHC?Mv n A/zC5w Unxn 5uY\ZW%LZ` t"޽SZE7=Ϲw 2r&e†Gc@N <c[v*:ܴmFi1>cjpK /E|_y􀋾HOh&jր8cA0vpq /gg0|@^mwc2/\#3h7W?ؒyo[6?/ E'٩03cGB#`G US43 qMor[T%bGREi]Ϣ%/(cL̸g]+)G8K{ \ž oʋc}Uhƌ9tHӐr`E3¿. x//Zn|sbrfw)NZJtGۘ«-)Nst#XY}ym+3^ʿr$)UQƹq{2|8A-A!NjuxجMO-/4bF0%W0(wCي/yW g;^$7W$W [uŋucֽ.yE 4(>ʍk^xmU T3F,XIB4NY8=[SBm Y5ڣ_'@Q +zj+&=Qc뒗V ,X|ʷn wMW[>ꨝZ[K&cc+ p}^ћ/`O#o}'CIZCXuq%bAI\vSN*5pcYR \7Z^5zB}UT^ vu j5@/X% ނe~S5)RاX 8FwryKu +MmZT}WZQ)(S^#PC'X]@uAH@Ƌr=k܀o.MX`zJk<e[Ǔ D>k߭]'@KY3I xNt"5ksTkװKk$K[Kr R .@Hʚ%nx.,fɒfPT`<$Eʦ5+fIxJNZm|%CϝS1A91'1+ejXZNv*ékg^uYř`G@W/_x^>ƙ!(bu>^\ Vڴ*s3]x5UA9)mx̐l{?򐥯I*sWQ+^z,v.6<9[O KmgE1DEc,k}G̒%gҝΤ-1IoU2s"sr ͮr S]xFy֨KuΧcN06Pŀ#&@O[uNXNpնUs(I8lc3{sϩFڦ;Z'P҅ݑ?,P(>(~x{q˃f8Zb"(d7;!5V rbXf rysk b@C ivJx4ܸͅ5`{:sDSdp[1-HWFlDb oFRĺ f,)>GԿ{X/!Y,/Ha-ֱ~TD_8Y.f%tEXtdate:create2012-09-26T20:26:30+01:00%tEXtdate:modify2012-09-26T20:26:30+01:00'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/fk.png0000664000175000017500000001523412625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyt՝?zZ.K-/1,aw1X2 aK80ɛ 99s$' eK^ L^blk$խ޻;jdΜU*:}N_/:䙜$NsLJFr9%YyV"?/@@PrPp ]Ѩ~Ul|]EQ!ۿo1cPT;m(Uq}˩>W^ATy'GS}(4~7?dJ>xnZp/YPU&SSMH5v8 !T|t؎EUT,K2iJ%7]7fB15k\s 6O1>_;p1Lhۙ#,0bV,B|ab YT;Jwn|h(-kajneYp^NKK?WbqqU"Tj*ʇ(-@LUfq}c oܵK) Ι`Ϟ )/@%Xc6m>EEe8 EQdWKW^?>|ۄ -KJ-!PT[V5\PI5lp ]7 M yq IJ$B L8Sm'Pvrò^͉h߸xz-+Sn*BxeRj5_R"- +v$ڗNdzj5$BPqBeш®]'ؾ"5鲓_RpWyLي  ^W1Pw[s~6v5//ԄPUP@S?5rJl'DDž@( WsYj hO|3:/nhO5 YŨ  !]]d΂WUDReApϞ,x# 2*$aXfxD->4EiStb#@ PV*6,_/nW94*@iD!D|;mrԙI_P0>7RAHi E}K*Y=_LQQC8oH/I'iד((dCu$2׬U5~~eUO@9@J4I`I-rz5#(tHQa"Zd>̴/Px`qj| U`&< 4Bm=^DcI^"έd4CaN'ŠrF!AN% l=.%N5ϽÃXP'3rFO,[u=o\C]M wq !I2jIV)9{u8ǓN5R"MUIq.TޓHVŦDLD*U`f(M(!t4U)xΛm"J_!^0.l6{<^0ٵZ‡ܱŦ"lSh*UׇFa fĜ= sʋ#A<loD׭Tmh )B"'0 ǃeCP(D,tXebzD=-4}QBH s7` 1nO?*y)εm-K{G:iӪP=T矧_oQ ^ƈL+RF-/pGQQQXXLG3, Mc1N\# gD+JNkx9sGX&O>G߇k^~~pOo乹L4C{{]ʕs)7)7u)?.92]RYҭB*bCUU(vh4i(i9$:4 Fiɤ0 PJ0M3y˹sq?=3oW/?宸2b^kj?}Mq`:C-<\},zh5|ͻ&J|JnIF){qѴx~?ƍò,4>***(++4b!@T` QEQU\McDu|oEڵmt3 7yXP:"K$!@ ֯?޽yRny1%\@Y "}O?{~Wwqxj^։A_:5u1 ^ Ha`YA\NdiɩY)sjkeT$DO{U gZks?mLJ))sfuقTɓ^_fӦ|+8T{/E^Hc] .b;Omf%a)iQ‘ft]ǒH VbL L)QwhWȠİL(Od(,ØK.Kf:}Ͽ'>/wS.lՑ1=˄l rIn|s)U1'{Iο&$( grxH{,L6XVcnW 2u_3!Sڣ%fE9H"}FkM,{c {J<˞/%+V6RH+$ &d?c}xKYb6.eO|CȒ!0C$]qltxh,}:bARq]L/yBMƽqB$M,f"G>={r\ѯr@G;+q̠7>F":/) !eB((BJǑϺݓս2e2RJvh^䪫4<>uz7D Wi ,=y !lDfRSH3iR" (A8X1C(w!dMヤwWP't:(Bij&! !u~-|gvLٷjnݎD gZ0iC9Jw%,͚j}ΑDL/aP^\C:fepf5R:pj.-a!5ṪK,_?J- 1$MUI+$phUs%ipW?=Ch%E&gxM/Q޸XsZ B?{%q[ sUF3:%GYӢі!{>B2*Nv ѳ7c̜g.S_)ň=\I֕ 2佃|&cإNXJ?ƞ">>Yʠᢿo9-eEav20`-_qZaP5B1xBkȜ~Afhc yňPf]W0aX1o1m'nq@^î2,HZ 7xy$t? #`t p2M<'$rLRCT`h[k+TT0KyψN]ʏ,@=؇1gl?sh:IÇW\āAôPdr.٠&=Q$2U؏Lr3Mm7O6Ѫ:P?sСI/ä)yVH` h R7 ?^@ww(Nk8 qt1I irBsg+[w2o">0ja޾ Ft(e}CԔS)"a>,#Ơ頨D0%nzU/i£/yF:|ƺ!aw9 ~r[UMW1^idOhqN ]NNJvy$vp9@[4tȕܤwn+DR !$~-&m|獼:Vê˙^u[LI<Ɩ8m*Cϙ߇@ %l 'P4Q_-%c1 }[`0}}cQ\pfF _],ϲ$SVfк7,_ľAޒsX$Sd m^R" ";/5[pۤ0#ie#G F}+3@(_ 5fh11N{l4jma ( {}t1A{ zDb&繆pģAy,^u\pifb0~[=M{ M?{60G2g%㧻pXyalY^;P-} ۫wa;[+K]XNbb>_ϮQy "=s*ioK18(9Hdo#F9ŴUܴOf=F{Üe5q) gb1aih'Ӆe]>NPX7S%p]nИ"`ff蒃4;FG"w^҆cqUQUYbtbz 4;x1˩.F;PNT`0x8y˂9n!n=o>Gp;fD*&R2lHss1G~48^Ù`Nxc!6͌ah'&4Ze!ͅWO`~=.2Mͽ^vje^Uv&CنEpH=UT?ՋVOKK#ypMMF ]nas'khCޅ7wR0/r@?UhgG5usw1$!AE` 70 c8tNтLj)gsLwZ93Opy#1".t=ưwS!;=DE#ؤxO!QPLO@M[=8KjX-!]V6d0}f ϗgJP?"3tLAbU.b-B'AL4&M](eȨ]eCSzлO | "_)S̺| 4C6iڮ,)7_{P78t<֭ _JR&wS)?hP\&$f#*ġ0-R[⥵LU5w7 ӋnvFy0 xM+ht:dMe__")HX'^I inPiZĢN,f3QmEQp d iHeÈ: DNPWUubAA E)_B_2%o8et8ס&㡅%Q vޣi'>o+JP%L|7oK/L@@ПT_ `1(@ vJI1 `0L:DFx隸꜀-Nq%z,% `b_U%S ͌eB:]~U_o J-%tEXtdate:create2012-09-26T20:26:06+01:00b%tEXtdate:modify2012-09-26T20:26:06+01:00yIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cn.png0000664000175000017500000000630712625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx_Er[M( /Q1Q@ACĄCTcC@@HDA%@Z{m=0sf9j'ٜ=;ffNt:gOm HkI|oT@˝k!@ tvK~fkszۻD6 {m59TZvwOY y\-UUs]c{Ѳ: ADuM[:gjsm3Mn'I)a9jeν>7jhߑ0)\cWUy/vIszP5ɛ$Y`p ,kUDH*CԼp5uGvX§7m꒭lM}_Hwn7tE:ӌphh1Z7 0h^ 5XxzlC y \^Ձ$Ə-@S\+Mظ&z!D@5v C`/ȯec V 8 5M+lS3ߖ>`>W!Eu ^Mi(쏔P$`cHyՊ1#r qxz`M@Ivbj4 WX s4(4@uGz85KGXÄ#WxR@4o\ (bjfW=*FL^Bޢ:OnsƵ^RVj4A;k;?95\]5 sM= ;1K>萯R"u\,6Q_Y[ͱr <3 @Rx#^ Ղ"%̞U)jw.pU,ӆR:G >2n!߄m ʶ XL(4*Xv1& f_v7 !< |SbO D&PUE5sαpL 1s10~ ORw'bϩ>,㘪#" m7єNč1:ZrQLaffe< UWu >p&-QX1!ɺPwez'd$v̡`ʴ0a/*\\`t60m#O1Mq)m/6v{^XL"Eieh%ơdr1xm)f7{z *a`ɗ yQԅ_kZ1nG0mj kѠ6 P˂:B8&,r&>NwӂݫLd@5li O nŗ-̓GX `~55fZ$Ӆ:@w@hrq{Yx`_^>,h}ݱ&WE0jIoZMp0^kXme=T/1KX]o:WBt/"04 Fŭ\&I.4Z̅6J;TV ,/6SiWX@:0%KL-ko!j HƔ 6]5'(hjRGڡ 64ƖC@/lggc:41#Q* EP&2V >s(A5񮞀<7dƝYb@+ GjK x#@FZ?l@-*0sF^Z=s0s@Sݻf[Azǵn4nH`պ^@żҽ\nxFy̖A[yM +E3TyC; 9Hs*[ъKSP.j1|@ͼU#VGmkVԬ(cyӆ&eG<7WqdCg&@X 6a6/Gڔsj^[|3ΫuSkQ„Wr8:W2Y3`?hN%4pdO{>>>|2 7Y2%M7cioN@C1x~>"f]޳wkd+zfHRnLeWXUr}Wlk=57owtKB_MPi-Y /]~~4ìfAH`ٖl&8~xQfq&|lXBbTjNP*1AQprDb G[Y0ے|1nm@XԤIW)oNU+R9;GtFD~Oդd閚X)%tEXtdate:create2012-09-26T20:26:23+01:00r%tEXtdate:modify2012-09-26T20:26:23+01:00=IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gt.png0000664000175000017500000001071212625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxkUv=3cǀa +Ċ+hhw%6("J$+E!Rla^KXcyόUuO>tuwUwuA%u眺U>3L~DSHSI3qoAӞ4@l?h^R(@r./+*A՗ӵ@}/Mz!B&i^>V)" Sّ?8dO-6j^~O膯XH+aVw3LZ`il""D) s f'Pn⡎ʹ.MZc 10:LG^.>4jKwk*h2R$p .@TNj nX *3uuW6e-˜M,yã9i[ 2暯nVmh RFD<hĢז96F `\D"(&f ?ozSm-Pl Q P~ ECjPpz"Ju/.' *t-b$ Bn_8Bi[^Z=0C2Le1@VON!;;ي"scUK:UszabjekXndl~o`Νضݰ=-k$ZTH)#4oa~/ocI|WaWs&b.׿N$񕛜Į}lBmP oAK8$7zPG<)NM w^B­ɥ\l:6_}J˲سoyt~'~A M@n&X1G2HaL <,[,`ݺuI%CtuB2?z,!wj{$۹;裏nNOb;kPzmnEytMj!D9Y R -R,U3<ٍ#IcwowؼL2U(0}n\Rք^漍 ?O3edIo?̾DG°w㘟Gh 8REd_szN+<W(f =4?z}Z7ŦMXݻ2;eq4- 6 E(dq|UlܸQzazY ""i:! O3@[@Q4\QˆV=E]Sセӻ| +{t$aw'MדwY;cc#?8۶m^hlk3i$JE«o,} f+b'>&*e]Kz62SeNj2yRP = ~q).Hm7뼩ҲXւEaS)0,sss:SQ"r8 |H!E2 Tŗi^[aӎS &Dq2q K;14vri`FB,!C8ehS$J==]kL΅14= o[zKG4.W^vR \#}e]8tu:mJ "ݙa^:N_dy_ 3jELp.ӴEm Pd 6#JzHВ~֘+c`hXڱ{0p V4 1œ8"X;ND+,\q=Aщ 8a52?=P2޵ `b#Y l2zE0 *ʼnOյ7svkan^H$R,cѵL;(L+V00Ov@6':!jFWnv O&ZN]#%!c᭓o2x$p~l;_䶻s`>M-AO_7.~~'F8Fk/GOhl~, 6ʾWXʖEխuu3>3˫,'x'aE&eI* @#һ@ r^?[ B/NxZ/6oM.3it1yu^6z}S z8æy>Hڈ1QV츋==UK,cMqlOȝ;˧w-Hr2g !҅ƬcW5L|VT-=VP(cpXml%LMӿ5uwrR?bJ:_IOwGN?)csgy婧X8v6͑w.NR}M48.^)@˲2ye| b^Oob)922La.Ցܷv N8[aǃ =לqz13U6>/q W,PaSۄ/f)&[3e;ٹ)nğ?tƓLh]A} X𪝈! ZwxaRI6ȥ,d劕@kM,qoN;!}9ܰͫodnaA@}uv`NjcbP٫֪iʧF_$-w|񦻘D4!OV àW#w+,"݃>FV[sk^ U 9ݩtKE]yOM "4U,-El `_mORT/>@li͔@+ΣJ& JV) }=/])xDKv@*76P(ıeĞpKZzf|6 d;,+W`#wN̋o`a6GdZ>_G%[:tBT>:q8vbk7'T2 %ʼn\Ly6JR4CŖ3KPf5W#*} P0< yX@i) ٳ'] ^4 b{tIu>Rv ,p &\rlX%f]P#lwI"b0̹89F@{Rh&`ݧP4%[gQ@uu]ԌI/Lq.F<ϰy^ZjpJZ)q%tEXtdate:create2012-09-26T20:26:13+01:00%%tEXtdate:modify2012-09-26T20:26:13+01:00x:@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/uz.png0000664000175000017500000000571312625666430027175 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxI$GUYKwUx`cmـ,"8rDBp/ q q x2{*xr̊̚|/"2#HH+r `rif}%fsi6I/ZBpO~~z5|Zꝋ yycX^N?DXя"fUEI rwM w+(QԿvǷ.abQpǾ_->S7g+,kt}>zgD#XhW1",=aGZ 4WcDtԶR}et/8@MnZY^_{>.*' G 5@58uk~Y^G&Wyꀭy4je"0^ء͂Ԫ.<[açYlװVyct^y:adP_{t.b%mc~w~d|IN Ws ԟϞ̶sFZec;75"I6%IHvvUbXnlac+ds'hq1ddu1MbE'pҧ]Kͷ֘iTyQU9u8*oҡ`A5iŭ/DF!Il||;~iuJ޿d`Z/ägkAKQjKדf+`Ʈy`3qDЬWi5C IP/jD3^QШUnVfjl<1 &Â%1(fZ\#f]QTEVuMfXN\Wro=w @syEq u s+̷@Rݳ*7fiC{95TpUv!^s+,< OZخe#kXYӬ$m4WOQXzby譈OK |rZwlsb}X  6> aǻZ^{ ;7?A*\xsE]z =I9@4g{pyͫzU#N?$/;;a Pa`^X,^x̸dl'N.-%HngP1$)JwkxJcLS663#c7*ǒ񀟗t`tz==BکY kl]z|rcRqL0tLYá:#,Tl#tly@Hdf) l!WWpϟ;jU+)ՊFyS=(Eo)UYfw>7n|'m<:2=Y |F1SZ*j-v'%p(G)G)G)G)G)G)G)G)Gnf*߉A*SH$\d˗Ķ:nlq+?\@8b^#I=Fú{cyzO_`0seCW#yfU1srl E,!gq/{B=NVxr)r/<-Ȃ M'CPCךy'[4p7\'kF )+|G~PI 1?Q@W߼\⅙ۺ)D()Kv\ʯ -2rctҕL8csI2yML^Wq7uޝ EsP0)4x<@}Ӽ1KK0pzr.`X d[fZALp@*p=X3.j6@]w#ZsC$)@% 6D'R|tMu5'>;~Ï'F<):;0ǿT"S#!%*Z?!8&x%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/aq.png0000664000175000017500000000664312625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[u̐.I-%A%JDl E $fv1'O7% b# _K4M /!Wr\o՗%MW9u}/W^&5uNOd뜉ΗBඏ=<+8[G)߆a1GRRiqJ2fP%&ܙLJ"~wy|oA2 pѧ_:~闿Toޜ}յ@E)Sq_Teev pPItN/180ݵoկ0ԯM'c"jMƪ}**O\BxuWC\'10S[?_)qcxfZxw_p1VN{Y+q"U{h/;+=fg7h,PLXۓ=/_H`NwmBa糼>!m^2*;e 5 5Dxy{|aQl~?{w}#~Z*z{*+ܱc t4ۍL#[iVqyyOj]FցȘ[8M!{DO?X IA a4:vo&ɋR4SLĭ̹}k~J@PInFass/ϟ_dan*>cGt0#T|'0_eılXo^F+ | ةC84Cg]((a_yP"V!1Άk#Qݕ=_<Ĺ]+#/SiZqM+S2$92i\tl97̴ݶ_mfbAmyk7L?~K+"`P!g֣@6{w'\wv ȇ4o6~xJ `}Ὅ]1R.o;) ,]&Kbܘx o}3F8^0DBڀY;Y! `Fi,ѩoA@/w9GfyCcm\G:A -#m#fj?yX߾•f rڥXy@ErSXaK ;iCo.pRR W+6{>9#m<١tYOMj(;7Xf3)`NΒLɳ8^eˇ:J]l|.\'籣Vv\7,pz0#/d*YmL2k10aUx䕌^?[wpUD˹Ji$M C Ӻi\]fJgypVۡo8{ֹ-m"dx&k~s_˂]R+\%F!o$%0sV?:Ew1_)ibj+-iZ!cIn-ټ*k7|嵫\s'f84rg.ui5-3JW2> c`e&WIpT#B!jD)94¥=.;g50}UcV&W|^eE*,0&JT9d^dY`1ObcBy4\p[lt Lm rqjp}˯@D ,&oH1>&QOքUvqcuOM86[#L0B?)&_3Q_?T/h'{Ri:GdkʰX|9љU% Ӿ""i-E[~>%/mͮE8?Uo͟PJܪ ( 坍 ,٠ąMm{ b@"x=&l(e\fFN^`F NdM55_Btݍb!օJWBW/ 믠y6I|ޫ"c>˝z-$%tEXtdate:create2012-09-26T20:26:04+01:00(%tEXtdate:modify2012-09-26T20:26:04+01:00uPIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ky.png0000664000175000017500000001477612625666430027173 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>s+t߃\$NS2%a*3biT@ YJfLɹXW^D. D:)%%%9|? vX|cB(J㥔B*&sۭ`ټ#V Ũ܈bRQlbH=N/)c?R诋Duz: iҫ+_9e 6mtO,,@hMW_7)!B $slDvv@qGdgw7hf5+Rټ0i.#K^NS>r4MGd Zy?0 " v !<4I P8ӯ`Ͳ3 U?\y%W_M鼩TϘą駷w͊H.(v#4 n¢.jVbEDyeEpYg MǺY~7ou+/aK/V*dž!Σվ@;Tu"/b~}i3{s__Vh \Eܾ{q UUTVsӰZ8NoopXxgCP#4))bvlZO($u8*ui,j"}>nupMD8,y}w_غ?B$" oP|o C|( 0Gn=1qk>MQ4wg?㝭,;_=ɉkq_}5ŷF ~R,#lwՅCH4 )BJ"/ĸ \,y>גsqAj/%mv#H|l㨿B"ZYSژj@p9R2_Q aBhtux{[#'eYsENAӿy3jNJUlt jף*b!K)q沠-O<4J}׭R^N/ȳ~2߈EI8+EIb1&UOE\T>@0RG0ѵ0C,VCfeeiYEBa˦kf/tѹ }]KnY_\tLtʘwaHVEMw955r ֊ :y-l|HĠe &d_RubȱāȨ: +`aLND[}\uUb(ժ7~K! R8 H$rϞ>Pl6,c ,dfqK*<aS< H0N4-^o(~ijO!E@(ك=9A6UU OUP1twikkZ֤ƻd \׃4m2`$ k?Z*1@<&oDPUq) VEAU` 1B *&/asPL5}!s>E@R /aIH҃ є$ 1((Q;NrH]טRñ׌Y&3Xx-2 #Z哙e}|vF$QN}H5^7JI?"9un nĂuyJf~#0@Qo- #ʺ8$3a B@DIlYh!RPǛ?u2!iiqڳj>C70  ވZ*+#ni`UXMljlD닖) SQ Hi|MnU=:FfpUE7p֜XQIw2:g1D l\uՙy2&N,`iA|KEW4xRUU9IJ\'ȶmu[ +Yǜ9t< oZŁ}'Qs%V͎gQ}8@ǫ>ER9y“F}lLeh:QӜہl(( ͛,2nu)EED:;xqZ~ 'g^ʏ>ȋރ0n'W^qƖA<a{u!8~͛*&עEd͝=Jz9he7tRZ==4dԳPy զB8HӮ0~z.UU˺ KlLPF{Y  ,,tzY~V][?w^Ko_?D!b̋yhI",w'xk`ݺwt\眃} ?կPvְ3{ٽ)i]]>Z} BH ȵQTZL/, Kݴy;kFX$`\lV$Hd9|Ŕq8D=pZ)}ś{DQSp)y0Jde׮["]hy'-3ŃT!֝ Dף Z]7aoPk~?yE%so?ٴcbU8y~\F"YNJ$LK/SGRpt_x K/0)CO` )QJei4|A~}(wܱ9dq7zGz| %JIl4MճX,?z6TUb)5@ܰB:]D|*^RE1tuMę Qv`wG~0k,WtWs,9G0$hvQ/~^ˑ:g 2dQx|u;}ImH$ʜ_ˊ5%4Gj^>}~a=i8.74M!/7 }PAl(?, K4QYlԕPT ^o]QX[ϵi,qV޴A0['!{K+<=>`QbA^S]?~]T(8hCQTS2uoܰG"]K'0,B4Dj*a5_'7&$bT%\`M <*#NJw35ͪ<0QHF%YUiSE11 UX =v) (t7"x ZS"Hl?L96-2/!m)  ] VhuhNKleuo25WڦϒLÎĸ#|37ڬFiŢ4¶A4@~b_ñO Dž8O( Iۛ"cq`j%1^5%?OnKw}uFD'a7AVnbW1m}ULY}7ՁSL*}HsO w9O42uZT[ig5o3#C^v%•kT_}ܳt+"M7X# \PntYca!g QPm(#XBBC iz 4|%U2;L)HZ9=5mnՅtvqVu57S{(9J7;^{\_PWV^NZGJ氼|=! vn&VyV8KRo8in#Lˤԁ@9lH"Pf G/2_JŢRsk)xd NrP_\_c7ա_Ҥ8,ߝJQ^Cpo yx䰱z:U -D xJVWL85N<vQ#X&t4m+Ʒ3o5ąMc7p.uKryqDc`0nCM E=<=b?U$2/G,%Z~!z_fڂXU_&\4=PBss>|w]uUSW0m?ʱ/a7M(|9EtN38=5y(M[\Jcpq%- J[YDp˗愷qy,VJs ~F73YHMǸfܻtСs+T v+\TϘЬCK[>#{Jl?Fui=iS)jHK;Ưws/lyRJh  '[0z;xj~ ɿBf4Urqt}6_5|&/\4 " ؂Ĥ-[bqx}oW pƖdϾL2wn9;oGt;f|V!;O~0oJ-6dopIk2ItMZ/~SKoK,^X]K ]_Oiehtm <'\a?TtS;>&5_d0TN'➀ג[ ckeBoSǀG0gXLZc.tk^ϴp݄Y;"kSf AKWil7{衭v)#>ر"'qyx}!T'lÕKŔEow_;A[[=$Ӂpҧ II9@EBREJgţ99+dYYpF+;2 #B`Bp.D6 ƣ15UHuw!- X !DU5JK\Lb8˂|Ǜ#8ѣ VGQ6o= 4@?ϽҩpKYY"|]:*Qim릥"@9C'@˜@)U54i=;W.@ $(ƪ:pOU lߦp8Lvt4Ƞa =u]?:b@ P%W.P򉪶kG)q#@zN+ ) SaT$J]O Ը7{:щs /oXL GDYg'5Fi߶Dm](hA[ۍ$A"4+ʾ$pbM a*2sk462\%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lu.png0000664000175000017500000000373712625666430027163 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏF?egn'.+;W"qā+w !CH8X@ dgÆvMOn]eǞ '.WW.\lg;{vM.6v)$-0<e+e{@-c:}1LDemK4?0=jYm4QewgRDߑ/'xɘ7>6??cciZ|P;WRaQU,qdcf{@@V ƞ 0~Aף/|d"WF4f.uG@}N!՝*z~U.L%i-:ul"&Gѭ"_<8抵I}t&CQ;)}JVjNH,DM7E?ӔC|z0N$Rzvj][>zn,#F"p r%W?7Eoj$b"d T!_dˆ"F X7@^ޤiދ> - y;06z)WZoH6 W/YYǦRYmW6> hA5!ZU=VbL:t6"iʕֳS [#0?1]66&z7գ-LMnP3l w66Pzi4ӹd̮8;ކ=nG/ݧѢD7v}| D\Qo(w1^C_}v G5 r^w) kIUMSZ }Py-}{Cn!o#GGx~&$ 2"w Yi ֮pQMsf0'a{gv{`Oi;=mv+[kV&a}Gl,pmþB@JH9;Q_"} Ֆ5,^ܛu2C,Pf{yߥ+M5t\Xʴ;5ځU"D|&cHf5gB2Li_^ܿ}-0Guľϥ/\hU|:.4⻿O?10x>6J(?s1M@G$Cl_4}8@S)X`7yIDATxyuyfެZ" -,ڈC06Repٕ$e9eeXXhFX)S*G#!X -4̛7o{GwY4^B83}s=ܾAooxnRc\Sh32@kZ@d+fZ;v:yaL-7A+nsJW-xyH)'nMAѶ K /9Z Fbg]wu7R-9}Fr DύѺD % *{5}/?` hBWDxS @2b'0DGgGۦo;>ݭ;ws0]f$/*Hn*'򅠔t ]h]k,fxxPN9[;đ!(^g_O>=Fkk+˗/碋.+]/-v06pllllʶpr"6=:Sn{:PlV9K= U&o{Q2lO$b1Lzҥ_M6y8m"(Z6ʺtm*ٖQvH4ZF&)4i[ne˖qwW(S\2BW5DBʣ*$yy~~osYkwtl`#(Hk$,'RJ Ϭcm (O cM4ke…]h'EnPT7Nk RNCxkU?@Dh 0<;q֚ OK,aƍl|?< ZUvE,1gU^>5U=aL v($"2ngo ׯ#:/wX{? FP{*hETlJ~ЂaUKL@H$»o|c3jk_'%dh1uHFu|7V>eͣhVC9+xM @âǾ5O:榛nkwE7&oe\ESDrjϟy@W4"F,KF>)sQ7J3@ ~ }-?ݍfsRWy9Wr'?o|s mόdX &Uu*`Py2Wvh֢&c;ׇ># T~mc3sӌHFoo7־}( W81;J=[ZZYd _] <*giT( <ʚ_3/6_@j+j@u5P&K) BgL[U"D ҩ~ILa /i\y{=|ϻOdSPP9JAdT94UZSjՑH+d+ӗFrJcY,%ٺ<ϟOOO9gx,S Qij]ȤEN5ИGŽm!(JLeX5鑳8v\303n 1ܞgѣu٦R)>z lX5f,ZOfk01BX9 sИꏁJa#{ f:M 3CE"ضC~H9[H6 4UY.*2$$f]+aJ).}['o|5qaܨa5Zxd/R/B*4l$b1Fw?ŒF1 ӲؠXYt/v*G ORQ~H)ᗑ*^ݯ{Zk;t0 bDSǚK%e&sjFF!3`;n9ʶu!xF ={6v"˕HW!fJF0[>)DJlݵ˳y(P`Hy~{ eOÙɛF(ePf_zo0/gϞ=|ijڵ6Edw3bJ4,YȪ]L 0OG9ߴsAA;ESѣi6n\@X.pH[Z ]Һ2 u?\? W (7 E0YDWήXJa/;tu6塇Y̳w4E1z1 fUmO@H&iFE]nӻ"S͜5ѢE eM$k fzB@}DDoSL8DvyGXs{Ӈ_E"$ 萻Q@Bp \$e%pvG F^iā_L^Nzh+ fX]kHBt-tU0U4Ep4>TW~< ?goSb.횽ƨ6E 1MAgu҄A\[r#zǯ-~4.aU^,4nOD#t֬Y[ʳ&W_*O 38 rdX/:Qާz/|~gx; nR>eQzk]z`zZ;Pk8>5 l6˖-[X\/k>ap_ -)aDZ>}WoBqlƶmLDDJ/̗<fWkn2vz|ȈU{J~m%`YBnO /\8eaYi'[gI/3 OHc`hA۠`58u̩ Rzd$cJ>W1o1244֭[ٰ~'L_&FtAF'ScΔu,D Gxcǎet0 =z 6O`ty9"4h# _t^*KtXe˵4{ܽ{O3f~>xE]Fu(щ<ꙸ& ~;-pgrueh۶m R;v$O ҹVRݥDE A _@EJ%tEXtdate:create2012-09-26T20:26:40+01:00-%tEXtdate:modify2012-09-26T20:26:40+01:00p.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/dj.png0000664000175000017500000000576112625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> /IDATx˓?3eڬW$B`$.ǩT7*U Q1*\\xTbb*P[zYV0#if3i8Yz~kZWC,TCe:u˩̈́LPnBZjnW~>ϻJ)4T؀4J[B$vU/D߯xryҩc_|5D[[m4zhh _\7yV e`f챈=K{ah"1jŔ?ljⓝcs0̵W>oFWjppPݶUy惏35Ъ -%aU= *9zlO2 V!Gvdߙ7\on07]fo,Y cqoa|qhҟ:zj5i^F!5W:7TuYλviik@ܻJXFXbҕQB2KRW[VぱUFBv*Vf ,2+Pe}@w~/zI=d&Ή.!qŀp`Tq4TŠSA̓z> pF\ʖ˽"Pu riܿ9R /Ivm~,b/`d^J3p`{3o@ΉNym;R'EyKW`'n;Pt0}5*fYP3l뀔.AE5 R91p6>,[?֭XwHZ& qOLBJDڹo\*\O> C]"XVX rDqr4Q7>gl! u0ðl\,+P(ƍpຝW7 oQ|p ^1D>XS8 K-.Z\8G~"](p0>ϛ?bׇG0ID˚qUB-/j*4Ez'mf. <eSb~Ws↓ =IX\#/B˟^ 3b0yC/yz6{ٳ v7ހΊxϞ}S2NEb-HIDo-h0a~)<lqN8VR{iϜcc8A"T#KL hP,p%ZsY:n _}}L`Jxyy=RWX0at5Y-j_(tc7-E)Ba0>` 㒠˜"\;3T={Ӗ칼iFߍݱ6܅#Yt p!nbۋF\8)YAZ]#/c_f=}a珰3MLBh8QɊkRGf"r#n/ۊ/>$[pH[& 0 ;_uۊ=ۏu1wMФ(d/ɝ@US:2KdmQ=i- `ŭxi3jij^xd/+V_-ŭhq`-u◴f/ `K]*bR+c˹-4%Sj$]yieq@YY(l3ԥ\S*>hȉuJ%Nu0nF:w"@ ')8V:ŧ9R#օuw&0xm7ڍ| Zq-?hJR剗y4Q$3)S]۳ ?u5 <pHBf<ʖVvM슬& SW7{-5S5jRp()wr/[8Z:XU9YҞ5勩epWUxs콼)4*BvR*40ڐnƌ칳SZkgǫW^ĹXZCmSN^[4 jSm {>C-{'īSQrw M${@-aPTc6z@YLٽg\)k NN* KMSL:n`$Pm;'0[`Dk=G@C|fΔΕj3d% @:WpS%p17M̍m.lB_y,۝BJρ+A$ &t&ĖvWa2Bsu˔:MI^U}Tz:g^$ \i6q%tEXtdate:create2012-09-26T20:26:10+01:00͘a%tEXtdate:modify2012-09-26T20:26:10+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ae.png0000664000175000017500000000432212625666430027117 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏǿgֻc{M!H\8r qā9p₄G,A Yr8 gm'dvwꮪ_=32MO8;}Oe78%a䉌~ʦI#O|/P(׏ h D"J> ck\ ̬SRE)]ZTW~^_7GjbWT,_/><*K!h}}Pn_~-mXXkxDAdJR)%ꪖ^u#z{R -><lj^Eww1Pn\p.P#-$wtnK1bBj԰b\p p^#S1}x͛Dc^(+pv5ʪZTH94Rjb5낣 :'>,ʝTbox/Na\U( sUIs 11-3筶![eӈ@cڣ] SGktA$ BTu nZp{f` FU"5Oh7hƬc)ߪ&slO502B#GcmBkVe> /gk/}21-ƺP'Rʶp,Y <,p*vD;0[%0puG$ O17{xfto;l5KiN`)1<_9k!+ͧc C }5X:b"zn ˓m['B=R3jq1˳,>gqV~UMdL&8#< yX׳V(TFgLy΅jt]RSÜqgR߶Y|6fy̌3eMoiD3o0?߸1$|U2i8fhd~LMȢ)p+Y-w TkB1\KWa3Pr~7K T]5#-L-_@~{^4l U߈[Uo"t{K UϦJu&; D0 @|mJs9T ݄cډZb~cM(F"i~_)7p4̜b4MpxxTU$@--Ǐ6!pUuB!3%!/ճ;wh\{w<)h\KH˙L 9\yK Ѕ|h9QW,JNU^\C\ŭ` ߈P]9b qr.<¡mU,LKdwVjdf).5EJ+Dhfz`G#"}.=*, e5 c5R o#}p P`ؠ]>G4Яq*M꿒 r2TvUp!]w<h޾/8FhoX[f486M0S0]S0J3 }Èh?,ѿx5}יx4N1)ig ڗ.VurMEQ|2ϿDCLwvߝoFzp^#".AYN00eC} .EĥT[p>Oo3)季Ϛ-wȮ ,l+{{ioO7a3.{ $^6rhu Nd`H18p_qXDB KhUw G!+&Ѷusf_^/6$BئOo<R(nq =þ*1%:%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/eg.png0000664000175000017500000000446112625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>oIDATxKY~wIF3qanča! ʅ [@…(8 3Qo[.vuuztPUϳ\sSgAN!M35 K g|_t,\7f^4#"Ҳk${-59hlXrݺ>սwC?Q_"A,D~%.}5uј^cLHY*Zx޹l^JgE~; DE@1(xD_^OVVWt:h\9w*kYpEvi0x!|ZsߋT `}׬~^k",EAԉ2gEIK9Lȍ/YӛQ; ,"L_nbH&XZשnU/I2 X#B_E"W?WI10PQdΊym+*?%7Ž(@xJM޷AװH:\i^8{@/aa:Dž*xY)rH6Z U6UL aa?&gRfI2 fѽ"˜SZ!+#0~1ufuQ7G[U~_::gUy;,zMeV|~8QLU0Zvam[_CMgtۙAorl^ zA 3oaHk}` ރsEIC \++ U q?yYt n`b`!ܻ7o­[ nl / /νGx}\^vYUC `}ΝY L9Թl] F~tP}MSZ$^MENuI\US|x?i1c(^:3=UAs{o1#ݥ]x ǠĀ:&617I xgvѓc;kx7܇ÿ}>bo 4cDeh/\􈠻gwLW[F'mTD$.QxL1}4(v2XJ# @VFqgh8}#GI:_ y+?H8U#=ir5#P)ze/o'Y:CwzxV? wx&b{a;,"3ϏnxfOq,=EpDcŅ`0^zUD:'mTNKt/1y(G Bo #|LgcΌ$8a:g&+/BoG]k`.<Ec Z{iYl SDwn n&UAx7$_ƘJg;wǏY^^ֆt(e;!bzSnc8Zc?Q C677Ç޺u۷oVLL/ɟR@uu[DOeuic4=p pwwWb9޶,<@"r ]:զy;?yƨ*"r 8hdꫪul0)&{&ֱv,䵥 TWT= k\#Yh[*.jυі>LGSYK_[m@9*vgu՘2pi~!ODfRk !uum6Lo"oiaf+mk""2iQ&x8TNwD 6ƴ:rN#Ӡoc4_{pXT#` TNj(>[,䴉v'ߌFɁ͆ %kವUc7Cv(J\Mws/>O@Z`! w#IDATxytTו[JSiy1m<8NݱN^Nܯ8c`0` !h@cJCu-FNk//{ U=uw9dM7}?K|&i{p6>==u ȀrcEsIМ>53֍x'!VUчS˄/\$|]k7-IlMR'];I2o@(?0B`pXxŬ\9UՙʬVΊ3E؉U)|JQ$|2!mؙѨ/ v=)S+,pwsjFmL/i7t~C<؆YL;!GfA=8xݬ[\Z[/v/)?F {b0##Y ,04(45uffƍ ә.]2ط-TSUiK|~!Wٱ##N\|T5)>EqosO}@,$(6p+ʼothџR朵N_8-bւJq˧׋R1:+utLd-/ b#E^S|µf8pG<âWHE8IW$hmqJ@bBňA"7/_$CrPf.sr=u_<  V+p?V 3x챵,YÏwjfAE7) ۶gϞFu#^n`[ޅ<3uh4ժ#eRK (M(%b5[x]TIBJ8IV$Q'v-H/ffPMӰ`ͳXsc9;xf;"K2Q5Ɗ/m&Ʒ6o4Im<Z,K;Z; \ed$L<"3gϭdZvK" Yŭf^@6]/ K"{2eGՊSQL$*F#CJKÒ;qav00|c*F(NToqʙ+Xr780>oޓ4 (sr)sݲŒB.t7@ZE1WQ$ *_\>_` eeYdf}41T5L4뺜OMa<߸Aw e"RR0 BAA:#!Ӄr/>BU~;!E"ViL&p0Og|z<-u5w酟fZ4ӝ [ 3xǼ9&o|:~< Syե.qN+JK=\Μ>`sZu>)8>t7%!L#_ZCIm.~S#t/3Q\) ~!D[^.c >pNnk~yY=k5vHx [m%Ř︁XK1i!ͳ< ZߟZ.5Ӹ馊I^vb*ȿ.(cjߔUS cX-Oj#؊3VZ~%Wub=̛L:lT޶9DOzfTҫ;ie4$bt.bjFD?Z9r=#=^"Vʙ3,芵N CUB:gӴm9t^oIP)QszwYw0!t1xH׽-@`V%"Q[[ʗmjǏO[Z_ |Mv5߅ <{Gr /9>>E($I\<3B$fxuZz[Hu23w LHr g&+Dh9.\:MM]46vb';ۍ Fik @U5EjO+L M_lxvoݩE¡,!10a$:i:w'rO=K7μ% =z'QGpFCZ\}7' " la(gD09p;Ȥo4pXLEFGÜ?o`z66IKsqc5kVv['0!EױqΩe߂?c۾zt`J8.Xm BT1)X&YMYgs%#sS⇻q>_6>Ghv6ʩS|fgX1c9| b:;a99|Ktthk?cc\. NcbV% o&bH\bγ?~`0FFBV͜9*efN٬44tۧa)fdt.,M͉_?*[XƮF:V]<ȅ6Y6msSV-!0Eʰ-CCA{~|8HUU!}h9u?f[6SGw<:C!)}:'|9~q-#[dkTp=u̜^6w7 E<z۔M[/#I2lR+qL/v|IHMupX'uXU5;x7N$ՖK5Cha-ۛߠ(_5_tn=qi4N g5i6tk}M YکIߝ᭷N5ac*hW WW_ ':3t}jxXt _O([H 0+WY)ɗxȣCF]78wÇ/e))]"de7\`OTeZF.PXINN*Ӌζ;YQ`P>ì%ym<ۑ2]4lӜ9nlfʥ @G/q%XB4-遆uSvn cZ?AuMn: GKK~IeA,qB?mmldY$E˼4-iQ˗9c>IeCRdhӔgi[rsSFj&ݝʬTZ]5[aƒ`l,Jcc' II_MMp\0 .-#=F={O3c'Ė-LI~ wk` OJs0"ظq.=vUUIJ:WDCS; =}H4^GBp^@?@nV֕%EC7D㔻/ G+##C/0rjX?TT5?L[[;?/b`~̚W-Wv|\.;pUXd Yn56]7uyyi|+W7Nʎ`&t'UU+n3D躎CUAl+ }&n_$ 9̌l0R #& à^NZ0U"P8uPcc'︣}JKJH;|?_I}}tt5G*^@MM͆O =;ԩnx}!J*|j n9v 7P3pac.qt7W Dq:-,_>-[(*ʸE,_>Φ:Ʊchj&񸨨ȧ*TGr 3(,`Z4MOO|$y&6-9s KKfj+gr,[t^c)/nv=ũSTV0}z66-0QU Y&y\mtn'Ь%::غu'۷cRv;;{]#77u8TUg3<^N&Ul6L6l}-"oҽ&+{4Z[xupw=KLO,Nx.Per uA~B"0N 1.w#bܥѨJgw=D"qݸ뺽]󝷸xq TU ph;v55"-5 ! zt<)׿~8 ^L%45uadepX?RDkIA# X!}2h{Pg>!6 CN>dlRE(-xquu(+$3J=ԤFl|yകpk'H)xuZ v.֑Ձv f=^SSHAA.`0ơCmiopt/٧thTEt$ 4 +9\v 4EusAA^zMj5Ac躁,K躁"QVEnn*F_#Dɰ>B*{A FתU:z*6I-aH ft4ѨqOQ$qP\'/SRlL2<&!͗mٵ7l%.뱤:^E kVc7QWW,룩?b S@-fƌ_fկ̙V,Bnn*KL1sf~1;㢥l46lCjEjUHOwRQϭְti]@)6i2jOC?kZd\.%%YSΆ sp7JӉb:N|-O=bKQ8Z?@mmV?m˹ZȞ=g8yd}YZSZ,JҞͦjl,Ω}\%ƩXt,3|? #h9# BfV>e9FX #7vm"Oh5T܉ZQ IKSPNz3ӝz\6n&24$" // gmM7ժJN;Vº"*y "BnLܠAտ:tU2{ ^Ax\'L>|K 3<$c[(,LJsB/qT5_[T2kE3ڂaڵ{oEɝBn;n^'?y;NeꦱeVwLCGj,}a!?>4US^mp&UbrDkĺnp`?>"PUpxj8%җ}p͚MOfxfB!I9^秱ys-=I~i-|[9y `,]7plXa][I46ٳWDG|dMJaę3{=)& R<ϖeK)˲?~~wN$= Èa>mץ1SA]&Wq6#trrpl6lCmm1yyi8V àk^8ʯ~!CCDaLrk$&6n4iN:qه1 _9ӧg~}kTP^jUTNggbڄݲy dBՃ0 !Ra(HjUFU('+LzEJ0fpplIBJU D$IRHI񤐞3N2-z|/((>!2i.qEfSp16A 0\6l6H$H8u̔<0;^plii-8Кe"6 %T'.;QZ!Syvn}0)YE~V2-\ŋx,XPH R_;9qP%IL TAQXg>o2֏kf1NhpX|wQ^^8IQYҌUǏ_fփq`$1(ٳ77el Puuy.]mq~3a~Mh.GYz&|ݺ*yFy[rhr 'JsnzsdDRZꦰB_N>JSUV_߿G։BOB *LIk ҜD"q"8񸆮'y|~֭Jҕ& 3ʕ3ٴiӦe:p,KKȷ)Zýգk&5O4Buu!7RWW" EDhi}3z Gٵ>(N /O:cca~ IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:04+01:00(%tEXtdate:modify2012-09-26T20:26:04+01:00uPIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/nf.png0000664000175000017500000001002412625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>RIDATxY$GZYgw1=38XiJaA $x'YijaYϱ=9gz23>**2+5Z5?|.E=k? "\m Y4c<a@(2tqOgfJ) RnR*9E3c޽VZ{ʾXHW{^| H13 +=y|[Y\1f`)T 2Ù%‹ϫ)u-J)ʍ9i "j\D0y,t2]wNdL45Sj~3sw y5;;K6EkJ+B)BnZ!e(= K xmE*xFLNV]fyG5-F!x(#K8;tAl1$dZѱsPC6Bb3åEKh1lu1(S܎Ό3H1azJe93  Z阹jc&f"7pQe?PnJom"q.gAL"U:̨'蚱7,P0h0Ӱ̴V:!N> M\%ihJ^H#A%i4,P躸^։5C.$͞XTL0/O~L>.Al6`$:e;اHr@>&Vi,kSCJ9ll:&xFA d5 ( bS&BؙJ?N<͘'0ͽ >s=E V"t !&L UA+[N^9*ЇumxICq"q|nJ8EɄ#JF\G105_;nw|wx 'ÕK'7u9ѭ`ʰ-]*ܽƝ|筗ǨEZN#te2'C`“u$osyC̮Q(X^\֨U'o_ӎ%Q:JF@ #gFD$L<)⍷xs5#ʘOy@ŔؿxmD10vL&< i 0B)?ڬEp$K[G9<:LqWd4 vb>ؠ[dcOr%^z4:=2iov=!Yɾ*BMy4apfoifYz„'q;\z)h)i /xzI_CLY, 7\OX ғ`)qti&ϓ)v; Bi+88.T$1"HX5*H+9k$Rf{94{ 3O )%dYήRsv:Pa~j~6٘f½륟tHQ2!88Rެ[uTC67iGhq:sx|K&&Ph:ȸGo)@Ȧ1T\.υ//sQ(QJc,%.CSfqI7uj>F>'9šޫc_gavl&z5Us@nQTRx=JLK~5o&]{7|yξfX2#>eiޛ( ` +ؽɇ/Ro٨ݠ-/q~S~@tET6`${&I:{7L~rk;u’^a([4&Sp 8R5)9=΅'_9bϮN[6xlǛڙG<4&^=>\a)i┳Bq`tݔ0$~ܣLQ"#9.z[;Ce$F.qIH'_` u16@Ko}SӋ,^ '9?8uv-7E||u6qMO8V-Oa\_(IOCZB&C8&HNn%cy(|cɯ%? 71\m] ET1RPX~{-j*Tqb~jn(2IWdzT: 6iq*jߎȑ$SqsF&ũ)Jo:0yU>{67_[]}+O8"dqmВ%?q%*JYb@&._+3a >lMvhPoFmͤ=R+1x"?:6O ۶ɏrǴ4'{iu1`3z0ʣrQԕ~ű7q@+A@ZcG,54C!&oN 72 LLC{n$L-Ā@—4H;M5JcQH_ PiZ[k<0&/ۥT4YA"#~L`R_s'dxwh 1j1ydD1P /LYoRy^3)k4βx7r<$}Cĺ$8>-:! @lb0qX%V.`|u]܌;O*Zi\VtDN|k(*LPVAM 2/oVl n@IƄ[N`$p َ<L]U=[F4wep7R~7d)J:NJZ`Z'Z "u-W60 @h#T[˳2/J!FˉD pkf4tvPFn9Ms?n+@h0قd89b̌^Cg` $: Lss̃c tجlPa 6~lA l! bP A<~u>k Ձ 50J .u+!9R;}$B j@#a ݋i \3(l=?͇^*"0>CIPC?"C?! dc\sYe%tEXtdate:create2012-09-26T20:26:09+01:00,%tEXtdate:modify2012-09-26T20:26:09+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sr.png0000664000175000017500000000514512625666430027162 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxKG_uόw HL+B>P 'KpC ^R YV!<)@+FCzw]ꞙ7[U_WkrM5y튼Byw4!Mę){5qWBH;7uC7[mADDl_7^tЧOWT}fK^3ֿF@VbUIH7;͗V?mbɡ)BJ%c /Rj\`e4%^w;On> ӊgv~zu{de V2FLeVVϫرh}e- `һ;%[wI|#f=ި@ϾNzG]֏IW$Z#fPd30)Y+o ?UT<n mHd+ܣ]UZ:i 0qeָtSUbE+l`b]muE6z]U6q2XU.,e9v ]:{;$!q\Ueu2w}teÌ}6wڜ e #qkkktf}znm1ãsfC,0.[et*|n}1" 52_';o2V[ *|1qzB[҉T)_u^'"y  w: |0PCr)ɈY$4*Ga?R/KS?i7]P 8K({ qXف⦔fVn7nĿ)ߗ)ܤq"8ƕVqzԺJ&JfnB`?%1( ܮp p/vJ@~ <嚷)PY6-ŸPS^,ii֐/#Y? |m .p"3 bݽ320T::5 _n0s-unSpl 6o}eg/?W$ zD/)pJJۉ(b|3pj^(e6\ښ; 杩@ip4 M‹ ~~xܣqش5<NUS~ppI` _:1MIHMƗ3cK:+x|Fa"a} *Oӑ48z<pךLAkP.lĐHBjRRdDDHLnw" $[;G~t=+ˤ4\;^"=@}q`ƕ .]+gس88x911vKnUx+U?`/ZbPeY\Q&?`at2.?aV\0ڽ(o~(_m4Tw^Bk$<H,`aFBiC[m5q@U*.rdA'X8XS}5C dd75V\.-h5h}S8 =,v*f^o?>l+VhO`􌶞i=>8@*GZ'QwU0 VHɭ'QG6{c2BA*pxc$7.X5YITVu%FJ?e/lʲK=X`,9opa?*\n1ƾہw } (9u_ripl: ./;&:59)'İEN&ĈI~a7^¡ AjwԼݿ& S2ٿ-j%tEXtdate:create2012-09-26T20:26:32+01:00w5%tEXtdate:modify2012-09-26T20:26:32+01:00i*6IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/it.png0000664000175000017500000000447512625666430027157 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>{IDATxϏFǿa7$;`ŢDm (r@O gH9qE9D i@Xvfn8eWaƲlڟW?\ٝH>Q9 @Dߤ^/K>9,DP8 8{xGty #cpuU+glvz}}ml4ϯKxY@E$z*bh! ir9j4ݘprru\~ n!_mPdD(ŽH}K佟!?ǕC2wIћ4#"MSP3@10,zTKfV`@0c`N%ND_PG s+(;<*e.[b#*66M+SyUI,KqY\*o A\!{S5\xxt9-0@Dձne@ `Q^ TD̊B H=+hmT]Xm0=ܲ MpxJBAzJBеf >A.lQa{FGj<Т>@A]ƤG6zi/cso#{a̾Fj8tzd 0@R^v Ṱ1zq̴Vfj8gA 4_!WVd ]%F躣U9*'G]./ 4]uc\VX* ]vesW>]>i@zGۮ5@Q׽ Qz[Ӑ́2݀хs}zZ eᮺ}Q$_\ؓ/_q2&)8H}x+ 풖 G}`GD2CST*ff(04]8PۚϪjQj:=ӔU/DjApmǥ>;1ne)u[Ir@lX/LĐz s3/a@Kmb 8IW 1 'uBv"([ 8nݍPdzƋb_$xhq;!~񒆿sa \8 Є`6c rW"lHB6E,԰ Mׅ3ʙn:@*U#` _&L>CytLV#nIc!-`_A'⋁}`Z+ C1/Lh] PŻIے]+C:cBts]yzAFte>:|e4Q3&{ BMutcKcwm ̭ͺL5Vi8Kx"(DXl  Jpl6ջ;Y@{<$>Oɱ Y|95`L|_~<9/|Eh J-`ֹm:$A U{ņ: tJ1<˝ni)0xHDήZ#DGv[3zuI&ŇZ۪>mfkyk>0(&aƚtnB%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cy.png0000664000175000017500000000623512625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxmlTיe{x6%,$ h۔,A˂6MwiոiwHZ4$*S%HKLEYlʺ &! 8{+tY8zzTD"[>\__OhT \.8W:V`.~B]عsk5MsgN0}[DѿBJMM ~]Y͘, JBk ;m*UOAzj]\yL5mkw(Ȇt'„_`QɈUl! |7uW~kF߃@BN]7,x9NA?E7a#^VaEǚ2E{zR$H}F} Q 0?QԊ.H#]zVnߗ@bL`fJM =@P˺S|Jh 3zUhC߯d(78܆!}Rp"Ly±_"URp-ef: !A٩EёO3['43K*8D `cpB^xIE1z)uw"O s&Po&A(!6"=DG1[+8D:! *Ξ焮) ]W G)4)%+[|ڿwAq̖pF& 8HPhA,în N]l}-*7g`qE3 aEw3e~rO=/q'w!pYW#g&":)Ôbxe@VQ4f#? Z:fRN{x(^ɇ|W|DLQ~5 sN,DM[-! h3pG=3Glց3WM$HSQ=EA2JyuUD=w"'wW [dݽF}yW2sw%4< W~!ü(E/&*h3c.[FY.;/- j`ȡ lSǝqjK~aJZD֙(J{o,iv&jFޑZ_MBXAp<#[#kPV? P ScԠ)ta(7¶˫ |op1 4vݴ3?%#V04EcMķo6/~"?~ck.|D^` 2$!({C5X[ˮwvrm_UoZWKDS4;z~=S9N67l*~SMoX^lYy7.Ah#"6ahwd]}>LavO_Gr;-\8Bs`Ucp39{Ӄi]_|=V[d,l4h+!#đ Gj}qZW,-'d_uuļjWe͠+HVvn7'qP-l޽.m&O~M/`v+""u65l"l|q_)|^Ѭ-*-p/` ;ٌٜWj׫9wDqknvmMМ_r!v,A~#7Enbϻ{x-ֲsN}Ľql5{ 5l([Gs_~Kz3O_OʒKsQPXV-Kp9}m4$ܿ~n{RWS=|0)p" $Ā]7444Ms !T*|gJ(b[,+6_/|ZE$+42UuUC@g=%tEXtdate:create2012-09-26T20:26:08+01:002֘%tEXtdate:modify2012-09-26T20:26:08+01:00Cn$IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ve.png0000664000175000017500000000606612625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> tIDATxKS}`lcc3p0,$KHK`M,e`UcRY"ł %1‘"$!A /0L=c؞ǽNէ|vÍp#7o J{=& &q}C :q6!@4]s͆xNYKvM ¹jݞ=uHx⳯U "D_Nh'i_f4n51&fWlqKi\U3 O dbdm-?jvyzvk[ff6l6[sq-R\%G]ٱ4nт&ҎFG#]Jk?ppM75 i.bʊJˎ8\l/W[~-oBFzbfЏT:[67OVK@7F&wFKZU9+8.{ 8)@5͆0&dmr1q5 &È `UnN->帊iP{iϹU5!$"* `9Hf 0e^yK)αU('snRq%IS1d ]RQ&+2Q鮱rY?9!pl4|y1f3HcC~,$`ѻ{LpǪimTkcƌ^ *֍$犵=]FDQD\L1HVÚ9na`aKkW sk#V"ج GfZDQw" C){NʩS+Сoɕ={h4' *4.:Թ/q j^]̀VÃ<<.z=套KXZg7߼@*A'UX=(o3WW-65سg^ACu(Ī"WfSx衭<'Ok_'e5&ŵgO<{9|xwnWuJAx`'RmrCvAmfX\ l+sRr{طo A YWw] T`)?Xvӫr.qo 8o~75Q#ݩa-Q4Ac@(Ǎ?^[0|l{3 l]wl3f<l֞9iE Ȏ 0&ۓm+0Ck b \.x+ 0;X ϝv« ՄCaH>&VG Џ|˃;^#SueHOI9pKCZ ˓~˶}Cf#lD/ \9%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bs.png0000664000175000017500000000620512625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxkUks_[B[ /`Y(V AJmQP >b4j~hL4გ_CE#IQJPؖ}sϙf93sf̹VCJ3{Y{kיrRNI9N}O2C&VgJ\qh.VL@߿31F9qHmI('n/.T4g)"kTU1VٷG~Zu;#@ج%gxnDeTVf̜ZYBkcO'U$0>Yf+qPmOcuJ#\ȑt?ώ>uPLgoSb;XJ_O=oƻh1$ -FfL6=iM7& d_ \wֈ}_xc[ Gi+`26"T*8UݎwyTۘ6"0hZ/<Sa62уq{?MǞzyhsJ3a}ySjZ-1( K;oM3gtT4iYfF| (}Y.vX^F$pvEO>2Rcσq>@يb@X|j7#u֗jˋx6VtwPŽ`UKWE<+t0mz4>b Tn #MFJv)04std ]7ƿf:mj0~JIMy-vy;Ơ{vp5ɡۭK֗WnM me7Eý"w5xb[SMuYBDU ǣGsު5IBo=)M3<>ZN_G'm) ۢJ{[{+r"CLw"Ԗ g% s޸Rp(5 L6 d>bx֢۷ax]u38k\G%7&t$YX22 >̶y[Lj* u вtx=;땕˕AmP$rB\Lh/]CRu X gw\T:^}͆{kxaJeJz+D?UUjÄ AcxSՕ'V^ )#J044Y'紷KWq`K P{?HU3Ü/NF_(ZY)9Ֆ '7|ʮ'ѺzZ`d^^]iÖ"]Ee TG-%p+ܹfͦ ֘DS;/ c^V{Ŵ#ʞ}OW6ݧ_q4.LBu;ōy6Z|*r|y#?S#r4k fML4v-Ycd%)#,R”K{ D xYmE( 0o|eg3 pEAa 5ÌB )3P!߃EF;:q}޻q(-$aHZ:9N+2a@Qݭܸ$ [ 3ڻEKmz:pY2kCw`a4ε+sTa}:$XCP>,_|+pb<G ˔ȱciE2H)fyŨ*3875ʴ)0l .xb6aЯԬr)CR*Pt 2zb- N\fx O}Yp`6P6UQZ@p(?s'_$eh/"E7a.J/kP&o.nip{t h0 bq:f+tэɬ٘5WPk\'R}Oh9ޒq(kfr0 F/WHo/%6a,DE~qy<}+Ce\PŜ}x-GQg.mNbй`f:O;^/\ "M*c mh'[P(@Mkc6Z Mwc.^ f8n3iӍw6;x7FLE:̵K3ڬGȸv4,Y`:PE0,psö $rv!Ec=8ef+ߠwU&v f w-Czo<)vɏO_.p kQ=Nixr+flӗ qd,a ! KL?H]5'Ӄxwl\FFvX`biqu!jaTG 5N=mA g-`.UȜy:7o/k1B9_y]r>80:}d``N.z@.\*fEEoxy=1a|V)gez,`yg >iQ^ӄ?"8qn~yZ.>8YzzMfFep ɯuND6Q0p8|wg)PBEV5`$p$¡͉ܫXGm۝׺:!h>n xӋÉ˾4JnVtqY'x,MD*%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/wf.png0000664000175000017500000000527312625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:20+01:00CB%tEXtdate:modify2012-09-26T20:26:20+01:002'>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ck.png0000664000175000017500000001302112625666430027123 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>OIDATxyչ{zzfupp]@eQ .\*E9jO^'y9}JaL$D(def{mf%$>9Uu{[_ӹ_"sGҏ33if!ІAQoeHnt]ɪU;ŪUQ*BX4Ma]||UE*uak;QKAV[nq_ )7ٳQUpXGrX3WRF.RZ)2#4ͮCwa0A2WU1GYo1o/FfР 7?#)!Br >Fi > xM54%gr\:Mx]~9s)-{{1B ^ZsZg&6a_WoQ/ W]K+o龟ҿ't)/\H鸑3+?q ۶0LTUV).)YPPUEiI ŭPXX*I,fά᪱B(FrǍEagX6o>PT3d2ּ8<Vۦ)cU rqWRQxzomhڰ~?// >8-X@L>aժ]9v,H'УH ]]:Ke*\Uè[J\C|s@cc'˗oW>UUbo,hG"e!DH"U~CoKgʙf 5B@jOC\6{ w m^C{{/ӹLT3ϼϻ0$:2U@&$/?O ?ˏq /gEx ֽ%KŽ_ DQ0bn"^2DDBhB{fqTӘo6(ePBx.ٷ4wN˧AZ_|Ou5C1sf #FQU5Cҵy3BUQTW=£,{h9t_z-:mEZ:6иL&.&oRF0ٰͦEƢ gΪqO3Ms* PTqٴ  x<\2FSZ7U)a ( 1 2 /D+, 1hhfy;nm'ۈd3 0y"Xl !q7݀'&j6£^>ʊynTE500;9&Xm7XUڈiǏ2hoݤ ┛Bb+'uε2 @:4N,fô9ǁK5 / @ik{k^Uŭ(Q?$@Q6t jia#4zq,,$t 1#@{V~jo3c04M%oO>I;0`8̟?̲#*/j3~4%ERJblz /dȻRr?֗^e2_~fu=Idxs]i%Gss7`[clƎFhm$1,;D{?!~ev gp3g{`򔑸 h|1}g3&~=ju֝4Stdu믟7ObX=8oI%(zlϮ9-G0 P*eV9u;O0yr5w=r>_6?%%TT3eJ5>Sl[ɘ{8~K5s8f"beU6p,=jd:̣ ?!@U:+6?ݴqT,\.K7qT^\.B;0Mɞ=uEE44t񨘦AT@EMxsKrD#X^K68uǧ#F!ior:;{ꏼDk޾z1h7ai8S=Hɤ}KFRr=%:;t:pXi+/U u$bo_=*^ (}p./6#!`sYp EE֞0yvӔ3g,>.6?Ͷmhl 5揠%mF_h?IaWopuuRr38qzaٲgO=YYZ,BG":%%y|ٜ8\sZZzhifɒ* _. lkmNaX{I'tu9~G8~˷{v#C3gF,gÆ.gZ/˳7OƔ)HiϴiC<AY1kV yյm.F,GJf tC.祂3i,/2غ0Ǐ7ril~>O]]1^Q_ž='x}N? 59bb9%D"dN%` ^N)]q__ӕt Gٽ0+Vxhn~/#SflkJm[DL/P b5.(4E%5].0VkmeR*_꩷"OG ^LX ;|uN]/as-PikC"kZ`@$cOwJ|@ P5S ?~U?2͞@0NQ!'tUϽ|@qt)2m:_5v5V hdžH,mBusP ?@A'HA. я. pE5O\L,_"uJM[ُ_"3.zf}L %tEXtdate:create2012-09-26T20:26:02+01:00%tEXtdate:modify2012-09-26T20:26:02+01:001jIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/si.png0000664000175000017500000000617712625666430027157 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[lGv}}l$skM4 BEQT(DU*/3 xTP*-K4i=c;ݟc;ڸx37Ϝ]nfz&NeBҹ.0V}7rV^-![nm{hE 39Yr;v|oݺujUolNu#s=##x^zwرuxxKCCC288H?<1m3::۟yꩧ^`(on4:===8Rez*PըK86@ Ԁy-cpuM@и20hgzl6'L+?{ .O:,o 4[=mYInSDzވMD$AؙE>;w'rwOs8c떝Oifԋwsz4:6􎎾ی"# `T=ty^2wk2~L(wå&\lq%dl_{Db>4n`MqU0Fٶы9rޚS0}\#4%h\%]tlfj_Ho:>n^ͦ` ,$Ay#(X*Miۺ/J& 4C!0pQ?xl-Az UjfQLβO8ܾin[^;׿x ]/p5{_X, p[g@ o3>5l-ܾq1|#MΜgE$2n_|4gCm4q8l*QSm·nE6ߺ,sǦ|]W_{ZڸюR8fvzs}n3c8ILt'F*mnYϾy6yxNT[ i"FIJ_D5@5ّѠMО&D# F1#ILb8`;;2'M2=imL8†5M.N3'MPD^êeC0*!@QT A T.c}U 8pm!(\4}ؽ8@mKT.0;䟀DV5B^V/;,0aw&* ;"\>v9K\u ;?^#''Tut/+AYU 4ԗ]h *y]ovHg;.@/)=(olMgؽlᾏ`lߏ3M HŦ&&6]B-J{J#hYSExv'N0'8‡8oo|}*6<X`d~ȳ=Q--hƕIVd *8rlah鯃~Ka2yyx>rRnLFXc L˿znc0@Oǰd ;1+ ^6/:@ZD D!"H:PR fmʛ<\i՘׊|Qb%E͢6R Yy⿤li$sL,VdDOG|I_u*Z,2xI|*>P3qFfKS1^3+gߩ{4n1y" xX-{mֶL˜/;{2SYk(_SjC\,yc!W9 Q!dgDJIO$iIv]Xa $b_yd%q>P% h1WqG}D\6V̈́DxG [P;,5T$/kuilߧ(q+xL}<8}U^P3#Nl#Вt[Ef۫u]eE֪|Niwќ%5O { %U!׳$[Lt6K)&չ#\qk\t1fvy|UƵ˃՜.4+ҡkA9f!jXG^)8zVz:+퀴C-S02#:9.]3N|+-0dKxo).UUXŕ>1`,|U|t̹FڡLaLSIb똝'ޱяUUlmUZz`* 4tK^YņP6L4ҞQtVZ96YM IWUeVa&P.xC `,#8Oub@! Rpes$ ;"SA@n3/}suԍOęϏOt`(Y %Cj`8 ߹V['h/`%0? SS|? 4" `:K bl~i.DeC%t%4ݱj;-l+dġc)a^&:K/-6!Yҗ;B*KҗwŖ)<R$%ǥx3]m ړ%tEXtdate:create2012-09-26T20:26:22+01:00ݎ%tEXtdate:modify2012-09-26T20:26:22+01:006IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ua.png0000664000175000017500000000446312625666430027145 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>qIDATxϏ$7ǿ?fz2=f!%WVq?;G.+R E!BUȁY6 ڙ*q(We\ݞ(;RWUJW7]{\D0D}Or)#O|'PL{qrM@DD>дehԞ83\`?yWw1tAtՅ wo}{?vh{p8&!HS$}ue M1~j 9fR EPg?矾ۏ,5āl`4^퟊=A.!HJjp+ +Skc&hW1q&yu(_^LLkpl<y^lss'^LGg|z] R@)(^WOlS8p:_LkHc߷v/lz>z bm7ыk0]جph貮n:_/&IVO LX̋Jv P)@J@JeEg!\j=7Ԧ(%(EdY'Du`*EQT[<\YV?}52ց>W:X.K}Xf4'LΦSu̓m676Vw_londxMmxRVq12riC Q)}WGL&͛L &| p6#~-H~ςCf>ߺ/].w τlv7ņc{ٳ  cfOoRʏ0!f5ħv .u.ĪC'  e>PGp,wDX38p9:rW lrP%c0).G mzukDصZ|hWJcky +Oœb& R?zXsZ%tEXtdate:create2012-09-26T20:26:33+01:00%tEXtdate:modify2012-09-26T20:26:33+01:00]==IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ir.png0000664000175000017500000000765412625666430027157 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxˏ}?U̾HZYH1KN1 Æl,[O蠋}pm@:8;dA$3(K2\RW~9Tl>]Sa#gW߷UjD>O+> "G)S.\G䠙5]4 n\u-YJ)P(-=ˎG^"'O2k> @ok3b>83QY^^ljBmZ޷ZWW^gBS`YkӳsHTJA+l1ZvLbe~RќA'O fFvD#Sy =7LJ4IhV%h@3:`AiZiP.n4JPcG_T|xO?'Owv=Q9[T涌dTdh(@R 1^+"N$Uj)rvE*  }x}zY׸nc.1qpd'OD:1DDgX&kyNx|9Tͩ>CKLX=ShwAB,f kJiNE1J, )vܻTʞ`(*ܛ%cgqf At. Sk#'Px*g*s\BuAR2D*u\R1,Tfq3jBOl $rk,i\ZifKQfD 6Z"R1rH%h EEjR:Q4Ie^e`tJ)4W);Q7Ht>Ji$d%~VAG@|}ZE* Ĵ. Y"0LBf-\&!4dԤh2Ziɐa2W9QTeMlE;bD(9C7#Q)9>>veq@Vr|4b'Rʸʵ!R/}c͌@ApvŽ8M~A2 L#Z~ _C+d@/#"Լ*ݨǝGT2 !;a;>ǽ`W%1 4d+lSj8J|liHFl ۴`_g Ӑ͌ 1 6Kï$4v-ϐJZI {5q@‹ QQIDԼ}iB ksWn5DpSWpC/sƓP(q+ ,\Dg`lzmzyҬ6 O4PJqFbRю]X=Vw_{.(*q:BT?\ED+{;PQJsvRr ?qyX.pDYa3;V3< ҚZxj3O/|zJ.5ߓ>?1_}qfk۸מw)OK_ޓ^81-++ig`>>Rj N!? $AКTUO} UpӳAԘH,0e&p*_$| &oRԿu۷:_$][;U[1ӓzy.@]$I:͛ ϓ>x' @qΝCyYl8;G3?.O$y÷"|MW^puaEFds՞Pk;w݆$g][^3ϐܹC1h$ }k _~x/?l>h=o^@e?_]IB?_&э4}}p.Ow0;;~Cm^x1MZ#~}>wu~P' Hr/} EL WR~p|/far(:S庠5" ?o~JQW m 2¨I24E-UD)$MIpΞptBL!!zn1D<( aPtk Cumm=3"Z U.FUZiҏ>YX%KYqmd8D T:^Kz,1$!wg? 6,ǘn_چ'cHm$^3vv.Qex\ny(HE7 EduF)EҔdeλ"CeZ%c>mc0Tx}V!id3HyZ͂{Rrپ!ڲrDP]LSΎ>uT`o킃oW%Ip%Qv-y(Ej!nEj6`]*Y{:(ϳdl]n61(߷I.]Bjkkvyו8KK('Y_G7vẈKvEqHXvӕ+8Ro}@Z]-G q e2 2XZ-{,P 2Dz=0ƞDZ11)l[TW8(ϳ"#-9LIuDe1]Y7֖ 9Ơj5e{ft(*j $Q*12LkyľϠ#pkq4A@}mvZ8` HߍV+_K/$I9[ Qu_׈3L}l8RrmqknET+iV5m#' 3oh0Σbq .x|lϹRw'gNAAe8D>Ʋc*Űd?ĤKIDATxy\y߹gzMhF!!ЂalȔR+;/ /~qH$zqlc;`!Ђ2Z-3ьf4?{3ӳaR㫺ݷνwʻ+NK23LZw͒fl2<#Љ@er6|f]|F*0٦^`@#H ȌIkڷ&_nj84| 1>@ֲ|t{UGK*6m\a%]O.FJG2D>U5ýmO3KxBƃq}g+X>sP') 甸D|8KgۨӔSn0 3)n Lq]Xq f W-ITc/]=3&MB@~q#C@Di ,0G.PGJYRF2L=I68> ,]Cq\qb6!fYB "%DΧŮj.ZZt-1J3r23.!T0Ib9DtPm"#Ȥe%N芈$Y@vͨM વN* \[ȡ4DҦ&'ݑR,1 ~Ye~OPMIԝ{0_^K"uf=\Fa-^,qrnWy2L1?K/B@\ ԅSVnp~'o#,bbp< f9S;-H;ڂ!ZӠ0=Qn,O6]>?e8(MW3_9r{䫔9,6G>v *s׃bkSeN[MSZ%Day1!>BXY@l7XT xJ !6;,СK/i.z3ܯQ*V:@!gQey=;TI=CZs_ҭs`:Uf,>ҜJNѩQ!2>lJ-aoe\>r.Fkl> d/qrJV3[NQ-'qqʔey/ p4M<5k'Au;"q'(x6_!_$uH2Rwy{`%w8/Wi9l"8}2Ę&y@0=:"i! %CKdTfl馔vӯeXhvbf7D5uA|OW4 ٜeTVQ&fڈRD]hB̡5q|f/-Ⴝf&:&1bv_eγ0S%1xtT飞 +K2>3poJJV]SZ!;V ^rȖ"tԒ r\z\ѭP̄{|ӧ~[YUP%gj`b,DP&KH)LCR9ȀBiHЪѮKhIbg\؎6%TDZF<ʥJSO]K؈ן>R(SQ$-5HM1ٚI/n)fnr*q F !,iUƔ q0Ԛ3ۥv> 9E| \Tɦ |E.bɧD.h7rgd1LE0i)Zūg`Хu5tayEU:t6T@N\9LYF0 ;5ɸ<˱)kGio)v]f/sO,w_Pc%[u! Qe@3|#I}tk-Qrl&4aJF,olZ^[bg7ZD4a$}ri54Yϡu< Rz#n&(ElsKa5͓bM;?'̠"K@ݿ wRoᄊ^Ri&)[tQeNrH?2]B>DƎO(\չK qY`ySnVT}r9O\f#v ZB2:&F>v k<:vp@5,0{u8s~|*-RO={^O3h1|EXhvcpȧ_xp0uf|nvۜ>KTIvJ9F{~0mvf1"A߽aG\ 1 ' iǂh7> HdTzC S-h)F2KV$U} ZG?rK>!Ҧ(fgVѫ5'- Өr9˱CndyǨ?rYWP(7x&t%#N8#ߤ"~byj9#Df'Er3r:Wk0^]r.R" wkC%xp1uW 2=tRˡ!Z:rJN~aZu5uk<9|<6] Rd!KKg9\sVEXf~(}Tz?$X8|vn攽ΣAKk5Slu9= k1gJz7#.wB_f WWRt||^6:#N>! 4 LcJevJ7hx뜟R+9wʍ ls,i`O9bOCp,y=xn1% R x<@T Saqv5Oh+FȓYm~Ng]4ح\73eLOkdJ>azuJ"  -tڅ&$u/WSBVdoO]`oŮ劮dPґZGC,2y0}4M>I֒E [". nnfHG/鸛UXhs݂G+sr"nُpFm(K>8ʸlpK@VڵNY`erWϡ69?TmI;qB^:sYsv+oЩuu4l"I'"b$i6Fr*9 bWqn>(# ~&pܰ鬍L>IxY,vpYRN%]@&ʖ᜽&yk'ȓ\C6!rd 2D.q4(d3z2ptR[jbӵjr)P.g4qUT:{1Vjl-\qj#oP#"'3#;R1Ft2e1E=#&tӤ8Fb.gi s>By{5/JF+2nyLX 1rc<HHqaq|8xC\\Q -L7R͜Щ M|*QklcG@ `=g(u,uXp7&e8n8ýy8Bb q 2TY!(c|<Ĩ'1D:.. Q\s/$>8'b8ތ4^d0GԏEdE;**k RA?Ȧ kr `q"(D85l|/ άMN G bQzQG&ʋ>w5r?8fb\ 8s ;tRFVa*vKBs~J'(k7UTm`,J<2@r_υ3W0!@% _=un8NT:9d;2%D]AL{ik$pE#D/kϟif@TU~Se7V`6k^b*=vzUET{t9x~/HÆvgE@0qڊYKgBLIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tc.png0000664000175000017500000001266612625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyE?Uw}0@HȞ@ $ADA>P=AswPH"DI0<ըldO&dd{G.3C?QO}Wu7|>MO$I~Ibc<9>)M򔗟h!0s f[0Bض@k֬*/0M!@85;ߘE׃v!0_ev]H)7u4.Xv3o~pH׿naŊ~Z{]6.Z35ZGk'T޲q:b 8 @x%|/?_^ŪSF(2b8jkGڍiJ!PJS_7)MX]=ֶ4aʚee! TJQZVxBȋR3kxtu|XЂsRvB%mBT<&Ėl@J o&?3\U؋lF3dXƍܹ3Xl="p0 ֚Z BXUHY]͐zjkꨪ4%JiLrW]x cO Nbµ5Yf++WǨs{[otz4:;lc֪Ӄg0iq̹>;ϛ̷xGѼv-_:W]̙8$,]*۷Ak,M$!dY${zR*-%cۂ)êʩ̮D=v۶S9{67Ht4q,yW^َeL]4aI!J0η BBD aV)%Y6nlaU3⧖q䮻~z+%giӆt<;bI&,t<R- 9s&q4_E篞&4tC3.BFbśopPN ʫ|Ba"Y'DX`7~F[ 1dj`כ7xcO/e̬SI2GWio'2r$5'7p98ӆgAJ2*/}sW[1K6lj4ǔ̘ABKkqmOrtvN0iNb"=K# W)Z'Qul,`zVGx(* 1 )%Jt>t=+c?~Pc#\0a5PH?C|3tȩ0>3g" 4zgR ?Ʈ]Z2Ē*#55~* D<6D̋.`\}ͥ mJEEp0Қpؠ;юJt$b2#!݋*J^v9W+H]'ଳFq٣|JUؔ,]?n}r}K5c0SY3׆O'GӞˢ!B#†!kK7W!жM {(+ F߾B 4ae=W[kKWĶl۶a}ӹjUFYQm?Q>sY*h B_*TtJPgv i]l|{2k:^J ߁rDf72.#k_g_~e_*\FA DD"HDH6S\zOMdD\['uZDۙK' 2V]<]Dc}=%CaBH0Q4}7Ν FM N!(e ԢR' }0 I3;п߄|V+S>VQ6YQy cH78(cy.RX{b!awPɓ ŰvDUU'`NZFCJsSl!8, >~!aaQp}H)%Ӧws>`#?(*_KUu@f*a3C+(BHs'mKln6ڗL:_>)QgX`2.cL @)ICC%70 ΤHw۩;/W/Ҳf-K.Lx7h>7é)<%K8|9Ukn`i?˞]-D93o(Yp3g@Jc-EOfUW6wGP_M5H$VM1ot N9ƻi~= /q;ʳn;ifb |0Px8eu|,%8]]=8{{a&ǃO൵:gL/~kwpMc ϞMԩ/[FCz^{=h6K{@ Y* ?+Tq,// r-3mLàg:{/Qqį%lV"^TZJhtjqK{o? >p*79Ǝ;!.eKs2i4yr<w!< ԰ @=n7Kp7g)'#;XW2xwXߨz.VX%x챷Y~7 ɕWNrTN~:}ŋqn_JdrZ' 8gXgA+={sJXQLI[De|!*b} Y&,'B*]iL{Oca֬TϛGh['IB3 4%%a~3:rȌc/|*F~-:8V"5~uJ8buh~Ԇa5&ؼQ3QN϶6r52uLK/m0 .d2h}ՎWн*Fi&z i1IT( ›RR! 6Rj;/^4 څR7\3CBm[3ݸ0w.041{?=˯2˗wy!qIؚFD#$[Np'њvٶAiXAQ4E"ЄPYH2 @$zk| Bt8sA8@VZ6TPTk;0}ZQiP @O" Z& z{ʆۣtvvqmP^"co^ XKl㞁-2')ꋀ #nb qȆwI 9 ׇPDm>.A#S]0 m|~[>斝 ) #aH V-dTFi'=L $D9S:'%HK5,- 5s]pw;_<͞jLU(dNP(ւPD`HM$).Q(42cΧTA!fԦ hn$ +/. nvQ$ZI@S]klKuLв/DiIDLM(a`ӀC8[=AxZ7$9C0^ EY0iw`lT8Ƀ$(zHQ8J#& BQT$uЎ@J=s-/kɳ/r1.-^0$vAp ōoqp=b= 4eIiSR05Na/寸[Cj7/םqL FDpVoJH1kQQ҃-Ͻ\âp3_3=a(ʒ5>NIZD))Of*dSy٪"P^'ʳ,íFa 3G}Pl@G\ p(*K8¢9HھHo.XxoHsi+qv9C|r#&k2T33d v/3I1F!F;?DGLwqT_}UIpȶYj yZk6ljW?x J#ǫN3$ NazixO|õ:\;f0A%f?by i~)omp):ٛ>WD.A4r&G[0vh<tw >X 2ycr7[\Assk,I]4/a QTULOkssI6 ̈́4D((lMr,_ymǧhsfcz+BD9X,!Px6`Ӡdk7Mw+!U`!I)x@+K/ 0,|h\A sOPJL1A/ǧ)B}t(zئąf:q]Jd@bTRflR.H^ӓD,%tEXtdate:create2012-09-26T20:26:34+01:00{%tEXtdate:modify2012-09-26T20:26:34+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lc.png0000664000175000017500000000712512625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[ly3ry)[bdɶ\EFp$F45u E6h>-E9(Ї(R[EK-ڵJ-%K%WE*dHJ.3.gfgfg)9@T`@Ι3;mr,7ߢ>7B$Tk 0h@箽ݟz;TP PJ)ocɢ&-{eןI "n R.-^x́*`v0y2;?ؖ{nl.֝δiԦz>l:'aRU1AۚJyM./.OOѡi 2َM}6DžUh/N1 OA~uTIG, ]"T*s1E/|qu7m-ͬ8Ukأ{s]=)tl3G)D)߄41kNH<,T` EE0L+#߻}/IBL9 `6sْJeL*YMi TiMp8fq-UkE"xXi̖,lkokϭq̸4ӶU0LP(=A{Cz6l)糘V#(01̔!ZYW|̂ (I|wpa{sYs8kJRh-._AպhmABޓ::RpwO)iccժ09M,0^a&CDNV>8T, ½hOE_E\ Oi-`d[_Q46uou- Γ7x  1==Rʧ}J)Ξ=FGI+G !;l.Q706׺`0e&Zm9 !"h}W=J/5vAw*S4"XTsjRF(> J)N8[cctcE-pL.h[<2rsxynnߚݱά= αb"⴮Nߧ) o>ͱc|gۚN5K/11qmpGk|J$ }g3% יeQ`<:̌ꂭM¿~UxwXD.\`xL`oIPۼyeIb5"!6"`lo)=fiI ɇ49~jΎ&eYY^fgl Nbʡu@ ]̀r/ൣGywPJմG'qs_5N͓')N6l^y`4&<2ζZD?kkkWBD@ [z{`YS"@fO;` (Ap4ﺦ1 |~ ^w~[?~o/:/0)<% 0<<̹- ;!9!45n`ª^c F^@p?--5B.#K8|0p\BظApAxw =Z͑_(e{m591n[ (/u5Doj𨠔`6\rmSr>1$t_É찑zMPW^ܹ ?V>W "dW~### '0&\؜Gm 5uj)G|^ȤQ: {J`qi{RvL%Li"( ꣴ1l)@WN};:kSv/x2ܲ|T0%i46- 'yfps\៟n$ dZx` i?Nh1×!%4ʅ yAh{.\AEaBHO'&'/ӗwUlQfLu4f|z6p9rdG>.? @x1C9rw!5L佬q^0ˏUHaQ<"k X:3_ hb"Ԣ8ʧx#YMzhaKg~8k'Nǒ\pm2, T74L5hq.ߣcm_,NTsMeKUIu8c{}aA0yEh{p_}Q ܙSܖZ@O ,CXiܑ3pnWɿeoї+;R^`UW,@Alm[hBY&P*!oyVJ"5/Xs i5wH͛q"UyۈBlV]+eP C E)[wXwD[$ӢfSٰbaM}l=&K+J9 9Jqlb` i^n#N#{V+K$zg}B3lC(4qEjmvU*U\}ڗp3mb[(D *\?Z_|g2!`t>UKԪf0e^M\JTVX\o| ~m$@ǚ{3o;ʶ~"׻UVi&W~DŽDm?(&k,"3|0 Dd<߯*=ؔ7R~fڪHe~xe:,%B>6 {6L3u{mZ7oSق!7 Y۶& OBj&s!G7P΍@mpf]xE|lXMλP3|t*22X]uA$ 5eq>B,Upu].(~|VFվjZ94 QQo"oY_RO%tEXtdate:create2012-09-26T20:26:15+01:00%tEXtdate:modify2012-09-26T20:26:15+01:00zIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/jo.png0000664000175000017500000000546612625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> tIDATxۏS;;`q dpL K^x %(.^6 Dmcv7 ;;'=S[}d3*Wt9;gѽMRqM35_ ZK^E p1!roi!BH7dǾmeUՎOU9Vg;$?($9 bİ^4KO/1FcUa(/ø""^{\x9jW$!LEE_dD_Y[6?ژ\lMh '" Q&!Ha-SeQٹՎ-V|$ɣNR 7 ؽEc") "Na7Ge2J!"bD ^]u.^W0g dzC"]g=PVƗ7.+:"vw;|l2"%,TsyXj]U@Wb} 1w*wb/*Ӿk `8\]}!KVmRov6 73gDTH,ȗ3u8|u~T] & {Uű޽p}pkI <<|9 A ~/h]L͂;ς~ط6lXSx}c*3Ƃ| +mͧ N5vp[ቛmM8/ %*Ij6 *=+0Uq 3 'ӮD+ Zq#䶪^fmjV0_qsަYTɟKp.sp.MIDK Li5A Sʞ)PT@,cCLD'N`zƷJ9(3#\9aT9XF+W.s: q /r1sE%i@]t7I@:jħ%U/[P1< :`[<3C7VݴB=u}³Q62L%:f4{, s =[j\@Z4*t_>@gzkC4Id o)ٙAck e4g,8yUZ% {3c0)W)H,vx2/-` d |' .>l BBBJNM=)Gق.DI!^FW7f+2&Rxm*f-@bU g: `%N_c[y WIDATx͏qgfȕvał %9oL$@r)/$- EB`G('AVʇ-ҦiEr3UП3۳%\|~^σXc+r>(CrsFдѦE{+I ̙3s_t:;"+D3H)y9wi !V\aktBGO< ֪oj*{i:t҅^{<< @_K/gkkk^XXXz97!IX}?r dg7B:_oAEL U%I}ꩧn<}W߾ȑNt@wyygũSxeeE;Fa7ړ4ʓ_8eu̒k4e̓VWOr,Oq/;(;{왕o-..Ǚ'HHc;!' ]M&2%X`ۄ́ff'z!zqΑe[__ /_G$3+++>933C%"R! ţIi赒10%DPڶ1oO}̈N sss'֞~JY tY(\ `;,NpNqp_>#"8vz!wg߮zC4GZ0 )Q}lF:ZU5j6x<*%H1j-W[ 잛"PG2l~l}߽%q}¥gT+_3T2Ou]kۤ:bqUuk PpB !_@PY3 P ~y2 87 h.)G>Fo8o)_g1\nHUJ}R^ pRLIS<}~%|31&|~߸22Ok9l)joC2& 7*\cU qL' sU6i6RI#1O6WoĊ@S6#w5O'x=K-",bDzҙ:W+ޫ_~z{[5Ҙf̶MFKZ_"430 snB9:^$Dc#y.FURW@ fF)"8&*nsָ=ݼYqUUI4ja/ ~5w#7GhrU I[-\93|Zx;;lZ'>KW7//ߟɥ9~8}4t]OP5T9Wȇ3O/_?k1%MS6l'0A H rώSRr!g[mEZ%T%F9<#kąwZO9Duy V1}+ʡz6Ko *vpڤ19Rnx!<" {K|P{ @e(C;eL}yW灨5Lpw}U5b6R)3A-ۢ͡kfUݿ'"lj"BLA%\f:յ%@ĩȲ,H?̣! x*YmPls(D>#R|8\,N54%˲VKc[[[i߷p(n774?^UD$fxRSRՑ^i0&B`825a7%D=w/^zgqq \aBJ hJ|pg{{,Cx8ɕ+W6Ο?kp"@#߉?Ësss\__L>'Hjza$> n޺Ix@!Bs{wpO fop8=󿹼+@&Mm#`x8c9b;7IDATxϯ%U?sfcyBdkp@$ @ X7B``Ay(  A1?L3f3wo]uXTuwU7D"fZsꮮs6<ݞnOM~@~6$P&@Ky{ Odz_̵,T&2ADDD񗻿س,]z?&M|5ؚn[9smUu7oWoPb6/_}{L}yo<2D$#RYـs]ʼn*gsɗXf HmQMUU ?\年@+k+u#qEUֲ*J#Odٟ|c>o`AF&R\lwo]d(#IEIz 6c*u)qke*A>ӼQyp,b_ٕ+U}su+M>TG3cH˒F@J@@$_1gĜ M Bp5@LG|Dc@T Y"0Q,Zj_?$iQ"eLhoU!Z%7jxi F%yXX[+9S$LGizwb03$b-$>x҂.$.ڸfB| @TޡgDpc"' ms!U=(6xW[6Qs䧭1 OZM(#">@kpij2 Y4_{jËtnhDqwi V05BPwmtEM=0'aY=k=F0 ZkUUH_:NIͭfnRrmMi!F` ޱMe3:ʀPٶ]aE מ`m5N|tx4M;nӊmL5v]N| ԭqnnvUnSL`-ʓu珝۔mxrK(^_9 4*a%x+UZɺ1*nI9$t^x"o 7M(PddQt2ni}Jvprn^{_`dE}Is)'NvïE/"> bS9DV]:0\=f«w7aExWdlI (56{uOlHgZ ?uܶ|U8 d 4 G5z/#E2eu+at t2N68Uqv C 6ʌC0kS[+{hͣqIUHmdzD#0bQve׻Zm&(kK&`wSf7 vEV RNfJ#v^*8D0 WnmhŸv#ogߛބuMQH-;/}pWL_,9 wS(Q+K(h nOcH2Sz]*NtX} *~H5E[H,?#(aPyef_M;U3h\~E;*MQ4C* Q:s^If`zEZab)w2Nni?kaԭ#r. .5胔Es| }d Ȳ|7Þ%hq*@~:mo}[6TIHwA ʇ¥_^rAɔz/mRq* =0+멓ue_\ҜU9Fϖ|.'=$^WB0#̱0V2y$W7Dajnz7jf߱C1Sc6Gaڪڝsnf/XJ 5p1 &/6膡x|/uٕ{}cQuPY׸CRUaCpa bTwoe, 2}e7G|cӄJi\fPt[9jJU*& z:P@RQ@2Т *PzϕLo٭]'Kf/,Q(\RJ֢eя;V+1JkRk\48>4TPs«p[ϕU ʝ),2}d7zR 7pe, ghQ(+k 5ht1ʅgXt\QX xׅ*KUrU+P@US޾㫥^:](gof>tpng 2WFrk<] YQ0Wњ+Uz]clJh_{ٿTUv-$K86~x;-ȎK[49x{%=ϔceq.UxiYrl-V.ܪq?W宠*[ prşNOߚMK8Y|-g F3,M` aӄ~{l4qX߀(,ϸe8ڝt}8rz^6^(9gY<g'D /0Y$6@|K>u(gSn~L =Y]`X~X01>p׃\C?W}uJC:ޜ /V_UCGhg8:AdD)%Č CYXW-i^$4/}TWm$, z1j`Qt`tAl)׃o%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cd.png0000664000175000017500000001043412625666430027121 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>ZIDATxypŝ?=eIec[ 9@8$@fʲ!r*[Y&!^ Up*v0!8:62/YlYut~f{6v^OϷ׿/ B">QTY ʌk8iR+rOB 9oi̺Ba(Α +*GR R#qB)cY#G H B&*uM$*.ƹeGul혉iJLC&ȋB6M9w &1RbEn7i_Z=t1"SH"B"PQj/PkbF!ʋǸ TUUJƢtԣ0™e7 >4P*I4R'Q fႚQ?2+/v껕cV&6+ʋED+.>m%EL3ZnXNk˼t(<] t"TwY/Xݿ߀)'0 3LGo?SƗO^=TW^ǫ/$`!RKψA'kH\RFDy#R%B38fF`GYEBD*A^pPU2y %%ڇ隣kz$DrRDWPle0A ,!rhା!BÁTHx{?>ɕL`@I-q/$2A#O,HrČx"I~.~qҚ"@I3( ɞ&vwO "ŤtR~Kt\CDV|%h\4bLbFz2?M;qMb6"j`B4xݯXθd.4ImJASs<߉4ŋ@ֹ}љp. *},hy5he77\@p M!`ow=SSs}4lS}[O0cHޚWؒ-&jM,yVWRuW_.Ķ4spR̝ M9 cQ]ҕ4b"3&lsP)dͦsi?hѨGAn:<ȷm8qJ+͵۝9IOpx3[0R_Vlt|(%.ׯ3{G4U&`@Flz[3[M8<4 'S;-* 9x2-6YdGJ2@CɎl, wJs{hf{(Tw3J>%$WS#LB~[It6 @)C[A'Q& M^1x%a .g9w-1u6?oE$*KYSlVO6KR li⻋~Gsv8ez.8k }䩟.eÎ{~sAXN=8^;y^~OFdAi~J"\`<,)=DJ(/጖uNX/+ol Vq⻹tS 3&1n>1c޾/LvThDPϊ[3a3w,ǮsCW|ؒp|̨kcy<\'XgQ ;4qþz0, #}gB"1dM !i?O76b[ oa?(3_MAA;;%K `x,߼}x&ɢP9oᬁt2ק6mm0< e^Սp}OȚ+9nE }%~/BDel)nrN@܍Zw~6#_-c Fyb9L؋C H82x)1}yё=|pEW=+VFp44cNf/Pڲv{\B2Up`lޗN?6z? @i&+, ! 0 9/N{Lw l8#rL'ы@ȏX;ʁ"R9|u/v8t&dž1'P8#5N#888 xH[Dp>r |hb_H_!mia&M}_K m*|?ң%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ng.png0000664000175000017500000000532512625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxK$G]3=YֳHndg>GH}|B 0ޅ=] |TwNJ,ʌUDddV%"H/ҋhC1ho@<5im~0P+[|s~_c""@D S;"{Mf7}ؙ-{ <.S_ⳋ~C|3#=fժyB~v%@_X ,^﨟;{բ"Y1|W+x ߭&S)Xr(s E]}o~/ #KAu-㽃]|>A&&dM [/޽{[l82q CA.Okm7;99˴wI=][x:Nv|z0{X-7*CJhAcHx0cݬr#(@V$u$cڧ*ō uKoyc֣`Cwg7u5/rDg­-QDkk`A+2tuc A j>a̸U.}_"ed~P2O $(z1Aʴ]9W,~84zHA^k+bT0{,5ҚT2C.p' p7:ѱ=v,˫1(RcAYP/ Sh*F IY*G$~afeJ&lVJ\X]e4Jnq 0ei@IKl,2 M 6EBH@W!X\Ҍd|uX tn+X2\4: xxM{s՞yҶLpbS-OFU bQe:&m_)k [`t/xyВY.F =FPDeR6ڗg;,z9ֆ1]dQ{LNPBG&?,o(ńTnM"w@d*&!fy*ڕkΥe|ui#ʠF4eJq_ ˜ mc`c}7X+YΔSfd,*#n( ^N9)l'FD.idBpZ'N+ DZ20 *eɩ)mJ#bS G0fQč:- Rͥ;I2`<(&}%ExǪc"+j1 a gD䯖4 @bB^yʗǫR.NbI}SQ/kT['8ЏPJ1kO^3 gFt`@}c0$*i\4AJI(^J|D &l#5[_I3 $4R6Ǎ Vzra!ci C? t@,S%0P\?f4!"lAƍbz΂Џ\8O`29q?PQ,4&TZOx nT nؗ\zk@X *]oUFjVL|!:#fǺ~Dc%ySvt˂jUs`69ڕFB{& hdf'NwͿ.U|I-]g@pttʶ] q}CwklE7@/w?m>5{{kьJ5N|!vMpޜ= m =/{O_1 S]f/[؟W^%sh[ón`9?9y|? b Bc?D[پ~?}rho!7|n]`fC Fu, ۝6.-gC6 Panfb9L׵0jYDN̖/}bn}.9+'< iS54E/5$L@%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ru.png0000664000175000017500000000423012625666430027156 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏ9ǿϮ5?w7fXNHi&Dʍ#' \G9"!E CBZ$&f1Ifʏ]eWuTĦg^T~USg]ڥ]5zEǮ"Q> 6^gДצL{h ۷^7!ŽCZkx:=}w>z)@J27n|͛?yocc` Ku7fR D?;<<;w~~/L b#\O~4iss^u_FiIL3˲ߺu<ױz9R#|*5)zɫ?GZ]BʁӘ!NVTS[rs4uӖ1;46/5b֥\*ݜV"aVA.r։C':KQlC3OHף2 f/Y묎,e kg&Y`a HHHX8HL{lz\Va `(0P c߱ 3A@%Pn_Dօ?W\ b` +| )m`mD ܼN^PǦ \Gsy1T (y q/Kz8O S~i:3Cr>rPn7XXk@cbڀkcYW0ulW_|l`xu99<) .<3h Ї8YVcSurںWfШ C0) 6hW/]euMG mM00Xq[Km tfR̔κiYLO -`|Bb7;_XGǾeh]K6[l/>!SCr>\m\WBcSE.)|gB<81rYkU km. R?d&ӧH,8)_)`?߹k=mUҦϤ|,N~(+ ,zA6ov&wU=<D56&@D/(c%xloDZg c <3^a >"˝V,)S9á2@EЪB/B,[AS} n*\$[|[UY=&ArU好  !w %tEXtdate:create2012-09-26T20:26:01+01:00K%tEXtdate:modify2012-09-26T20:26:01+01:00M+IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/iq.png0000664000175000017500000000654212625666430027151 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxy}?uΞF Fš[$ F Xl 8$eWAW\&ERǔ%68F؅Baa8tJZj%V9=33=jD쵾[]ӿwyp^u^+qNJ22t }Vrl'P?0sq(TG0`֗IBs ν%$e,ڞcعFkXR& tT*XQi1h 0np^/vΝVK{>mH[f2 Ǖ/A YLUBLRW' H)TnHŎjhO{S*Q)EƲHdL޷F01mۑ,<\n6mZ,h6M- 3\3nEa^C=PBzcxTe?umZϫN4F:D1 `rق>kb@FAHZO\`N5Δ )H@84n23r#Sݎji#'mi)8M\P1@-1O;%JDNAWk{9p4y8A eSp3NT`..@)mVqzqsuBWX {e pc<&ݤ uӳʕ׽_򹎵I΃m1Y)W+r%$K[R=e,(,mcR{ܱ[kacRNso4.$p啘MMB!z |m+w-~Z+󊧵FfL0GFo {VP.[F; li! :qLni@)4H@_r_m;]wVoYyVQV P賕ݴaw>VRkSy˶ޘK{=C}QM6$@i4g![pG{!NO7^%_M?@fmz h!%a"3eIsKH)TZΟ3rqFqC45:M6N,V64~k.ÃGHeҤ2), 2őSdlB`<7O7Q>0?~:zND=_~5+.G { ޵?0̐6B8E6i7phHMKc9=:P:Fkc _]yx.L7Vy4~3e i+.k+ԦVoxVSV=e黌$b 3Zߡ(\z5_:Bfma+[)c<ӱ=;W#@ A@:4LZ"`A,]ބl,"hY;e De{a!]=ϭۖz|# _KqcbYw=ԇ}\yz-dv0Fii+h"F 24 fk@NLW͂˹nvtLdˠt?ǽ+bHv<Ł.^hG{#{ԒY6C|'S 5_-?y (H%DgZk_6."bxAKQJ\?A?4ߦP-R=Ȓ9WW2ӚxH'gNbrSaKsx{H҉}}}jÆ oܸq0YY 3ZbH5B i0m#/-]! {|%mWNh*V] TN4Ys{i7xI,>:NQ̴7kJ*"HQN%梥~2]2l([Zj~o˫|Vn 5},bk2m:kiQ4ܘB6 0J6cȑ#466bo]7`TmSQK{Zk2 X!e}h)}:s.칏Ks?,Ys_e/,WUBgsiŅsޫŎ^j[^U9kmj]UEB*P+{`Uમ_UK{g n+|9Q{wˁt;;1jeVsk´2U4nHTjB !,^%8D?UQPG%R-U#!ĈbgiCna^œ g% H)k:ỷ%{pԁ@T/> $3 ue]cYVԖ4GMH&[ b8 JZ@ayaZdTJٶ},Hm8  Z@8 m۱X,4QZgAuvRN|Hʭ.WV.'tav;M"r׉d]D]"D?p"--bq?,IŇb5rB۵<7r>*?s(؈u%tEXtdate:create2012-09-26T20:26:39+01:00p%tEXtdate:modify2012-09-26T20:26:39+01:00k-bsIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gh.png0000664000175000017500000000645612625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> lIDATxY%G,wf2w̒ c$hB1 q#"""" q{DDD3&h8$L4fÙ=Ow>tus޹q twuw+J r^Ad0^iQrhK.= йne1kz""dqs^>6=R+Z9TA'骚+)?Ǐ%@!!t=;zW>y=ޞ^#,P҇Rr7浗oCrh l<ׇGLYV?>y?mU3fazhsUBAx1<:LV㏏9}6scUPxiīZwqPT,™e*2a__>Ɔb2ҾCum=?X-޹N)̳gY욺sH{5Rt _"7w]O'DnJ9eŔKDr=>%xu-@@%PZƈo~{GFXkn·Uvޝ| :V ˙i] /)Ht(-,X[e>], I6Z%IkEE4mW8=iXLaϲO,p+|+Kw\mY᝖%ΰI1"}h8Li.ȿVk%&Vgn0®vKB:NUy}#k1 f5?CЍ,XZY9$6q: )jgtwEU*t$)-X`10 ݺ}Ij)nj}"װxNj˩'܋Yb>uW\ ԇwN>yRXEbg8L됨K D*u 7Λ FvHY 7=i'4s_yYƓ%:kEƦJ7qZj\ ȌrYV QEX~M-\Z5y a˯ͷukm}*Jv%[=b/Zs,],LUQSߎ煲Rd[C Zg0`Ve^ `1T UMn9 T`+u0YMs^+-u]صmXg 3DWܫhS\S,/8'熄L3Dbz E)٣.^^h tM܀0ESMB[.4(QެQ<:w"\Lұ&jbE#5j=iM)5.*I4M. N ^^gI}oD8[^3lMR4Nh5yfpӱ}} Ϭ>҉ޢVE1=9*:NA֟! 0TՕz9wT#kߴϏtRQ:HV!?w%`p(ο`53\6hFVHi$rC U.([rO<;x1φ>d#w(cYyR$P|pFv]gdcݘ ڈɟkoYX_ԋ5! M$_AW¥Q;%tEXtdate:create2012-09-26T20:26:30+01:00%tEXtdate:modify2012-09-26T20:26:30+01:00'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bt.png0000664000175000017500000001341112625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>GIDATxi$y̺s/]KXI!PÔ²L:dGhrHl"̠dR2V`i IA. 7fw髺LY@!_DuWeUee>f~yT;c&~D׾L"qs}are4#!0>,ӿuhV1ئF M(֫º=o^ r9'4>kZ|2|eYH);"/cδWHvGg74NXv!&3T)V3]5OXz7x;=9{ظ2!>Z QșdS1nU:N )~ ٸWl%DRHd)rz I1fϓZli}!b)i(r>+>\|EyT$nO;Llm]T_t<VȪOm[66HeA8(,bmqy|rΣ=mF珈WtUkw-NJ!}Fah e2͈#ktqt#0@6yٍ>-F(Db=Zkʴ&`85vGHZeRӏ4?O\&QS*Cմ9H߃sJh$Zt_H`H0%Ri @ p%k fKp>n /X:Ə " pׯJOq S?T1 HJụԃY{ "E,3I ܧѫ]`O,0}Rr3[T pl?ΉJatb661 0QO} VZ"E.琞 !SxͻGT"a/>DD)~~>G*cٺH",ANJctɡ ,ZHm Qc`nq&S޳w&Q5ƋjFr4s/r9dY$F)UDum .;8ICR2f»*FwGi>9ά^Hꀓ28J=m젵:8osz/s3Ff,L5SSS|'#ڮKE!F1H: @?=ڍlzҒx!#2!0M|>@NuAiS*VZ.3WFƻg0a"kݧClkr۳snѣ0a1Bj V QPV& CٌoSȧcuu$I@YYO?,\G(|80}wFFxa=o[2Bl*LQ*DOS/SaAR`c_b_@JŤKO!JruLC8jѮX90U !pRTX,\:G+g#Ӻ/Gr[K+p|le7*((]pYXVrS`Nn1~)$K#cM梁:JzN6nc& c|aQ7؎CLVrZ[i>Hyf~b3;v=cҰh`>wB)RWZIgۓ Oef>ZsIaPYkmއ, :}vM:2MTkEɡ94Je v.auve/33sD:?EoY:CJaCS?`]BNFxaH#B`^d;L5\d[ H [nr,--&}2Ps_4T]|9*/?Iv y[X\:Ea4x [2p=b?.%GXԇJA)b}YVR <},ˢ\..i4 lvxF ckWm{ÉX`4a9%בXX'mR`oPh^7=Kx7Gf`bn-Vm 5{joWꄈzsC7C\ z{&kױW]^tq>J)* A IJ,lɜ& `P6_v# mJS/?@amLk<-È]ǰ,V )%QuBJ_g&{%Z]I[#~]|(XYYAJI''K6795E Qp@d0,{9@09S I0$L^tڤH/7C:UqʆClfI9kw{ \bSBO`뀁j]'XDI̸M*n/xmNdiu$,mtdyiX ɶ%z=B\7y|IwDJ#2:.̝m%Ig@v굢ތY+G/|s'L)oh}cc=f l QhQK&PeuM2cȓitg{`X)2R Jk<%y'8קX3E[;,~o;2wi썏 !2(QacEXʌb9M+w| aaA:Sbe_G|:ߥ IHޥ;OQ.{/8],bk'n%;x:#b|?2վܗـ:2HCiMwuD}bZg1yfcy!GBbOgaZ/N\@ y8g^yQjG>b#u3fma6:MLџQ C$AkD߼ıF+KUتM$8뺤i4M,Euu)6J!l~,ak1\YMZ>]IY~(V 1w'/ay マL@u'5~ Tjl1Zq+o2B #G 9V;AiHTӹ4}Dr'[dN^Ӂ@,~5(*aG?p0h-/|ȔH~Ŀ$/BA5~À1ps HU$80?#4,gTHH4$Y2צs3E4*{1g%LJ0ck&XuӃxbCXnOS^5q/J#jLhA_%=zJ*F5+g0 `|ڶ]ސRvHj l f3'18 !8'>&` :~RVqX yIDATx[KGNu̽v8 I,$BY ,!e D,@7-`BB!""!!6M~z꙱%tvOM:_}ԩS<.oGtg:ъuj>ŁEY@K@ @v.mOKjk jF""??٣`&b]0}ٗxm::_OJ) `.{d&\W^|{K0K)ƹ;x@0 HNfK ekѵn{wh_ҭ7~G db@`J.& NQ`p:', (QJ1h/\ 59wX3!g)HmuZ3^_=w޷1fW_od6:4HP hRQ ¢Uc{ LĘ)NǍJlap=֙:`Qz jE'^.]M?3[4~U$U5,e% K +, SxJD 3h:̛6b)uYu!,bz܁jPU?EWi͸]6w42VOY%E:OءК랑!T^ FY,{nH)0p S;^[|N443"rb>c $) D:;a <ۉwlb=NxsKxg?8z`ģ#z&)FM2[Y6;_]Yx"x1Gjq#fT5A|.L|DT\@&`H F\8Ypᒔyp*z"}0zזNJ1 ֭fh[$hnd8nv׬۸@l8'?T/j }Dcdmqxzz&p|ٲv$O{ƤA95${2Q^.JZwt#&XLd b ɁTz9iVEdY\  EMnZ Qo%A@Td Xy(-*gf`stsʄ0?۩{"_Č"`N b05// S#ڑiqL\N&t}\ &cL^YH[KEq;LJg+`RWNX345& WnΦꊔ@OCᅽ#Ln=0vfK|p9& A-# i,WY5z4''!o~:}[xs; gV2bTz;ϾLrS̰aM&g4͏4õ77HR@E@&{:E @MlF۬zy):cf(hQ+Zi(*[hiS$ rxDI1y5 ޳Y=|T@vPTG4{2OSޘ9the; v%`ŽQD JE_8GdRbDIÊ P#>y#1``!4?iEu9OY:#z>ԾLw2ᇜ"x@EZ١"T*h%UI> ]JKc!$ue>2u=sxw3 2t| ;䛳;k`Z:itsXωZ0L@LBX'ufwBq2qn`3enednpxE@3ww_Džz@i,~,8 E,|QXq1dBfu9 i x!ݱBnH]h&t=a"15k#+")@t k[4ԕæF8f 3{&|W>4|3&LGGFNY=c fX^pufpOBoslWi}ۇH^.rai"nw ~=9f-)rH1{d$mt$2[̾`rq,&^Q"h"8#%vEd!3xD@fMY :y9R1,"l@k`Ğ@m -F;2뢙j0=CC\ -2뛃p襼cm7e/ճQ̊cbX +A2>2%t,&3EFmf-=+i0&zc@R8 cY#NA+խ_0xR1R %虘Ne(-I,@vM90 ˆx6f HíS1r~Y-Y CGݖۡ ;r$ :[ϊ8POu  79@z`za[mZ*2+aM@"Tq $^葃_,vmy0FѽIe&<vAv5 h;4M6] AZӛΞ<(fM1tGJɅYA tߣiˉy#W*hZ6tx 3Og[F߶$M'aS\'ikq?dC @G_}Y @ɄiP i"yjMO8ǨN:(p>g*̺_,hfBwt+lEq>s*}Bb͕^'ίX a Pr%WUW vuڂ X5Eգ ^ݜn l[v .l =ܱ`ˆ.0 uXPȼ*bp0DJ@׽ `3&nҢa|] eA@8$ߧ8+ŧ\ *? Q)O%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mo.png0000664000175000017500000000750512625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{\Yܹ3;;뵓#4PZ7I(( "^"!! P@BjUj U i61N;wwvgvq}͌5g={s~ ʵr\+Bu"TR#~S1~.4c3Be M{WeU;RPJ) (2{})):HXM b -wV_7 T@N[5u_ wF?\mTnE+Gm`BT*BDtbm ;&_7K^)DlG7 w%7k/y_|;C))53ybenNV](BI .G꠭/˖óLp\#o9ZkoO/7 `}9Mۨ`kYCid)e`+Orlԕpz}Go^N)ur٪]x~?PT2*:+6Y^يZ]Yg8`@ch*4 &W֡,ΩI&I,=F%]E:վiar2(\䤳w0I7X?1!+q(o6[;ݱe6)61[ߑLE0"49~/edBu)EQ1 K%r!L ުpO+ownb6UDR)w:I?M-|&\/g)Xe>PF!;XnTW2)!xy=qڠFeߥoz/Ѫ#*ovZ$A͈2QIW,08|_7 '’@ӝfw}ou0g~m V8a{ōL8R+ &)\_g6rC}$u%)c4'^|]eb02'~p9\:˧_qNgG\9]:Ul83w0SƩAi\"}3`shVkw.y㭣v&+f]n D h7~x<2x Sܿ+uNu9~]ld=?,/k$2L8HB\8jHwF#FF@[eg'NCF g sN˻wA)HA81F|f,3H)mV~=˩IvOaҝ. xcks᜷HTn[lm+鎕Hы!r `uCgf{+mLT1tmz=O{FXpFKC+i(r,b'qw$\%utWF*nI|sdTT@3@yp~ΞsiCORXyhEcf(u!`$;I^< -#`x2>#uEx)[Ӝ5uJM=͍¡51t>?zQԘaG<5zƤs0g@if%BĴ #ڦˣ}I@y *=Dem#IfReުHbItK%"\Cue7[^ۀ OnՆacuZZJ]fD%>0/V->ul6oH̊jg9tן&u_QUAQ{IfYT_gTZZ1gpX~x?#5,W;κxMt)x0CP8M+[(j$x@}ߟ· {6"P!Άm@:WhCHk,aE@uWBU ^wJl Vv!7^"~Gl% 0/aȀZe\q&|'+vj_T"+O|dj-R}\?hh-`%tEXtdate:create2012-09-26T20:26:38+01:00{%tEXtdate:modify2012-09-26T20:26:38+01:00ZiIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cg.png0000664000175000017500000000553312625666430027130 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxK%E_w{}p2k#!00Ayąј`x?DqiB$ v&`#* #;{=>էNUW3Cԝ3}Nׯ_}_UWåt)]JoŐO!/'xyΏMū U)Ap[[~lznS` cv]U{ikv;Λo>_o#`ktmQ|+u +ՠ,X?o?5RƆS _[VncwyG?|?a Yϖw5}E9|0EQ`Lpdm*@(/- _? >mS[4cZF[ot^t.<ԷX}ZȗHub4bX'`OE6[d즸˄CIU׆h|J寞)r,rTa5dȰg Lko Fb_m_P<@+'V^Ɋ ao?cJ%3`QP4eaPS&,* 3o^f_up< /N;E xh~xΖT4i~P܂COy18AF۪<;PWաe#1i)ы&\kTnXQN^.3H p!((<<\k.gQkO+Ay +(/1o1xo= &6f>xΙDe*/l4|*Okxԡ͵::h~L)mAtlK5$1=h>0Ѩ15jj: 6ni `j\B<CQ}S7 .ۂ)7UMLYvR:77̋2dl{w -wM8xf 2SJM4530v<Yz5~U' 0cPHrV!L .^_MpF^1gM(0ɀTGp]ͪXx9($,/6B}LEF!4o?.'֔6#ePqF bf٨U'L. ^}`nџLP*ܿGʮ w-6%ef;8lLzF*LR\`3OW^ `N![sr˚M_y10}WD?*<oV!RpFड़GIG@:xw+l]`ЌL0Vu)GMa:v=(ʁ 9,C(Pn[mrG}@}x 8$zHΪuN$m ^\Q .},izs 1 [TYa )6R#'uz}@j)YËy[P}a"OW)("ܾbYSm4lu=`2=" N>%pۺئ}f\#).e~̚0  8R(t(Iί;*rq8b 7G./ya).y>Ye #0ܳ BQV 3JTl|/VyP*Ubp$68 v9[? ;]~kY܃(K{qSq!g(/Ⱦf1S 0R%1oqs|v,i`F9xbWIu )|xGkA8[8FoAxGadYf.S?ΞO]<SA? "a 1^_lo_gVW=OxXuB.`Dx*?x^/Qz02{86ҧ+6˒Ef|o`oj?L~f,;6xpxeׇXK5}[|QKv W;GeC +35";]D5fI{Ѽ#bG.l2ؾn mS!a+_ܻb\qvD$kJ=ٺ>_SO>W%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gl.png0000664000175000017500000000525012625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxMlEqLxJYHHpasFHQp@ 8BN A@@$+? I$ΏǞ=t3cIWu{U ܒ[H.1$ @Fd Bӑ:'M! 7nX|nݺ?r!D&Dc MiNK>Z?Ν;{ / bV@~˖-غuKk׮}bʕwuuI) ֚j[ok޽#@-YN#5knۼy}}}/J\.Gli]׵mO>Rj"׵y捥RobժUtwwcMJ)+׭[?I4Rt_X\](Xr 4;1XE.P(bŊ;֮]8nr@Q)cY DJeYض-֫so^)zrvDT ԁHr- Zk+Zg$`a+4tL:wiŋQ"[R3O*9;0={pPgΠ'=۶W~6Q|Qd욊pɢ'&識kjhAXH B AZ\9x`RܴE>8{[.:%h5~`)GkY%JڱcΗ_sPBCmYa{K*Q: dWqO.gv$B1 BmxԼc˗߾کSׅ"BB %΁L&qf&ZS~mÇI!Z}| 3w/:v& - wOMyZKSP޹ 兇1LzdﳒBU1x ߥ=yʞ= YW&4V. 4@jkE(!&cP9^`| x7'ɓg.vdǎaY#CQk3Z"u&A Z MGѺcf&R_wuqJmK<1`T H|GC;>~o:p)wwjt=0 1i+qx1}>*OFϤi9ץ2=s V}(HIMJBB`z5w~1 '!ٽk=YSNm 5Қ1qJ<7HƼLM'&f]T*ɿY4D3}sEx mI7-`1ץvug`cʣ5FdH%ؚ%ҋzztTCC~(,\fS|MꊔN`嫯+ u^Zle^?'2~1y6 [yEXp4d72NHQvEh#qm!za5S/2m?' (H]HpIk]߾xA.]bq]mӚJkOdk[~Npڱ^»t#A><3~:UjL ik&MS\ٺʞ=Zm^tٲƜv:/jN B/Xm9\})=g!CȞf?v !#+.4mdtж$grQ !xV(h {>#aG!܃EXuѣ!GR=|It?"Mw=8Cuޘ}X)1C.Dw7X_P 3==8)lji@n]IX#no[Z 8jllD+u4}eykN8/fC؋Cdh|\e5{+-wmw_M|5 YvfvJ_J}0|?*ЯH:Ƙ1"4F߰nxHwZ_RSAq0!Z'l? e1W߉-v/ lWR$5o;&۾x"t)7gj l8LG3ֺ֟ )mJL,11۞>aGg7LL6y %m˺Xtbߤb^_*<ϻ  螅;r;Cn1p F+ M6sx5Ove$!P LC Bl&ĆvP4s)Lf'gB0Wlz_(aF'#3V`*&,>ck%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mr.png0000664000175000017500000000743112625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>WIDATxIyU6[B5̈"˒ +Ab,C@!A $9%A|Knq#pH-٦IC!gz_nR fũzUU}۫WpmwtdԐ} V἟f>Z[n6|ǫ3kJ)P(R"uGBuo D"Mw}v?/smĢ;Q T~aqať?6곕jEk" TJTu9刔;Lyljg΍f[^z݂jNT35'JBВ%,Dp~ZX2zXlHm󜝝cݹO/?" cfdeQQձRR,d9\IX@qxe~VT*5\++^~S.NVgFWptCz*mFshhj1C ͘YxÑg-f9AT8sm> 4coIaϕUk%J*TȦP%(Et|p$|hZiD:O(PFߪR%LͼI***D|šK"Uzep*\B,Eds<'=4Zj3Zy c0?R+d-<׾iJׇW#Med>f_/ *'P,pa @awK&C x#m\sʮ1ӃOе`KFx9\nsӐL_.I$=Q^^dC=ڽIvB4Uyݼv#KzI j6-h/nA wkJ*bHbUIU߻=LR!XcG.VQ$/KҒ`Z#^lE0I|:pz{2Q`$1yQZ9s2@A?ZG].g~<}򫒕rڎT X>iyTX 7( 3](E xU|Q1{X^- &ţS-wqTz^<_}k2f(ҧVjvzUj:P?[{@s 'Ƭ ^|""*:,xmAbxOl""~mRs|^#Byy2T FX*228>Žr]4Λk 8r$>bfKZ<3ɑZNAQ(|v/]J#^}Y sEq;4gw&y ^=qӭ&che ë8RfSߜ;׏ >:;3D"53_ys~w=I_XXҘ&l[)Cxhjڃ;~~`5XADjr}?I=zv'vxgTd_8 xQ8,B8ݚbۯ.WP*sa"<+y7s78/ 6j3&+oxe AX t p.xPnɰ&h]4_:—pŏ7@Y`zf r /Fk؋ކ/( @b!k&!^=qN _=tY) #0jV*[o]?Q1xCgG>dI6>&w>_~3|dkC?bY~9Θ`y,#SS'JC"R 4sz yj䝍9zF()^GԜoՓy տ*,;8Jʳ:ACt x<0J#H Ὥ&|q=t.y&wy}y3&28TG&*ONou\D'wcPqqlNG(9Pl" 013"2 <2/s-bH8F8z}yigZM٘4 |τWawۼ0 K²/,cDJߚI6p<8phE FD{qaf]݊ؒj2z<:AwhyUv  O1UQs;OϰP»7>L碕 CRa4)%MPpio<3&>37r@ZOTyǹ_G+Ee@J߾4"W=|+W$MΕ|! Y!x,ވSѼ̟anLT0R\9E@43:% H2Lk ښ+WXhf@gݫ (޽Or?!;(le颢r-ڙ|}G+|n*m\5i;ݝ;W;pbJ("t͔t~P,? +`eo!޸3[<7CS;>hhv}v3&nv^8<}TYFJ+5A<H0S) De.;kk0LU=&]paǫUB+A%đ+_.ە=2!? S0 Z I9}=Ag.*IY"/}5*)Z|$/ Imw9u$HŕJɆ5T̾Au)fV#74c xEs3to^Y AAx4D"A!R٨hT ױ+p; `ɚn3@ q5Z@t:;hh2fy;v˳ML/ 2qE VVoiTQ *5Y&:ÔǞu`:vջ?H˪RK}igeuӬ*SuPJ[RVO^"}HkoFXxA98sD{~Z@ ׀&0,:IDATxY$Yߩ}.sx<2N/ĉHX"#"<"@J (CNxlj=cO2VWx77T]UT:rs Lo7ӛoгo{yHƔ `yfD33Gwf 37q ""HAB|U?U5?DxƿW{~M-1]5uV:>k\o|f 17=Ɲ;Q\cA$ Zd^TX+*ٷBGҭIvRyf^0?Q$P#""z ŀEvXk=;VoVk_:1fghWrig?ww SRV,A`I Ј17U\,.2Y$Y=>=HlI(tϴ>4u-2lRāNyIn8Ǐu@!TX(N.^2\[.UgrKJYxsh+x4XAh:\Z#0nA5{|fK\=kKu8rLag~a=hel^4K<asRy3DR/iU 긼=L)eFnE{gLi(= _6UUP)NqDVV]/a$KvJto=4`ZK(1L Z\jrƁj?l*0V_!3[[/͐s70&&e}#U72]syTn([E{|ݘef*njP\(C:=@:B0U:Xphf+.kDz. ;s:iJ{KRU9"*kcN"vXBk,nbaR26P! ǡx(#a`eiz?oôaB[W9FsX~\4cr#l5$v&*HE|UwjwKzd>c^",' PJMN51IV) >|bɭt)CʍOtN)Be\ ׹ɥ[&  f`I0y#{ ֯oH:4˅Mn&\7H1xšZ %QUs#8F(@A ~73oOsCFV/n=.qg'S*WZ(%JSJ)ҸBơ/ MmfpCl֞/ HI5$)QV'Mp[p)t{CJ-9Vנk_`|gA ~ǻUl"wwW\aصIA&&^p8N L][:L]Ȧ XOl[ .aƁU ip?FpAA})ןF0NuZF<4ӼO8PְV>_s|p _>q Mtϣ[īsڗvR LZ #DWy^pC,h91[G+@Y¯>ǽժP*)/+]'}D]$h\b [<`j}rSLt]k\-g>mir_G?IUdsHmSYg^% j;GBڢTX B~`Pj_W6fЙe|ɂWR*b<]`~"5f19q:;|_1 g #v1$b;l%uc$yrBOa 9jIj,SuR;3~S.amH,-RMأ G8zS\8Wu=AՀ&CG%3"fVY ,MlqwN^o{ QT.& @*cXk~M]1'Y9॰VL!O |<`d"2@AvMc)ewhHe6.W)gLs'0#̭S. nJN豾|"BCJgEL YTۮNkۋFXarw=3Sѡm/S %[]>3"w[i'J)4(No1ppZ&ҝx)!_B2#Q+n=-8G3cނ #sdX"#'E1Z1J&8yhF$ ~s@p3߼~m4)NP>ALo#j\}ax-O 2He% Jnh.r\W9u57 o3ɠQ(Y` &u[YłpAc:8pXUT Ccgc>7!3l=KiPueMe" !UCE+uҀQZ ڒ1ܰC-Fb E(*FdFZE=OZ0:l)IqKIBGT }CЎ:[J%ٷaGv(mɷN*zlG x?7H.I5luyq: #v[rx *mPGUQK;A#fv;0iЙܭIDATxypי=с,HH eWH&rI S&U[|TfxJ({$$1JdH3~oSR"WYU=~w6|.3zAPR#LBbC0ahKUs&MQQ+QLAQPŌn'L wᅢY(Wrg?rGHUZʘBzॗѣ-i7O2uDJ~4;'?1 F*4\;4ҥur'rUUU%0eew0}tU%J׋RUTĸ{!C7;2: -yNBa _m.-?_8g{(/*.*Q@dmZhuAqhJKKM0!8))%n(x\j<VBH D TUh.X0់J}G@O>ҥ[~uΜrv!Ṁ\.S0SKgֺ.uPԬ,Ac&li{`lla\2`&`a&8ۭQ1s5VP9pe׾>?;#L9ѣ'xn>_!V̢Ew[ǩ?ıc1&dpf{ "ȁmPJ0ӴaH u47s>6(9@ǎ\xez-.82, (MMP!$EEybb 8v ]7p8h! =嶦Yڦa(Bɮ }[E"b3 YS;EN&Mg{~P )lohJU ܪhZ|>_WWCع@: G/ 5L'qp}hjbSI۴s{Rvn"&.׍_׮B?L6H|yJCϗGMME6H˥$ȊEH)1 ǣQQ1U_`{mo؀!NNw*.E /"s (.FuQ4pk+۷ԤR]=Et͛r%K#͊)Nh35b<5̛?g-6?|#krߪU,[F֘1Bou ÔMN\."ttmߎk_[]ʕ3Yp MMx啣QCM4EyhihHL,9Ӧ᫩1Zi8sz5@$xp>Moc#wFA"ݯFWme9 '{)l9"PÃa}ъYYS'DDK8 \i)X4 )64UU|֮O pǎ9}z 5jLD%wƌbjpj7\w:;g{2%6y,[((()vɫ"jȅX!'{8կҨ@Ux0==ݴzwM'-@j 8I 1 # =Kt!rsdeaO-(F5#-d@ Oh^0Ӥe^g[P$Bo @ϟ>ަ&.݋i*Yfϊ8$N8'Ԛ.a) zG=rb! 9x!)+B  TU QhBFg/J M.I@1c桤gWZ"T+L ;|5ju#2xW%>KB44Z`bZMO&jbb9c3j`\" YXBtYqe HjG"tfC9fNYI텠f,Y&d~DK0cN#isn8`)ߙ~LHzIg0X\_QAB&*7)0Q2^q?饅'1-@i7JX$kj1)]JƙHRzq4ct̾Nn?D'OYWGߞ=p8jR)X5Izanl@O0$$;)zqn?'K9z!91|~f̸kl=!޶p{{̗Y`bF?893fpbc֬%R BrV_|?Ltmb6;d;={bj@ H䣏7gb ʼɅjJz ou5][Y_SfE&7gEk͚9̝xg ywC((ȱ$cmpѵu+a 4 S,ddϘ>6ƆC47_#H! Bq Brx;?Yf+W9_]͛鬫c>) CHhr[\}H)qSt)%z3iiM4482Yi$0UOK -[OSQAK[ ?ņ lm B1v-~rT>go SrJ~GU_OƍҒvQZ]o-[uv2 s˂>izJηlpVFM5EDR s"@;' !u"#J8Mgn{< ^![n;JkصkxC0pbJ\kkc㿽ECiR$Tb0t*a`Hin>Ncai-> ĉ6}U֯?`zkR9A$FQpSu(,$"nyq+xVͩQsI̴i©~BPU"k91D @8lA4eL?6L|x(4<'{斁!˦xy[`vQB!--H$#@=~ _BK%tEXtdate:create2012-09-26T20:26:23+01:00r%tEXtdate:modify2012-09-26T20:26:23+01:00=IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mf.png0000664000175000017500000001015412625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyy_ϱ3{kXi%d IC9R&!q**");PPvىcǹbv#@RpF`t@+:=g/=ZR«ڷ~o}{>)O'oQӵgC\Si3fqRzb{(( Ѐye55k;|g)PZ+PPJ)P(My$6RKuwL^.x "w@>3 8ax&馛z~_zwcӗbV_76GI[K[b">J %_ʵ뒳-wy.;tno,o㙧FOM+GLvttmhhhK&qJ׬R|͏JgRFn=7Q>R>"Rr"% RޣV=1&0 EƢ6Rߢꕒ'|cc(6or2H)[xWIBQJF&zE `08>i2#(ӥ%:? |5}kIb1%{(5aB'O$CB}.va$ EWXbCyFiaiz{e!b6p`?;x\kx3V-/᱖`!٥(3Ύ=I*kwropܫH{+nU6\^օ^{ ƙlLS}+\2ֹ#T\l*)&ޥ(Sƻ\]U(M_\+G/%7"&:ti3$Nљpp/I46Fwԭw*jq'v5/Phunȵn5O¬~{LC.Gwb%93Úͺ>5SQ;IѭL XM^ŕ|̣),/8){`a@04`Y"7'?NOo/e㨆4XV5E\6dyYYTږ}~֞Ui .iϸDpq̢ƶGADJ|c-8 )ff`hI$,Y{zTvS$2r !1D6ҿw:%lzV[ b:$$l/Tn,3nw21?J)D,%Ls^{srrlƞ"dd2%۵aeߏ|U`+Euu]aA6C]ZYSѻT^[D\ĵq\{;SV)bOYw $\ytwws;aX9x8㫮)^=jh4a(&EG-” K-O(Օ@T.0XPIH&{P| iLSkL&ZkLE&i8}T9mI'?~Xg&j=q2[ª?Cr#']ĢnN]ؒ.iXrƫ~ zgVϘx . Qmυ%UV(RUX yL (hI*KzFR[tǠ/^bZ>_.XsRF㛘ȾIW*fa\C)\o5>{ޮ ZVZ "߆:84%fbm &yng6@_bsKv3cHGLEdQ6$/zDOe2<^Wo4 dƾ|]`!e܃ ҙ\;ƅV4`soqOqt5tlv9?S~pEl*/Oyr6ӹYNAcj҉K[{јƤ{VuYS__9"~KWn{x'gO0)a+)jOUsur7=@p "Dh*Ryy<Ow,Z+3+^g.A4ka:g ߿gwHY[L5+ '?D@#@q, ˲(Zl}m@6޵]70JC770֌z (O" 9,{d~zzZ٬b&c+n~WV/Kjn|XȫJܜu%a߾}[ZZZ uM߷VSWu^B5#5b4 N^"v,ps(':yB"9ޱcGT =sߖ8Qyn <߰'a@ȊH'x*%sɳ Fecг G4Ӧst{vr@a.uh^Dc7*e$uUݜ?:35tq#QTd 1 ;G;Єk b):EHp*›dž$@(Xjϝ΢Rf Ɓ"  5KRc|Iq)uh9*[W,IQj}RBsDNGfCE=[D|أF.%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mx.png0000664000175000017500000001032012625666430027151 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx}}?^go;&6QFiR!P R%UUUZ)j)mHC#9(45U8~8~9sllߝomf_ݙݻgf}3q8M2]QH2Ts 0|y 9yд/O@P R=-ukH+I((SWRn^(ZE͝<~ZBT<  RψH0_Dͷ #wAw^,糀QcUŀDU 潉d=+0\P.<X]D뮣=Z1aᔎ=Hc֚ebqdq,̋}PpA*Hx ٜR---q DQ>hO m,\tLȴ3~Ayj k-Lf2 \OoWg[ ϪS_CrU&.TT%r)d`Tޤ&/HR SS R[XRRH,FQ)M[Dr5IIk1UŢXAU3Q4@ WRH6XtFC)JlYnj~FD=@J[ ÉJPa)竾shtR " ^FEΕ"](P2l+0@'&DKT+ */k #/o ceAhp DbfE?}V|?0 ̭*ǕrV|*Op&4a,ꨊOɟ_G73E]zg͕ 0TSҹ>usxgǨ~Qd~> n g\MY\ JݸYSlB"VfQ9mۜ?}Gc1ڗk*RuP ADU^05Y&ƹt$_##QH \{\zK*L:v~eP5ކ6 ܶ?Ng?d]Kx?ciH!لj4|o:5`W` ԅ7ˇ1+y ʻi|zǞ38n. '˅o2I] f@P$b&Z {CU7R^f6@VARy^8\)-dFOoe7JQVbP.G n8u87\9W~oE0kRD1%pHP$DӜn]`ƭ컘Br :WcF`i]j1?vƦfĚ  t7eeϝgzzWbmnplAka5"`*hma2Wk/15)z5 w}n2, ,܈E+|џ=ˬw(H暳a"e* er!wݖ <V R y), j$„-ވc"HD5]7|.t, WhuzViEc bcXy`7Rc-8'a 6K<9'WRD!FŌM- _g-M"fq0 SVT e8GړP^j9*W(eЧ!khLl2 gc9}eVva>Ň{~ tc9ɂTa?6hF5ifdzW8|uTEF~(*pTB,,&TF9 , 5xt^kǞaa laN`6&E~_?3_u鵘M4$;zoYk8 l)Y\ 31ұBr&W^jfAfEٵMe:[nc:FFGO3ve)O#h0vv}7dADS\3¶QFTl&j6if@ *JԢKޱvatdLxaRsL.·Wsݫоl7ߎj\@n<ݼĂVwm⽢S;eY3D4f ьcW6 یN?vFn BT4LqpܼfV{39|ӝEx)GӐ><5,~ [Xt/ qpoal8et00TQa, A(0 rBWD@gj @8s %RD\34׀C&$#o 兣ϱ{-l;F,L^+f0?ɱ}\&q0({euEۅ`I~:V7N>WkVQ[Ɣہ0ѽq*pJ)-n>}!hXNn\~T>!'h~+Y=L's_w iOTXvzi(XTt+Eؐ5_d0ua[?N "V:ASb$z7sriOMB ~sU[0usQc GtaDi!6/^Kn9iIsͬ7䎩1|2I&xk"ώ57~yy|kϷ1m;"bDa" Pu:&p\yᲉhښ;yy̠9*-m˶5lOlӅXMm3LJ3HP R=?+? !dRu޷GXw)I kd 9[|eX:R:C=fuVlcNq"S ѧHD|ݔnj;3_rQL3:~ÚY͝Y޶`u*{/ * A4pAJCXue㨔J~vnZqbxm3ʎ>iXfDcz[)1)H&CB3O2<صL0 R )nݼ ][&H00 V69A"TBgSw*EEKUXc[_*/[ah@^}< ceg.hKU͍ԫi^#Z\2 $OmːJcV,sI+r߷u->э/-vV (JJvʅ2O8zJ5("(-<`SQK<\C-eؖz՛)?m"|Xh/o ؎b] \]J|}OmE (X"q|qM~s8WX2e+F(LXL/^` 4||"? ,l3pq4䕒RZG~9J-4ܝD,[LMM3iLΫjM $O= / (5~V|lG),khTbQcwJ(bΠ}Ɍ86Ƹe1!(Ð#xߙBa*Z@ND2gt$uN:&,;K Z R kr3EX3*RbS؛}dpB*6V`p6,KLӼX:#H`\1 $bqn_B܈kȞ>ORK}n(m{8\28_P83s)bj .0 \]xlEq 5AvP ǐ `tfEU#لX#f`ZfvW-%[JļbUy)! Ur?kpM--2%tEXtdate:create2012-09-26T20:26:33+01:00%tEXtdate:modify2012-09-26T20:26:33+01:00]==IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/am.png0000664000175000017500000000452512625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏG?g119!Eq '. č#' \#H)!!$$~qB6!qL xzvUSUݽɋT35W?z3{f!g)3~4[:e,  Wl|~& EmiR_ZX=jm{x(nO }}g"@ ̿F~1}j>S8K>}ws˦ԋ$UZqk,,A C`ȅID}ikKΝ?l6c 1u^vv!u&n-,K>.fțyLVѷu#I^$꾈OSt\ Șá(]MhD $ISV"k_K_,)n_yy>-|$Y.18׍AzHs^$Z`&¶"_y E3^;KDLkP!|٭6,ױlJ=_@Qy1pStqP} (#B@2wMWe]4 Vɦu ڢr}I7Z||Mx pc%:2uINܗm(>)=_='o NU ڌb@ʏu,$d2hN:R&L*%Òaɱj Y #GB :cI#{ Y29n~JeC-> {Bbc"/T@ Ҋ^<@W&ϔҋ}'3t}\Ft#=u9#?cQ8nql]zSBnϷ/v4M8j;P!%fj6Hh;QS鍛Xf4a<&CF|AUրbAIy@逳]?c5*TO)o>Խ{̑ڳ¯j5)8s]sh ؂jQ]b!Ox1_se?N,'偨wY? )[iB3We)ӷF=]<.V bJ7,qtZ>; j*j A**wZt*e*6ʹѪ"iy~pSi7+}ɏXH⹃+RNNPtaXeȔzQ89NzW[WH: }-3TU_!q} [DƢXo "5pB[Д T/]KwX$|fb"ú%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gq.png0000664000175000017500000000655412625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[Yjծ$V[-1*$P$) )$K  1DCQR*eSVRlM{^efan=sf9ZŎPΨKtKҽt/Mr IG)$C `<3}?)f<!̑Clk""jRGkiu%/(G5&jo+o] .o/m 2e58Osz_fku1ƘX .zNz5Z_gr K>^yJ*vvz+5o=|b97&ϼOYkg   &fD{$ntAMI1>ymoOa:ӿWglqeAky5sgiiX}:_F$D0)jM2])4X\]Kq#k㕧%lBcF'>#O:}mῂ(1*FsDXu,9%H!9A&䌯`vE~њx\ؼ6Z" ^9Ol0@ˆ1PE1Ş1bhbl5<1Aъ"*E61s[|I[1`< ^ lh*EߺjXQ6ZcD/ռ6Tf'K7rfx)TDRߘJUKhlr=ܼt3ah-?PKEd䚨{X|F;Z^/W۪% `kLL=էw!t*]mfWv_ynІ'6iDXBg0UyxAo2O}S%!`5,LG8]u)C%zU٘+1nO7Fm:_o|:K}(j>:aI ,3'Ϗ {8@gdڙ5*n!N49\5;xXkim&'&A8|陹»4Vho/tݞradWk!b0ןC<~qN͞61]ynDsG:>*AUV_oVބݗx"Ow'Ux#SDr-PGC mjo_aڛb6;: @M/ZOz!!Ijݞolq`M3g圜}k+}GNZ*oߝT9:+ 9g癿L˹8V:?<1"yZ +ԼM~&` &Z[l>^3zQu~ynY__T'hK:bHZbxJׯ_ƍK6op㕗us)m^{ =:EU UZ <_ h "A@Պ!A\Ih-ZZFfjj]8eY@&#?QGO7xfvjnn?Q @UYZZb~~-8ΣgD&yq/x11o3Nrĝ;ژ&]>RBUx8}'0⡃s5{ ttyWRZz~‚kkr6b8|o[\UWw2UE{$y}0#~JO dY^%@9zUh tRf-΃s&in9ݦcnD z]tV4'΋rdhpr.3ETL$v~rk cW-L87Lf+S˗8tP=< .+NJs>J(i80$/Z V.qgbxfT-%\VFS:#.*i1f%y/qg؟j{wië2ށ>p.`u4p>(;j}U]x_`{똭8+ UXOH!;y/?iԬ|iUAW5\JۜWADx# N<ϹWasPLPq`W#U{K  X1U9YO5ϙ{ՒAY cWsd'NSpdž> `K|_@5ۨe\TMW!$1W58h05{oaVt%4uT+ίLu#ݜݷUFlqU<*@#X!Sж۪~-r1m/m{8~?տYbސS*W):Y T課m-V7B|i`+/>x֯>k ݉cU}1AϿFeo_24g;ͩ%1VwOzJW蚘y/~k}:|lXq `p8B1d|OKu_,Vnվ$%V:>s! KRq[V;Mk0AdIDATxyy=;[ՅЅ@Y$c0L(b$NbbW9'q%S. Ƅ0C [Bh%`ձ]~3c;=qvy;{nz7&K7!$f8yr|]J.%JKW(R?J9Y)zmljgpjA43;ZpGs]{oyiPk󴶞Cw !&B`:ϘkAh4Yv-=)'u xr>xJ>~4ݰ]ڌW_@ !gуrh3v^M!~ _Q _f^W;|OowS3wncj>پ}>z;D а,: ds+Bˤ--h@)XyyO7,R+ =Ν)|^ ZRk`AO$:YXW 99d>@O|zX}]Ϛ3=$~4fZﺍ˻?躉Otƕ@o(Hhoo6cS'_ O1:Y|=mAH<%[5&,8/<@~J.ZBI$+MYĂŭlݺ0}}Qj%#Ld/88|5:G!':sWAo0OBapO󕯼` )#mٽ> P(xCJ`|U@Zk a]0Z4uL}}r*ɂa֋:B?oB]\p"z% ^{! 뷱ܰD)|+OҲf&"$|Os@?J) CCDQDQEŸ=͓jS3RJrPrEVڨP^ڼzZZ;hhh4M4MGJE0wم_}9C yd&r$Y6 J. Q·LÍ J8gYW7 *2_ G)j5V0yĴaz*/().fo1+C,&g댟yˊtR(( @oohwGH{|bz[  hdtJ"U&qjϪL<^W%J/›;2"ТO>'&ci4L,]C Z)(^ /O˃Fx?4gH5r { c tDU¨V jEy(+" zml^c'R"+UK6qe%X!`Il;GCCaG611S yN^zɠ;N<:v>ٱc ]S;#r#4rlN'dj2ah3<{TEu]xn;Ň>twy)m]tqȗ n,羯D< W^$5;/cG\H,z%z :o 44dƕWz=f..[WqU^% a?6xo^8.\P'z5M04?ëغu% '\;##ؾo2po[,s|+F ٳ+ޗWC45͌ȸ"F[[%etc4M`%ȓ8O ɏsk-}{_q$~B@WU]%"q8}Ntsϕ\I˝дy#?څ=Dza%ˊ2x@3Uc˖0* !Ua#79?@IkZ9s۶XA,+N|tzV!ԩ _- Pաu "\s]X˱fM+!^Rm8_xnLJJj㦧hs\~,]|_[~M'j̱:HhNx A8\*!+p7 3ՃXa.56șSR}޻uz ?Jݭ 8͢Uq yG959,#E^;DOu7TvRDz=cv^@:STtM,ƑJ0?#^~f'6䡃58> uzxe2GJQ *p S G/*QSf -=',^|>Gw0),30ryn|GB9) ;hp,5d[xbL߶Sf^ԥd5ZIu%x"e-]oq.\x3o!Ʀ1\ACd>+In E5ul`h {NUy4' ,Y`'̌*6XWnXܶ%DR!&cXj2Cld ԏ =GI,2!b̌t e,[BJsV-H29܅}釘b|Mkh>s@A&PJZ0 V:`}݉p9@6zӻzα+9s]}V}IqH+;~fap,Y3 /FOq`zC{?d1w ,Y;[f{+ݳ⬚V1BW$0D_w:fI4q_Vd!ܡ~ 9U5Iw (֒@H!ÐTʜWR߲sxT-J3 9u.\B.b-a1 KjpǞu8 ʼn#?cldTw%T'Jz>m'ddž6&X@y1W=Iӫ{h@IdG"\9ZuϽ4Rί 9&S4Dq RcZ*,*P/BoPL$,raD(~t+u~\b0_$ -Gc &I9f*c7=75:w/#06P_%tEXtdate:create2012-09-26T20:26:26+01:00 %tEXtdate:modify2012-09-26T20:26:26+01:00QIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/br.png0000664000175000017500000001301512625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>KIDATx{dU}?~ͫ{}KD@@! LbH%2V*JYeGRVEԄRh(.k\xS)El:=5>*h&p͸ Hb4q9юv R R @)i}zZ;x[s^qq늩؅Rzg@h4 ԥK:ڞ5ޜFZiV|: :oԣ'(޹kO깈sn\.r!]^X;47 \ C FtkO|#qWs{jkKFL);EwU5>$H?}3y yD"1B\lߊ/SR_,]Tʾx VsTEH wqaQPB@*VC}imX5 15\!"*u=~krOϖL?MQ^ ;[Ǭ(.Vhi= }vM׏%(p^*NcQ\;Ϸ ' O :ߍ ڠ:鉤⻀]1. (Є6J++J2n kl \uXQt88ˍtJQ-|os+v\u@ִbRNs{*D0APZ4BH. PXߘS*<0Bc3'FOr^*B%PGM%b$uf /ڐC 'P])zU9){zĀMHqeD"B:`=+PT>7(zbEny2FT`6Jk73,h%Ӝ0}" %V婳傧k448 0d!C5U x.\䶑Sj[ׁVEW[ywGNp(ӆR.tS7z֘!"U V+ +N`S[f`iZ"LWJq]u/F4"Ցek*??ӽ69}PolnT -n {,ZW1vY`"ls=,d[)U"#6<#ӕ̹5ɈGf=ϽXLRDɆ7T6;TQ-bSKre" 8a1'6sm-S(E,-cz;2\8W^xSXZQl'?cz5k+u:/X &ms˺і"ehm_L.g/d1ߊ8 R%|s;t>W 7oѴXnT{'ٗnhujo@=QO1`+|bt˻3Ĵxpdb&6 \؎p~}rdrF'7;}XI=x{.rY40²f~l&榡YiQMQȺVW&FnűM(e\6zC່TY&(Z9QHhkҹGn`vl@?5_6裚4;; g&l*·YcNwL/Zn`3Fl_NwZpUj8{;Z(cm qf@it&~xhTmd/>= X7 {z?/#l[7FuJD6__E a"puo>(<_a)WbPU%İͰxex﹇1F@Sn;E׉A^XjP1ÉȨ@莗eh[UQgx` znɖ2YP.ѢL/.0lcy^}mtBRB41X 1zj*H`2\޽Ȯu:2|s|gڰ׍.AME>>2MCGm' nO;}/XK6RXZj,:RL 34gd8ǖafZ'|k Z6qrnB '7oiOs@|Z:3lk񓓽|B%&]/Qz-Ne]CmNcS}kɃifVxz d0= ,b)EssMml杚R<+/y<3s'-U54RwhOmbOG7 G5M6ǍCiQȢղb[3IlȓW*=ˎh7[.s>h{4S JP, WJhpLX+/T/>sU0S!EqN[?o V@逮 [*U`]al(ҋE? _tȯT(moU ͗[|hBˆ) y[}.2>È8 t#. ^nllnfW(^b2P!b</з=u]qڶW7EZ(oGK1ngr%hsx /P:iIry(ke;]ZdKTVa)W=NzH._ %.:HDsj3\j x9׎l;,h\qRxDE p  Vjh+8͡6n=ElJsm9.ܺq g;EoW|Ղv]8C}Mceu糺u̓$[X-ج:~_hjX>^zC ='!Y nr?/۹?'7ds*f:WO=*_vppOy.pcJ$dPF S|zg']i×Pz !6_Xe*T]BC@TL 9GGf莕B[@O@+C=|+M.|E<<iXCğδeWAor '|kf0? >][&koR+Nʢ؀jZ`ZO:F[s|=H`}:FbJ<5ʑn㶑i( S򓓽|orB c[)kUq}@ymMmE#]-RD\aQ*XjaRYqϱ>̶'iR6^;zugT_5q)嵚n@Lezy,)ҎR(n_gLS ֪FTL*![:p꾲+Li<20Rȍ7{Bǭc[`jUi&ToF0%dJR>MT~5^8l`( {zc#-f݂g" Ae}`{>| O%eJR@/0lYhKorW,F̼E R,-糯ڶ=Ls.Ek\{ܣh# Ī]r0]V8ˆ( u3.16*UKA?UgDy,#оߣbpt]"ul=&U_YUKU QǼ Hg*jϷk5>)w~#&%tEXtdate:create2012-09-26T20:26:07+01:00q%tEXtdate:modify2012-09-26T20:26:07+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/na.png0000664000175000017500000001211412625666430027126 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy?ouuOw s0 +"QQ@"јcyy<&'c<aH ^BD2(r823}VGUuWUW dz?wU_к_&7(e0kn4ݶL7{B>@-,.zt`DB 쁴: ݟ@)%\:k.PUd"瞣wn`2.H R)JUuXHD5 <T\S1Ū/C€f4j.p 3>+>C)z!tI83+XP;\דǂeKN<JU} Eybw$M`nAa9 ň"B&<)RT<ԓRl_pೠWWh^53&:={sO?XQ( )H&}ݥJ4zB!>߿5<`-匪;[" #1MA">FפF'jFn4בiSY/ 1?砐)A¼ 7^Y+'P$Dۺu==pKP>!JBDۯخi0l@4/R|~46ut*C Ɣ#D9lmoP<BA}ϴ)ǥBiS/ fMwLczHD"N׶m^)9K@,&ĀY$TiƉ%(҈(NO .9y(g9q)a D S5iŎ* saxBbghk!%n]k{gd!%BP(Atq(hnC@z4۾r?NUi3"쮥t_ @kw /~x3R.8{~jtvxRJ5,~*ՕHΗ_uZ"'O|ieYl mu| @lADO`lTɷbRyw>$W} n}szU"{uɘQ]r|J}Ɣ+[Gѡ:C48eB pJF(0Cdé; g5L @W s'9L)oc4HIܩ+ Y>*Wά  :DSO{dSul6d:e9j1>0ܗe AanBHy?= 'U$],k'SZ&EM_O(PCt&p2'/%l}m -]%BUֿ'gTuxӫJc4fOI޽^M޽R|vugHIMCeҥX7޸8nC#[!$M׍vy!?&ڞ~͛zz _gO5<$> xf%9Eh>=k~h"I)U8|pNYe'+:bǚzǼWw9z 0TBL*po}2Í 5*S Ӵ]K端:0KUp(3l1+(\Qy9Ҷ-ι QSf'5UDB#\)XDؼ5k]˛+ڎ@1#\buB ) A[wQ ,~*Wͨ*Dh]:UHwb0z^p4sn@N^80#1Tg$,j9''miL KPk۲&А 1w2H6aSu%˖#+ukyzz❈Tȳ^6@y/nB"uP`zHbڅL$ x;"09/ءeXh,T7`GOH#b2o}DLEc͔% u(yrD$;;iyěR0bYL,K[]M{W3荝GNVq_غZ'bB)R)\>u4w}U%(N4?}{ 6lp}R NBٽRx1zǾأ8" &m ! E:n:1sQV˲̍WVhmgh߰D{ḁy%[F4 a{ Nȹ/H_ ٶ"m\RN]8 jNc%cka A"A4?Qb\spdeMϓLuB'[-GeϗoأPPiq78dv+8:Fj?gXc#kбiQ} )[SSUi( KPbɓih叟bG{R檛Iq6x'Iۢ=jN8W k7zz,Fi~1"GTԨz,;4 1gוXAmЂ>>l=/wN?d^ եd³wH-RƦc<8/$%!nZU54x$uĉ\Iw}jƣpB(F59GQ78kC2fE5e}[-sA{ޱ\WòEa7TWqfl6};7INZeSuiN9$.s 5q[Ao~C5ěQe g u4UU\ȻR;^80Ts|q͂)~l,ߺܠ;h>oDQl503_$i@Kկ_r Mx "#"; ?h+PдnMיg?H6 R/~dHep!껎!Jݘ574g ʋ"??Nſ{|jCC]G-YB?KgEI6{ #0UG*=COzCs˄l>R\ +5 (_Q?!ۧ7Cw3T̄Nꤽ{A{̔w1UWx1<@h9{Muf'Pš2ml tB Ҽ~8Y'8m3~<+WR#sl8 =.8oGav|Y2w[8d2[YawO _w-!wwױ-ĒG {ƅTc ۧ&.Duۻ W*>n7+}I䪼~^80='PP&&aיg.n7Vq%HbjuR @ ]JRpMy<ūĵ9թf>2y'έ~Nkꃔr'wU{\@E﹇LIDATx݋%Gun׏!< |P! "% >QDV" EqbXWM$3wvnWWWu׽s!-VWsNUWt7Mwk7*{s'$\jNZUM~h [l~c:hEHf7mU^<mU|UUb_b(2Q"ρ>o|{tO1 ̓g7ɋ=x[5mNjZIzMU^9<Ϫd1򏊃 G IyBuW ַh6}肘Gϊ3yvQ1qZ`u-w뮱u1*O;P`Wb0D{$S\9p1oDo0=18׳k:q!s\o@T1UEL6EpI7[RU-BfLlD\l:/奬r0znS7q A>@CN tZ;WAfÈy1~/h< uN066: wG ͕VZh;ga:44y~ kuoҰAYZҬ0 ?a>Y@Y6yᏩҸPC jgD+e1dr6T7Ћbꡚ?$0BuPs4 0^obe,{9䒡Qr֡ܐ; ?Yo┍Kq'H! d{H"PPB?\&[9PULUd~M4yL^DD*J |hC1U\ګy! t: X` Y`s&(+8cpl=ȱ21fuy8u~ lm-Qyܺ{)e̕Z6,""3|P#dq}W;Ϩ݂K!ϑo_F׺EH}!.Cw]%RJB t+L[EzpbqPP`a˲m` @dwje6P`Fu+´a>𨜁sˌZFfHJ `0o  ,0MLM( i,=2)ƠV+ 'B ^s-!yCy1D)-Llf:4:jObͱˇqyc"Pk}k+,Q zyFWp{Yn p6YvI24F,Qe hg+$n'czd[Y o> dd8%XNc=W|0ʩ|FƍePx劆.nym?nv5! ,Z>dvJev;_<pÈ2ky"La:lCey*e:S̲nإdž1 %g,ƭ.rV3IL@U Uy޵,/4 \!@l;i_4~ _Efo m3`(@% o j8:C5mRiC1ُ{!% LJb'lj;vR`HdRFwӝHz%tEXtdate:create2012-09-26T20:26:40+01:00-%tEXtdate:modify2012-09-26T20:26:40+01:00p.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ph.png0000664000175000017500000001002612625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>TIDATx{pyޫ+ɒ,YdKyaҤ@iK:yLaI;i'&ıy0!u ɲ^t{w|c^޷dA7^;wwޅ3L>>J22Ci%̈́L^h1 ϭNǞ5J)J) )E6={VKIMav4DdҴq&;~z} B _{u -Xvuʉ5Q4e)2ׄU̽Ke+5K22ٿ1I'|Ќ=}@:Xh<[o/X,{~Q'\H$ Mm}:e29l_w' f.b}U6|muK G:R8=Śrv{ʞ#Xr:dنGn&ᆫlfKD Q*AYi-fձ۽^-&[-iL$!7Rz>4Ml};W6]@'O55-QEq8[ mŧ!xX ˀ7v՞1s)gf-3@y5t Vsv~Gb`(m !5ZOQ(5հ`SjE*U3Ҡ,m: G"YZGG㏁qu {6]kƻCK7~ͷ\̕/*n46s>֕ĩ}9B7āM$] ls-o$ֶx&QNua덀,-\ȓy~BDBVhZ D-p#P322_+kp]XZDbKDj _Zk.xcEHsPQ5}.."D9GtFFhJE +tՆӨ'IKWa,У ` r>VʇǵR*Rj"\sΚǎsN:? F DEIaR _%NV*ĸK9'0zCMWfIaxE! Vˍ^XB"ay"$leW 3uґݐ:O07pwi(XOsZLCŞ+Rx ,7CyCoI܃a// Kv-l{{Oסτc5Ep%TE+peq/[qx *63هIb/$ 3у5`/+XּU||…`'f`0eP.z;3ڹ(|)i3rۤn%}p+%D)GVHztR]MO]R<+.mǶUvɀ(Scb!x<@fp,‘O%}p+Epʮ=8x݈3;⌐5VrbmY9M+[1ObӖxgFKu(մ< $?{C) ,zȻ8cX7  sq9K؍. 0\vm5W-µ زzXV9<)bΥo"=/7,M5 ˃fi9xUՌ]B-3PvP%MW=1vsYaxf/ytM+o9߈3xn)o"oֱ:*ބ76V2֋+*UמB8ǞEWZ>{ݷ9 Z3rN]<Z|"%x./W(+N'mu] fpvT\i ~%``loFuiE@(Ed?p>ZE>й< Z`>DA."qح`uAa;vXㇰ[/F״ ML6a^B\\%9ǻvf 0oMY$V|ր4JWA='=Z/+3ü&m0;F} LU,ʎ!V.,]U+ޱdnjv<'wt/}#}@[>2+*7w pڢm⋯A'Z۾X#2W^׋Na#)U)!`urr;2 ]2?ɗtX+]3;a nl>*gv{Z~ ZfK_z@!dk`ƜIIYZYKݶT#( >wx[AqPxm)0UMd q=mc{gS얫 Xu+qc~[;  ̫hfD6c0+JaC8dxvސX=ݽgO׋/buM͟/$ǒ={{F^ gXN"2 SBFvYbyd۶_MRUfoXϽ?q,!p2`Y=ɉWPP:l^Q߰%vhPJSnW0{g"y#@2XT[ #Χ bxN p(dža @Mw8N"b`"`p 8M"@h.'?dhh&ulYʒSZ_F“q(*r9&/ͪGG%tEXtdate:create2012-09-26T20:26:42+01:00,%tEXtdate:modify2012-09-26T20:26:42+01:00c?IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bn.png0000664000175000017500000001324112625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx}p\u羏 A)~д4َq"[i8cNN_鴝4m3=IuF8qĊkYָmɒ(EJ" ~ Hbw߾w ,Ňd):3ov{s;#"oѵo\$&i*342W/o t-xpO/ߤAi0꧋խ늲7>zi*Z]dOx $mUK??vcEXCX\6.6fפLM犪#nX1~=Ï,|Kׁj2~7@a:|{۝fMYYVk[0/)l>@V\?%7$lh_dv;ڍr%Mkj\ٍNʊg ZQ-Tq~/ݗ_|gZ {ؗ+Bx$uЊNj!AZ"N+( skT[u"Q m1{W x#ޮvxYkj>14py`F@{k#߿Tn\W^T(F'F<[+j7L?`1YD/@u![? iܶb-FV3'TF"}>iJ.kp׷ k9%RM\zb2Hj~'~cdht*_dl4?EEVjMsD/_=,{B=1RoQjNVTs5-.?Uՠx#^;v"Ԣnkux )pkڍ7wv%62 yMQ>B'dZBkMnbX7M9oU<{~4Fm 4Px hSKQURlA-s3bach{REݻZZ6z4X'{E2w,uP `"x+3?);OH:AOfnvUzkl[ @X&7z"(_S}\Vi=8t͈] 0H۽+0 ΑHOD,SkQטOM}ʫV!֒߳obR);z. m{t]hHݨ)XmMSe~f)^Fe3f}=md{S+իP9_ 4h9,0 +5R[~]PdsFy#IljMqQcynϓpL9UK=qF`|Nrw{y˭CԖ0v^C98$ o[{2+bڹ&@<<aEIC[ J*;I~ W!Epl6@*QąerS_' T͙rmmߗNm;w(Jw'c+Ρx- sƳwݍx銘 lɜ}cWT] 4Ip}z^,c~+_xݻw~ߏ?5g[}{ }0i^ڗ$.y|ß }y`Ŷ,_n[}% \uW^>Y[6 vs\.28ptww!vr/MqJa[lgo'76YpzrW9Fգ}.͒Mk`ZOC8)W<\/:%uD %n60+W?O[.G?۷og޽3d`rx~G=CXf_x>N\;w3?xYu7 ކ5dk|pVZedz x JuFrbU%[wY) >}ɷo$Z…,Ϝm/KJ# ^R,,gsxt{6u(ߨp(CnUG 6O tlM< M 6f$(^x}>۷vQ4F`0m "Յ='q>8M9"EJ}&ŇR(Z]#1A$ Ӷ,NTҁC$J*c)^ʱG1ƐN|.ڢBqsXJ9̡,\M. 2DqM =VLUfcT *N`gJs*./->xM "B%~e)w z|ϰuxO+[  Y/r?HS &.X^rO)y&^}s1,ZDkˢrc%pD'\* EWUE@aC8/Oe;:s_a[/a յj% yw6-2lh3LL9\vrY1ǵ' 괶NEQF8=s(F38WR~y:NġN*)1Α7eAZyer梭A=o9{ŒWDŇsJ\I" OJY?4Ȋpa%P۝+O_ 6vp. .C(ԧjP1*Qq5^DRR 獐y(ZԳ'`{=wC ~Bx5DI1P5<'Jf8c~RlO0J'x*."u"R 5vHpnzPWm^ [ ᠽK! 򼣭K!q8[wvzZrb$Tʣ>z.v xʣ􊇱‰ŸoRrvџ Eg0Q _z [4%mhJkg;P8h6WCEL9ꓺ-Rҷ&d8"$s+ m[*q-l<_JnRRԄScDVofV`UT٩_vTa nN[ ٜTH]:&[_yJ\٠1TD.2DL9&򙄓g-Ƭ\(Uqp֞Hb ^G:5/-;dgһ鿈Y m AxZOT\U#(E%~ȒK} `"!uyrh.ELN;F/[=~:W}ᕄs!NjG5Vs5 Q<pׯL\uaKL<<”!6dgD }*sS zk= mlٳރ IDATxۏGz.;{I q $qK@!"#< @<<q@J w8;^{wv=;3]lq\LwWWUsNUzwnTZH:J!rO3#?w~HdZAhҢ󁇾#\-su`Q>"J{4ќ0M78 |%us1_z{!WE`}M)(RǫO7\,T^HhxF|4 &0cwu8`1%̦H,Y큼"168x LVh{ss-oW7cj 08A<0\!0(% |v0kpvȲOk $s;TŰf_H$s&{Ux ^hWyc?})ݷ^hܤ+DjQy%O'Wq~4S^tw=##yw}aQMSyb5ƌkX 3dR gPٹQ$RQϨxKK:Ś~f`yX5Z~gw?Ƚs::P*4t:Ҝ,+|Tq[nbx?[zA6rXGnC6**05zZ6S_D6|{矣iz9msVog,/m$Mӫ8ժn-T#1eakIx:* ͲX[Bt6˖?˺['3jA7jo` r 6_<7^4r{g"{jhH W7RJʡby wP]t&jrkU^rpu~5; ݼ,uq۷4}C VIb^$jXkޢ+c)0k:XQu_9ʧ~éC9Dta-I,X(s@`$M[2 %ߧѳu> O^r]36Z_zw7W9y7= XdH;5ᬜN#hɕ"-ؖdRL"]~z~g7iu*tm׺PٍRݨEdDc@Nb.2);+LЈx9~忝#<0e:1_Ⰲ@ [Hic?jO9"Q;fڹ³{ik](бS.Û*OSeIfQ)E3d`LIE oeIZ+@ׇ=|[ÎKuj&_C O eբa4 d(U|O:Κ+@{<wҾ81-q 'eQ^^ܴ"=9<rѫRo>z_:Ni'"0XPM ,O^U~" 'PITPN6*".>vׯZWͭ!5b0.'pp-t81qWӫ<@f͗@ jA,_H0Wꞟ7H+&>3DZ g1fVhX&^kGY6M~hu|7zH`A=WתVp$PEM ۍ&8UC/ZE=E]jϺTW X|wնL9FkP##Gb {pL)%pW=m{ޅvH}g6 vi(V"fm{~ %.F`kMqmLK@ʶv\_@͆Y6`C88΍3{n7 4Ԧk-s7{g\:,p!$$@h$ K:$m7!yl6TG(ꮇ6(t >rBrHv&Fz7od |K@Zx$!|+vj_"+(dMŵ^\OW#:Dzy=%tEXtdate:create2012-09-26T20:26:33+01:00%tEXtdate:modify2012-09-26T20:26:33+01:00]==IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cm.png0000664000175000017500000000654312625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxM%Ef޼ٙ]B7Eb +!p &聋p`G͋&~xpHHH`Bh²{U]Y&=ݯW+J$P!,d9gfÜ*h˳.? 3w.ln ""PKƂSnW SE4jV^$؋=2}~ax}-/Q/_]]H7^9e_oϟxXrHAsۇn_ٻwn877Tw\fPos{FAY!R f>zt@bes׏޾FL 2Xk.wswf=s1 \1J{ai?h %o68^}_??s= ]+n*KK24 F>4 (LJe P|L:1X 1sd0_T@PB>'1{=Rp ;ӗ/$gT`(%tY(4YPDP`dkN֙6ih,B/ p({#? X]L ,4* bʎ U(RkTQax.^xNmfk T.闋DVI2IeX[ ЖkN R[Qc߾DI/1D6v*;nlYYÓD=)]8M  jH (V`)Pa;A\>$MLQϙm`-:)tvU* WG M,&%Я$)u%4 P(V,ݦ 3NXQ@)3 ,*K9Miz+I 媛*f /H) G)A "=1$LvW6` id'@{j6R++{AYvv`Ub C0X80d0^_곰TT͌k߫|i IqO Ee98kOX>Na4IȹS?VVlgEAWWuRKX(iTgD8?TJ`"&Ů o>J bV @)O$!P?:]BX'S<፷ pxw-m"^az3WÁF7͕EYtsix q[6Px^lJl*Rp ,N"nj,}aF<5ʩ#rg2K ٤)qFqVm}⤯la.f ^.wu/Wq!U㭫z MSmZU,zW9F)+orలVx{&Am , S˜Rg  Ϗ}L`1-})Q,S=(\}`> Q֦>xg@1l F*\UgY-#xޭ>\r7DŽimBM=6ݪJϴ"]AHo󬯢_|g'`9=qEĂZDbղsoYUM@:laZۆ-u4sm_ iB|Yo(v%»O`z.I:ԸuRp-e4o* k0)epa(͂p[e.Ohc.:+DP%j1_TJQ0 HŘ?PQak98Qߖm[ĨU{t: eisWڛL<`OɜI[m ?p@m~ zaLXH>Dm*YӊXe+BuͻL43tYm,+-me햸 vC EQZ6Vn2O:mD%<&L?IJa a=VKxy ap5kqh{j[(cQSl_$qb` a4*봭= Q 1Re,BuT8o?PT)9۬Fas ykSoN76mm => `=rƾ93OB1V5ZŘ&u]1 6>=EzV_3=쿬.\2d2xò|t5 }Tx{auO==ѫ|p(gv|~-*vQ3WjY'{KjRSRqNoٿgy?g vYZ;]T~UJ*}U !MKҲ\֖KVP>%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ne.png0000664000175000017500000000474612625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> $IDATx]Fc&m4iBڦR(@K!UT!⩏>/7BB@*(PZPJ~O(0ߟnrv-|s3c'ܓ_9DI$ 8q%f8{6 {_==~hQ tAKZ]t}kf"Ӳ>xh_AU5O4]Fnn r"@t<ȳ}N$A`I{HX?Q+Jc،:^p~yܸ0vG=ay,Cӡ =:e%]}Ѧ>UF 8 wehU@6P(ޚZ'4N/@Ӿxu/W&E&zJcjmm}[`:!n\P Ϡc^ntɊA'ڹZn̫V;Ǹ5@wvJS`TոҘLdM}-Wf=~݋H7 x~;Z|DC[WVEx5XyxeF_#G`yuڛQf ++p0+@5)iaxØl7;Cîw[fcIDulxe,-O|)9,n_nSw`H8zNp/p{_sf^|ٞ^9 eS l}8f2l~n<=qo?f-&֫:Z" \+ n4eUpc4`2̿ Aʇn/V-A\{`ԃǙqGNsZ94/ΡX'i&3|a̷a~He^g[u?-8rr$7>E[5"{5YC'mUE$ڪsOѓp-vzwŽ;붤d_m~wF-ܢtbcfe>͂Z WVT1&%nr}pA6;ߞ gk,AJZ9ֵVcPZէ 3^Q zՄ3np5t{DdsLR1$CƆ1r֦YTI!Ƒ5Pc/V><V(&KāX^ݼC@Tԫd60 o[_e6hVdI::;j;GL6G=!ZW ۹bϹsfᥠ 1إZ3lT˘"yc)$;R~MX8jj*e&W"⋟A.Ү =4X0ޛ# Eoj"$DdR.EfL?} Oq|\bK};KgguHm(ؚۮ>U,&F꼺v!>K> 0P+Wd~v0I>&ɢ:{[4t=ZOڵ@W }/ep$ 9hwO>D{ߍb`x%=4M+ B ٱX1@ *px[gdž(PX׽A.` ld n2N D@5؏#k)t`ۺ!&k#q$>>t[/Q<$*p' v%tEXtdate:create2012-09-26T20:26:21+01:0056%tEXtdate:modify2012-09-26T20:26:21+01:00h,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tz.png0000664000175000017500000001001612625666430027164 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>LIDATxmTՙAafES1 +AL*F^|*T a]?ecj$nT*h^( "(VfFef{o݃Za9COxm^snlv'4 wÐj:RJ)/S*p]+`Iq2 ab55psٳR "if٣_Ç 7p  b&^:>5ّlFyg)}bx < / r0  Y$6c@p萰w0|Py-N At5J:\7_R}e7 >kgn@M.d2(<%r<wrk0LV5"rt*.2&]w%7B(7ҟx08)tw &)(Uj..R(J&?C30 %*SxFiS^q 'bT D,ZtdS, q#ȆC--³ž= 2"R !K= ]\$@}kԸUQKۖ#j΄1y\?ES̛%LU챻~cع:;F5=CB61*iA$wA*Yvf_%}BB,*QNS@kub^!V 8kaW4'v&ϪPrD^^T bEmT<|N ? I'$WM!aRQQe[9;>v\-0 VL_"VŶmC22ҷC&mmto5HŒ%ʑaMW|捇Sm5UOS:i J׿n./_@'VfMԵݵnS@VNo#j[i g>:1;-EKL @$'"lTgZw7GFuNsUmVu0c?5JjUFT D⅁+ndX٨꠵}6WVu+W B_ILY.|TTy"TƮA)w5 6OY6Jل v)ЍUi/۴6E; &D;Wh?e;b,=%1X+,,لQYK |{f=aӢik>/ +VҥBC9I-xeoxJ 3C)"h!ݓp(7PuDT'%!FXS9.Všzv]|UU'V_5^jBi4MN0&Sk8pݨ(. ?\&l^W_3x?VQ ʓ"*?N.B$\zpl3FW_;$T*u@Q)/b;aySu1*IyFn7.Kix-KBQcvh-߯xanQ]i 3ۍǍ3sإK)&wm୏撏 OۣC41Ĩ-iNWˊۋ ]T5]36PÚD=_ Gá:st !0ZsSg.7~gTy*fYhFŪUpBC3B5c(p%Jg4&9'6S|uq:̼.^z ~S" оD MM*T]\/泥85/+@K,;rb*RTکpOBu+<ⷿ-V*eTdK8}:K|] KRT]l} @V׮Mv^HuE i&[ -a͟=gۼNӓkwimķw\ ˑBeh^n/CՊwz ~R I'< ,_hh9n=ƛwU/d򊬦Ac<%m7ٳFu۶UQ0&ƍ­F#H^YSP  2d/ a7Sۄ``P=$;bj~Na[>Fu+VNB=ί|`KUtuB w|_ aI\ʶ#k蝄AS(U1ٍvh㬃s *9@͔ 0fQܲe0z7ݴ:擗 ުLZ] O>E@] @+>SEjOMd]&Οsl?@]r 0}CN1v\d\ hc6[/,ZWTQxy ]gAP?ON^2|A~R*]{}؟Hy0!#Zvؐ|o ڴoMhoΩ쏋4_%i"`@g.fZ{?z>&0c_?tɞO@dG l4 Lgn@-}3䵏4&MPJ~sҙ  AO Juk$0:o6!Y4p&w zyH;Zq+Y9Rp!PD@ǽψ]y1q+i@u/*$ȗOY+U}Y+ "9R50TҊWۧJy}%tEXtdate:create2012-09-26T20:26:08+01:002֘%tEXtdate:modify2012-09-26T20:26:08+01:00Cn$IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ge.png0000664000175000017500000001056312625666430027131 0ustar fabiofabioPNG  IHDRP<'= pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxMlG3&\PUCUq4B8 ' *B* $Q !p)B9(!$1kLN֎uf4Zz7c"ku]gSBHsG&zPm,:&-_*0 @۷o}4\vi@P e;ܲ%^|D*^|ٳgcϞ={0rA$V9?BL]v}{#Hϒ%K|>J)- LE@) ,Us !9G2x3grƍ1) a ʕ+äVGXA*@]וP(BUz{{ɭ[~fM dB8>_wwwG8466" AU“,gR ]i4 |px] Xz( vV!EG0\D c `5(B>2PRy#).Zh Mͻ1*ʫ !`yL6nCs>#>\oہ?& %דo,X䚛#G{VBsNMx$[]j? 8^~>wXý{7͛vӦ}ݻ@s؟^lիr~<~3G- \x?+S8yy8qB?~\Oay?+pNa uC;5kmۀsg:O7n̬߾-a@',桁ڃ0+?xQaL XZ HONGKʑjU:OevtY9`m-o}w#H>K^ lJDQ9L!\>wV݆)PuTR/a /;{^А+ 1 0>?ZŮ1U/\51+nX4! %P(5˂B݊\TG  Pj3MҚrlaHzʆ,ܴf547Yi2ӚHS˽@=5@h-Vi\m/Ia f'& 9)fyAD 10Ơ( K_/*SalqO c,]ס:R|6L}]+usR)R)躾(5abff&H$iEx]ӴLGi4 DBd,mSȅvXlW;N7%1ؚg#R('QhXJhk[sma еAIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/td.png0000664000175000017500000000511412625666430027141 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxMGOuά7kP Qg RN9rw !q d A$"tá_z+%tuuSz޺.\BWe(҈*pDu@NI9 qpxB}[f1c@թ ~ Mܛu;Nf_$6?hwc03Pbs.>po߾'{Ӿ_!E;h>j n~*NNbOv?SkDV.13،|k?'I(Np<7obXAF) wdCf1@go)56nDZ;:;ӯ?A'#f,4ڷo%:>ђ~q "v&^X.qx0@b a 9S@0+BD}-{j2@00a < JIll @n(%: "p赯ڌ5 <&B  f*ӵm vp~3l0tDY $AEE'^LM+Z]G!@}>g{]:'!F5`r#+ 8<ֿMW#p`xBHpߓ߶9a5he)2)F*L6R;XD^{ߔh_K0S(d('dKI Lqn Jgt=%m3Zﮠ4PJQݼO=}U< hmíkS?j"jp慸bx UIeA{34.7)W0״%I䘈wh@) C<@ND @w wŘ+_ǩE&ڜ* )U?`jBT`ؼhͺ&JȁҮڳ[Y.@TIk&/@ j]lƹgڜ ` ^m"dq<ṆRXBvm;8Z}皫f*~&H/&C -uК=o[_چ^?Jc] M1RҜX$X Q;7ϳ8y'$VbvA [A+|ఃo.@h\x3 27l+فN CX!H\evAµsO!0Tן,4<pF>S!gCȻ&gW9ИpM!%AZbƉc[\77̄8Y5V5R>tl Áz1Y>Sgm̹}x&Z߫iL({f (B.D ` п"qC'![18%p \¬!5?x >H7f b©JA}I#J%4>υȇtk| Q2l`NjsB%i&M82uȖ}oV m180dZ.? Pr9`ʔQ8mF0t P3SN>L24_vN)X "!Tjc0rTry6ysvkyB#@NnFcb vRm`V(xr\Ѕh}g5k{ S!H)1MqLNX;sbʉcмo ЍE _Sh&e4 JB `<`+s:,87--'A l8|fKFS<]yr'k=YiC n"x|pbͷ[üiZm }B|DܽUG1h~cĔNF,4Mw-i Cx0MGTܸS;kz;p k4/Lqc~~AYvcCL O4\H\hO< ?}#DH(_:JJ?}'f{Y7WZ`PfUMI<ӗ%tEXtdate:create2012-09-26T20:26:16+01:00[%tEXtdate:modify2012-09-26T20:26:16+01:00@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cf.png0000664000175000017500000000621112625666430027121 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx_ߙwlIv&"ڂi HJZ(_E }CŇb}P }BC VAVKؘzwޙa3s̽7f5|ٙ9Ι3y9/,SO o' pY2h։ih 4F{WRƈbm]{Zɣ~0%GEm1ly~1'PM*gב&v^ L%Js εjzl/Z濢聃q bİ^4;?2]ua8b141;vf~3Wt!U c6>[t5Q7@#0.Uv8({±c1FP1ҀI60:f& k&"@yP7N ֖vٕӋAp_~ͦ:WL~&dbpMK_ڀ;)Ǐ_WwhF1fPB }NWVb>#Ri[/@H\ODD:FdӮ Qr"WZN> 7a۶I#A \wèxzb{ij1, e ز z`&+^* @d"cS"\#5 ДB4.8xk7 뤭mˋ/~&o1 0 F0IY\LQ T 5$ 9UI5ba/pZb&?JzoL{> $$,IRfq!ؠaiپIy/oa۟" ׀NԶ2#$oP~pmYLU~/~テ Pn~m{w8yr=+" 'V@wy=nQ9c ]s,T7I QqlObN0=[_}t|=#*#.kۗ(Z4J(X퍼 WԔ]Ɨ\8}^CVV&x1za-YxPc%T`n_J;w;w0>JIt+܅ዣ6.3U3q~wf ǩB+:7ǏݰgϐׯG.DdfFdadĤ0pيfYDfg1괦k!u83業K2U%@g?9>C3hBe0]Wuwnea:rWK{ȹaud&QZnF :9,&R1^˹t!dXJF % 13{!ݣ/2H$6R=ګҩ^.el|bu.ˏY9 60\߹Hs㈿3_?՟5Q+Kvvy\rqu"Zs-N?=4<} O 3f$s7.'] l&k,vͽ/~.4U-ʮcU\YE;{ڊ=Qa/NvJ!䣞oYlJrVOLoo*Z,T Yd'NE-sϝ40ĘgmpeWyA]b;s,ci7mYE2& 32xQS:ٌqz_ᔴƜ}WP<}qMJ+;E:.PhdZX &6P Y"28W d5P5ڧYa6-iMw2 {DtsÁ詈\kU²DpK] Xa\$q mK5`uDpL+@Oh|`Ả^_fDeJ=nj(rʆ1>-UdMvL8ӐK!YSy佲jz1>Z̯3ȼ`m'Ju1f ;r;9+@T%>ЦR~& IZX aF(&(/kq'='] k_cU?\{LLU4u(6D;&Y6}Qujj\i4)|X'R"A)6~9xN? pf%Id $FνS6ʵ U/'N,0;n#OUVU- U> ,!/bP0UYl nY:7_|!QXM&jAWf, ]! 1Zn!_vӉԕYz|K;5"EI$dSl{{. cV. `! 0IB(@tkVʴhݾ_kSMH}WtkwAQNg8dTl8EUfzH86eK㕍~@x فQ#xg8IDATxϏHǿ,&#!E97pъ(HqW\  eB E H]LfznUg eUO깺R.R.W}}Nc@mueiM^! ߺuk޽{_`0XV$8E) s=˿,:a5fH{>}dfI$SO?}~N1 ܹwƍ^rښRJ]X]ۑd"1 9VWw,JhllO_HKD@`8͛77e{{m;*(]NeIgV*R3.zc>tAmeYuxwW78#JYooիW8H"Iٝ%ц YۡG d$Qo~WG'CW766>p(PHHBƚhyɯE_ PcZ[ah`])l*y0zQ FeQ ܲ(aybd(.0¸|PԈD(M\Ewe=ę jXkH ktQ̈!Yh֑A+{4B&“͹>8"0DQ nC]<Vcn7_pRw cK#1?\Ʋ3h\uK ax q==l hgwq{@gtf=K_Kl @D\ID< >BBKXqNDPuؕ<>z,mO]sukR c59h/AZYq>yM=W HYkοRA2Wvc Fk4TCe/4]p0^Ŝw>/<T(B2f f]>}=y=(Y}t;X-@@C[07PO-0Ahhۣ Wj۷9*YPf$ ^is#, !a ' YY66K 4n9s%q]]}ސnxd>-fXޜeuqA+6}U{~ `eJ6`77&K, g${y@LHG_} Aép5XXVcͽ!uðnXJrF{lﱣs-!ϟ9|+| У#\vfbj.Y*@K;B @t=f𼥊k۽za}l9\ל y~@60_wk_hg;bIy,ːe(ɅF!r04gzuM^ xZyc6a6!MS¿1eLZ7v>4M5H`F +0*xttN&NS( "gI4I4 g.{r L $z `F?zϞ=;yK)U"<ɋg3j(f"wO '2?99Ç/Kmp]LR3Sq}3S:ݾ}hk0,>DӧP/ft~1% `a_80aC/`//s(H*Zn@0SIDATx{y}5^^cm!)!*J m m?*UJI JQ@PqH!!Ф< Xۻ~syܙ{S5#ٙo;9gOOCQ\$*OsNVqrqs}>hhj?7p&/W[F`8wV6/NHH)0MŦ-ku+>JN_|=sFBP((!l>q*7Ӹq#D+W&yw;V[ BT~J){ou_J*dLs#8zPKPGӁ8,v{x%1{Y|]gt!`nVr"Jʕ^e|I7q+5JC/KxӼ).]JP_߁ް! d9x.4w_z4M #grWhGdC 1 %|w;#ub}fC5 Do --̝on36V4uBjc`,Kj6|Cf^zzN15!'tVXAsMS[LjRQ('qQ u;u}鳚VV[@߼=҉-d4|4lM-<8 oI&'H) YtHY{B@.azzӜ`A`ڶb3!u m痯[wwK[<@j9,|dPHgmhdÆ.d2V2Y|s˭d(Er2b03#K6mZk܊ŋ23Hr]8ו;6M?L B!9BvGlxJc@MÑM+b~*EC2ABבR"TpݻR=yxl s'jkAJ_Ff!BD$B ;DY5 P(%uPP6) i[%VOG%&%Y!IfC)zss4 4 b|^<+\,M*Qو+&؍]s{r\= 3$l6Eݴ.'[S!\I5lYB+і8P ;D&n+aw]BauDtiy!aA^+,6q$۳n^/x''F$&1V1=qOYɰ`hhz={[ٲu- a# Wa[ط} Sӹ4DX^OJ+!5LriQI C\﫲*Uc1?;̅;_v _s^w7O0sk0rn3y[б4IWW:ț[ ȯb$g&u y}hߡ+B'R[l14iO>!ĉ+ű*R i8>aE)9mYEckq}#_տ.rGB:)Bj%tSՄHBV Nk3oҪiSIxD+y dn[8\,6_~Jnt^k*V(:{yJqPQ+􈌴:w@*J<>{ΐk}viWX,\ÏAW4Pu麤c~'GB^/UXkyCh%vI 'AμT;7;y^B@.FG7SYq|mYD(MR! mXL'1=)y睉ƗW65 es\. /__Dm5w+PP E$i0(-3x(cf-``R oYYQ]Vu†a^啷`~7ԙ铜] |pf\( N"_]:RhrBV6puN :rVPQ#y>oч/׫sU9eXJ9y㱉 zd3FP1/ Y*= ZLHXBI M(z3m|*R@.܅coW-2*[ۿ$ïĂuM@ֹ %ǘ6BG'aʥ4az_skMP*km*;EK"81S9r/ r9#gF+ s)[ᚓF.aI_Aa\)V' Ki -gПF@B~fAkRX~\ݸ|(|䕋kUU ; x!Zs]ث_q+9q*.碂G.\oy再QWR6X[}鴖S 2.;>W k)x.ha~Kmb]s*u"L(寷&ڏЅ蚵_9e9QϨ z+߭{'JgzW!VuF^Tp+{51ʳYцՅ O.qe_<&I2:W`8LV10T 1[yR(vix@Ȉtf Pa-v64ػ$[!w<YFk$=px~{<[R?V7.*uVTW*)] gc|.7P tf.ټbM$fg(ҴD|p@+С5+FB=^J9h  ft}79s 2 thYS-em T4Ϧc 68,[569Z $0\+ G2 5NNa}3 LS6 BRևv1\  y,hY܏,I&o>olt50DZTICvFL%tEXtdate:create2012-09-26T20:26:33+01:00%tEXtdate:modify2012-09-26T20:26:33+01:00]==IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pt.png0000664000175000017500000001027512625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxYy_߾wΌfH͢тڕ(C8I\Sq!TyO*v4nD; $vKz6ѐXlm˲,HϯXsv\WJ C`\x!"7UCq̚\ndO(ϯ: +,(_KJUEqJZb*Z5%J{+VAx*,S08Ɖv;b:Q諯.#v,J`rVX}"XX  0HN6vVPD#$eE.g~uqRKl$Nąș'O_q ,S|ps@Z+0|aEVC P/BH[l+I  SZg5R_Dqf< K?L^zuZ,@=02*hվ 3 RE? ''ʤ4]pqX$EU"6C /R4K_i-<^ h8"\tA˯{M<ƳG.[$f)<)!ZAQТp%X%Dʼn`<> ,CjTq.w%GG#QIVuė v~֛z@ʂNfNi#l XrnWj|% 5ƪZ5De ['a}qmز vexz,S\oO^E0t/ ƕW`Q (<;!SиpLf20y:!ΝC&ڡFA40*D% wo_i4<)ʍ͊P)-/ KzYDXXt>LP  (O)cPmxV-1ZsBC3Ih+ MMВUR=ʪkpҷ8 8l~^$0&U3pP d/,@-Rdu_xű!+a0S'!xڼ}k+ܼgV*$@uҸ\Ǖx C/z?X`5 T ٻaQpE2W Sy[8C0:UV*]f1elL5d-hH*lj.R r pu\`apffaq w*kS0= n!ܘ4,U5 {OC„Ҷ@o!zv,9^ZtW s8-/ nFkH]Z;u{3g`l z{gEO+Ҥ#m?VsJ}L9-Ŋ d;/R.'1T'޷iRN*Í#bv1>/Yx9Ņ~fJ[ސL n#U _jv>04eઁIXנZfɘ$ { W(G^xU#gx؃76́Wů]_CKbeJI o,ji{0c= WCǠ9[]M ;mxfXV'ܷZ!TOh>vU`6hwM#7K}گai\dm66،d\p(7`75ZwOA;2^ Nj=-G}toc_ 6s}si^d w+STolE8 f@ ,)x.l8 ƽס8c_gOZ$ك4X VˀU!gM˘zbkwYGԐ?7B-Y b!"Y-[d앤qk~[p`7@pY~rq S\`[6r] oe ;v)@U嫜TU% LU}7c*d%{G$Ġ W` l:3%a:`ͩu4B:5HT'&B捏Xx6sê[%uRK~*.UeRUB$ʢ,~E. 4}Q:. /܋v/OlŮ6:,m!#9a94˥g,n=ܳ.aձU W2∴_>SI!.`3+~끆B~jF)00TT%ZXai<ۨg zC_>|es7PjX f1W K*zTAq0^{tRxX4*j6j-,&WWRxFk`7*ŁO ۻ` Hj\GJj?| s}QA% CKgA7f j㫝V>VR@k\=ORIe*xZ,9r(L %bk19! a@x}#',YiOx2,t҆r|ܔL0IM-~(0 BfKց4`5W3ٲZ 0\,`IDATxyU՝?{~_FlF@D@eQQFLI%5SX$dIIqȪ 4lEEf~߻t7aP_իz˹~riʾ+ks`-_|&YlVx7 %0׻]0 7BBTJWc>ҧWA)/J),p{?ض# 9z t7A^rWOz}oeWyK)eA9 $2?SNùR q9A #At# O>UKH4-@&5)S)6lR󟾸ۮ??ek0cB])$S!'G0F dT|}{7Hfeհtb3d'gz@ GJ;9BRX#Mb"e7WuY&~MrK1d_?/4tGgI/]C~[DfU2% hZ8H;E4$^G7Wx$!hc Lh}ݸ2ε9ێh F1jl،9o>uEd}b9j QB+? l4߹8͒^dwP hB !m1]װ3J\s~Sm)AAvN;sT  !z'b~J!%2)H[8 a5":KNaO_ބJ@Hޔ&H4ɖ7\^z9[pB JH.춝DDxmXIV}'p̔ GFw8N&t\gIxUAf]R ѫ[߻Kc1qgQZ"•(?:r/mxtBHioaT9\ej.:>itG/[dGQVkgY̦mğXHfmŢǏ 4VrBT&SP#P ~;dwrlV4ݾ{ Y=|N>O6uGԾcro$wa3I4uQ pWI$˵B W6ÇQN޾^8BB+I\GghX@Hzŋd"2NBwBD[t}RIж&,2G}uA&`ʗH,{^B\xr{Ĝ=P0tx} 9w*_ QY7!REmtYe-|>43,IS} (9$u˃YAEٻ7$p֮=Mgm#cQBnŜU^~v[`)ُ FKx-MkRAvbOh-3&lݻ#/CCl&VԍiJ_)EÃğzoxUm9k܀y4]#! <U"nI63D%w?5F!vul/ -Ak?ΩϽBb U\#pJlɄn4E9()$gH*FT[dq+j$8]ދt>Σr.Yr5omt}elA|3XqP)y3 E䞉=.쭷`kQQN9dp3[p[&zyFYqzpl 50l8UğZ W)k i]9g*[k{;z. Mկ\'GzM)p Kא\9E#t3 ֑?*c9&Z^&l {^̜݉Hh܈N˵Sc2ż$\IjFT<:bB?tI?R# p Kh𮷏$2s"zKL6lʨ('? 8F }{`d7 {ۇc[I/J]3(g2D55Ojj].8_9.׵.spG͕dGJ8uqV0Ll4R+ֺ^ӣ^UwEyrgqÑf󾵿Xs'??C%OEQ+9ߓZy;߫4yu(Wƻ~ G !R{mg | awA:0N?*D/GhWsyΠ Oh.`Ww7?&/ X=L~]e B]Y&J“tZmH6p6SEf6[tFuƉ KKO9.#cQ̹JKIyUpÜ> AÜ#_y3nW{Ֆ;u\LD!s+ N`@߽-%6z?!x0wP]xbd6mڵЄ0Nk9؞]}/|5 \s8m[0, _s/X# JO+lچ9g*1m:jʲHoJݣOjHܵ1sGҚL'x 󞬥p-)}tS[R6,C3Yw|/.6B`nef 0 k[]kE7}XJ֙׶b|y1*/@4%/XNpA>٧zrMxrc>ਡ6eϻ9tqQjğXFzVMr2qYw,Pd]E8]'|s2g3%Fo*[ !N=0lCvHm'Ek~}Wp≆m3c\?9mѷ.WcnvvbǂmdMrlSfߕ8S}Ē5_Cs媔j(ȵ鄧tN3$&`[(.*qٰ;3VEmz{"B<4֛{^h"ne8\93&k)K`čEI6-p<7^ g}\cNIHizLB9}A>7]0:95!]~[B[;NDAJmڰvc>SW^zvL#5 BԷ^ރD݁C9| 1?E+ Gʼn? HDZccؚ[ Dc#KLGc>8_ eoDm.1?-{)/H(pP=KA8VT:RRX(woq(,7+=H:f}+0/ѧȾqpRR9W>`LJ rE C9(=.% IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tr.png0000664000175000017500000000717012625666430027163 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxmGy=~7Nl6ii vc;4 RBC5m&"TEBU*O+UU+5?hgh@(@&p(&qqsycwΙ=Z#;3ϼمKR.ߤ.нCanR<=s?˩f<{ցP@;\}cPRJT8=UU"H8dn: E!jfwJl`tģ冃Ws(գTnUhRD﹋{ ̝&$_&rv\j 'N|?I#~jF#5mCg{MkOW"HJ`>ohۿJ }MzH2ii/ J3gIXf;< @r+4Qa1=:m,,}i{ߍ lwgH@܁Q RvgU&Ǒ&U[1 fq@CUwZ8O [o عGrGI;FX3(jJGX q|kvط.,|kZ d÷VՎTЛ7}5k2d$)M;& |pе#}*j{$g>|i$ֳI `dV7^G{wAQS~}w^Y6qb{NMPk{,.fN[3`=Z֓dS¬;{vڂr%ͤ>ÏTs'\J {\sy:hD=bᣐ%kVe7"Z_Gk;^fzw} g:hòO#h]?F2xkkގz"{lv :Ql" @ Tv̸܍N ѪSOcz6_{ 7ה2'ћ7b>k^yXYH78&/Ģ4)'݁MoVɳkA'H9NҜ'|-ZFQg>t0;&çyEy뽰DyS \o;AVbry^o)+V!D>}>F̜z\9v +¶ KK$7MTb9D͒>]G AP= q+[$5O=K-HjeB]њs@5ZV߸#BW$1\_0mgOEI1Vz%Ǟ^eQb*W\|Yx+.4C1=#=|D{̠&Մ)M@C O\8B*GmmQqJn[27OFDwߞJM)ZV uQdoJtM.,g0 H-iF5'ȧiT&v 0ߋ87yŚ6Q3k o{{ģ6L~3z &\7D;wDc7OiJImT83eNHסߙE+g0$NJE+7y ݷy5Kt\ʱPyhI;K[O 2Z0k1)j^~}`Y`S7n\'xûBDNi0}u+<&Bdž. EAr`s[O񲰹9_IT8p MӗlmC !)Zo ,Ρ6|t`h P8:k2!1d?~!u16ƽ&)ʏo.V+R1 Sףy6AI/$5KBp!P<~%tEXtdate:create2012-09-26T20:26:21+01:0056%tEXtdate:modify2012-09-26T20:26:21+01:00h,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ara.png0000664000175000017500000001114112625666430027272 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{G}?3{{wtd! YVd[6?`\T$TJxR EH%PE r*b* 68&Oe[,uzN{s;:vg7_z(o7oQCы$&9'qosz@X7ml07ICĄB!As,Dy9hj~M䕏5{FtSGG`va ̎ƫ͏Gh"RApTQ|*(WJ]w[يRRŹXiB}w)+s PQ(+e1L$A YJHH|:"U`= z|\<)T(Jeڵx!k[g/xh !" ɢ˲ʷt\#k=t͟B 0)4Vc֋r=dX/",%XvL `TX(}P7V7Z 1[O"S89Lc`Swy>}xvq?n^};Ysܲ},_3[ΝliFWc0QCRsC6ћc47̦[f47r-i! Ztot&{.f-M3d,?];uidSVbFyIIv<) ~vfgfOJ*fL89}$a&ɞXKld"?Fs4ŊD.ұK7{vuLN}n?~PZ܇KOttӴKY,1L)û~'㦞wym^}t][6KRrɞJllL܌sV3vw3n_{' 3A!~4},U"bD31k:\n^Iw%=D'J<;Z_A2Ib2Vnֵ Ԛe{_Z{t*E 83;=>Da -{i>oyoE'J qevbFllwD.54^Ÿl+4^ҊO|5MDdQ#FA|b[+,e)NqcM@< s}6DLgv,a gT 5 ޔ>C|j?s gwyzS}U\ I{V/<z'\Q"8o1XNv<;8- Ҷť\y#q#daq=]'#>%b=4;:gKUxS3MbG|6, _#ϰȽ TfA! @SW^o B W)8_KlmoX{GgcHq_;M2Gd'=p: sG顟18wS98熮־/fak[y~&cli+CǾ3C”i~5;uq@xJf7/xZԝ}ȆXP&a|k W\_%t d}w6U<8ZZy`pֽ`M#ޤWd\=& Ԉ a GByTmTJ^آ L_\8zO1 szWʀK`+ihTkn]Y EfYEpQZ;ìk–d\Q#Ǎ2o>D"?iY;2W2/b͌ < yFR4y;GəJ!1e38Q#DGm%JD.s./}*!`籕-Hy^<V>u•_لN [s̾YԋqMT"]fU^R(/yYr**4Fع, fG` 7櫍/rkլ>Sü/O>Ǭi)y;O.PREJJb7 a`SDDSF1z9;5@"z} Iw}_W@]83LNkQ>]~_~Sh*YJynP5%20c XzD9oSJŽQbM*C@FjuUit: IDATx[l%GZs?w{ #yr~ DL+?h8 2{;;-qH!yln[z&-)KOΘ4o}*rX닋|Ʌ˗/׀qj5uw缱tdL$_{:`aoΔpDݕXk1h4?>ȎQ1H#gǧNDPu]lC⠶ pL26P04-`N.VJ}/_azU1ł" QQm-xq9{4FBe8l 1 CA ggg#<30=1&I3N <)@=xxq=vhAXI}C[Isqp]t:M69 Ks@&GHGfWK2lʳ6X|)%㠔Ƙ"*ZnOFi"r6QUG"V!BZl:)Ɉ(cpZDqТǨ%sh `̸k{Ë!TsO=2Z&9D1a?ާJ8쪂4"dc-wz1?~uYn`bRqch!Q7h{]s c]K%R^uS%_BJ &6|=9)Aq;PQ04a I@aVo l5#`ה FA ֺ E qf0:~B`m a5ڮ\l&AWDlkM-eF9%|ߧQB:YTah= ݌0 M:G88`C?tEB9coB݇Fq ݓ8!BA(TvF/SO#Y_̱ HG =k[1H#w"~1` p4En\`mL:3NP@J4-' 6Nco4hh]bФ|gḴS^1.4ˊ z:@z?Ɲ̑QB#5RNPGa}ہF [;z j"gwv dKlammEj*VVnbƾ#2tfHn{@@2vAS }!mX7a:0q~IrgRq%2Zk"T,7nJ{ RH@5 %Qm*[O6V-;.*ua6"{BW 5JBuhq4-X]]B`W8 WN&75ҹb4FQmGp6<ۻ5zƼ|>ǹsz*Zm:g ܹ0}l3d2YtyD,¼Cʝ^\h#{ךu(f^4OVX/Y] ?4z-Cy&'dSKS8j|+Xbߐ8֛{紶j(G4B88m;Շԃ_,'PSw>B3o-~x M_ݯq5JQK'aL /qE4n$y[L\(& qy~X_-E)xzV8X6 }S)׳b* \%51Ϝx$ٙ P_]l!vr@#-""RM?+Q{N0 ,inŤ*b&Vk,bz59s pin\32R2@D:EGTe@pƣWBdq}ce{=(i|;rW*72+k,>۷I'~ĩҩ`7HLˣ^u R.ӧσ=E=C&,,/S[[g+ !1R<) Z-xJvn{}oeRߗ`:o`fܑ|4KRL*LC/WO?xK(XGrYT. v8^ EGRD 2{ΎyP;80W 84@kJz86x Gʵn:L-'.^ŋw I{KΨd3#10az7cZC64h@vH?gi>[(0 ;  PBsnz_Q޽X`'b}|' ~7<7DEFh44=iuժ `*RH7e"$qh+oATUslvayyykaa& Z;q·޵כkٌi#GdR?je+p qCn-[{u?{ #:*۹{hJ"͈Xk Wxן{:1 N%` SsbxԸs$)I.cFkݸuZو>L@ȴ NPs0)lނWfvt]-1۝>E2ψ$6]Ժ4MCs hBkzDK[.HOTڣPGnR?c&6/a=ڵZ%tEXtdate:create2012-09-26T20:26:16+01:00[%tEXtdate:modify2012-09-26T20:26:16+01:00@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tn.png0000664000175000017500000000722512625666430027160 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx{y3{<66\lԑI )Mh bb' ڂ** (j}RӤU<" R$$M {mdO7,t-+\d%@ y֊.R^<-MN|\ݴP rfQP̈ժx~8J;DwsU**g Y s]*o]ݴ9qrdؤ_ ߳O;To^.2.)}SeK튾ԮL4cOx?8 z Beij0isDN4nby_%%Զmwh~~w@@Q%vj70sr+ ̔[aoKApsHˎ\:Go95s}j:lPhv\)@^R jNe;*JCegIzRxO,Qq]_U{uG{>y3D,1Ҧo-0e:FRк8R=JQoHӭ6;_8_@4긗nY||C'wӝk\u:m-7iĔ5]Jᬙ?GkbJk "82H\&*C$HZpa}P8&b&CA\|"\]'m @Qfsv&t}O}gxO<8?&Z gzR6M.$r~`D\/bUN4\3A yÓ=zyrƾk2e8"v$kIFfD+X0>h5F"AwCc,|Z#6@ƭ7R߷j,rѫK?0nq*ؾpXVjh +W0}iw'/zf~r <\7rc\!,}X`S,ƺItN"dU *#SקAJsm>Aey˖ `VzL"ġb&\xi=-E.ĸ%^4\{@Gvs X;1AŰlyM?؍3b-m8{Lᬽmx @/кC F>~[׽lh6R]X;g/&vW^x(wmhS ǤI:>(~V3!>HA{,u\uo-|b|Ify|C?+pKp2tlhk,ZL{?z¼'@!E pDt̾*l!ݴ7 .*.A4LT'N9CuIo3ÿNLeaiC)~JVkyqǹjx L1؝K{ENm{Jd́96Zq39X<',b;+cK%94@s3}_N%ȥ8Lଙ@/S#?d=S.1^24Z$m㊧_H[M6 y:u5[PhTA +L}T~T*' J^7bvaᆂ݉kW26f=rWrBMz\bLA ;2:Ye Q@HGsT部aO 6 9[q=WRZ48~QcaTD?'z/ZoFk}޹4Pn/| x,];[͇iL|lA\`q57/ j@_l. +!v 4B+k0Jk3 b|6{p8[chh V|t`h fDg&<k1!V07:(Xa}lq ,I0ؙj}Qy3+wE {{6:¯%Os%tEXtdate:create2012-09-26T20:26:04+01:00(%tEXtdate:modify2012-09-26T20:26:04+01:00uPIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/vu.png0000664000175000017500000001027112625666430027164 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{T?w{ 300%(ID,b*c2c)J$nHeS1YZe,Kj6K4&.$(AYD5{s=Yԯw9;w/ ^{6"sM8g x|h^> 4ӧh9fy"%)A-vY[IoTA5iR}J*m)e" mm w$d1∠Ah"󙞻n`09BMU@?U}Yo)r- (!\1PWxcaiCPt퀫1g֛\-Uʪnm@{b&K&|LN$6?YXF=п0bS > uFDqÃiLXiaW8VU\UV-C Lb:c.q#"BS?v6 3WdXCX,W?PKԠWG(QiY* '`pmNm*p1f1F1#uL F &O..42[ؚj08PLz_*ykOaJ@8}w1ƶ'Ko`!C  kv 8*(B߈ycԠ")9{_$, ܒV(Lkt޽ֽ=W'̞%J Kw  uJa $=V⋈8BGizHt1C8xI8P/B8Z}DPd&+00.wc`j#kk:aHalAֵ}D("V |H+d+nڛ ksbpUСW"LP+0X080=o]=10΍;ჄVذ~s`palw6h7e@n\ XHW535ap9+йC+anhlK_/OQjDVeaPckhL]XD vJ ۶ʕv9r$ sfZX 8(e>4H1y-_*,> +/ v(c'`:x=4ȺܦM(U` 1!P' L@E"R[ 7$\zR^n㺰k"_=<nk;#'̜ =d2W 3+]!,!\LqeB#F ضt)_o5wa3vش |*w(exlw;`iNX-I!-OEjߛޗte$P|MWct.x )~`UW\,8:_? ˗[@*[*˖w]?*f*? wac!>ȍ{t2s4VUǺ`ׅx0v2a|,L_ttXXkªUkp-[6qdAls2ظQF6eLexx9gO񓰤?>̾j6gB O? / GX ۷+55ԩpJ*e3_ؕUm&QnnjkcO(O>K28%NP@e9xͨ N,xIdKOdW-( 7l?KC\=,^ kCP}r(.V.*zT{}*C1z6x's&.=KzZRiEPN`x![ < eYS<[Yk|5 m'UZ1ꩺ l׍T2 gMu<8aUSVi[mG mmD Be%|k`]utҥ/R6oOP=k)\zV YejLHapaE.--%K:-{IXx%…0{ ^z`] Xq \P:Qa  C *3(,ySpb#[g_kύpp뭶/>:XLeC;%۩:W t00̤: (aAs];j'N(/4oL?qe'<=i/4 .TJNNyq8RBu 9Dy"d|2PKd1zb]& (J o ֥zK5 1)%Af.^!bZ7|s]()+}.f&xSʩ2/)uP/ Zp]kyc`WKmc7L2 o};5xM=}遗r~Kު~B_^Aa'TV*7dWGۏ{#_+nUc4R5 Sd"u5nF.L $ of̀E`\Him%Kc'^>uE>BqAJ$_u[d VU ەj{g’Wxx%F$[P(0)Č'E5t܅ O[Ifxy[rY Qwy-E\rbUiQW/eLLME`xXVZ**l]FyO2F^2{B=bg_{iVV׿weq`^xi;=iJvc=5kH%R^W!Hj(ΤXj)sNԶǎ%os"UUf <00` _UOY]g杣jFos1UO^&a"+LTUqE> /-㜛먚4SlHg(T_}nKM؈GX uE>t';8TMbج*mwTwVĵ%N?J(SILh@Y 1)5uUS^K7|c 8HF9a/@ PbLA}U50y5S}'rb{!(m6Hu?![aH :P#5Z N_Y9I$!kJ9m6a$uD+% hgsB7r$f{lRl7`,%tEXtdate:create2012-09-26T20:26:40+01:00-%tEXtdate:modify2012-09-26T20:26:40+01:00p.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cw.png0000664000175000017500000000632112625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[]Egs{JimVB "DS 11&y7ML0䋄Q@$VM X-@mo齽_=agvgfg|tIi7Xj7чTw=$xFQ&x4h( h mwͩI-"""@] k^޳nkǜ>cuˤ-CǓ51'  7ݶ-ܴqќ|6B )fs^|q;)siApX3]uVdlNB|kci@MpY[ad.4 |?=TSH d$%caݵ_Fa.Vai,:))o`uO,=v,R&q2.;X4 .w۶609!l #7 fl%zv-;W+)V/DcyEL2=3 c#]lƖ.&!fj% (p8z.pN3Ɉ"`4&m(K = H#oa_F}MKAAV|x0 #eDcp4enz ٧\AƌR`6Hɪq|THƱ@; vi/l1AUv쾊Y}u(QXH# N*wMhM l6A"(1XuC%}yy>Nz6vl5k "WW01h N@qd p =ʓeC="@%MT@BBOMh+:s-1kW?z;ejIYg>\pɪ%èỳ?@f'ZR|SCiLs:$LOx$1_L:W8lZ ~frTG>JWFg 78+9?md".zT 0Ys7w2&P Akb)帐5Ы|T}c@ƀ2d-{=S9ėXLዲ}|VQ*Wݘ1 #Ph IDATx[G陽eY;Kl FIE Ev!?wG'C"Q 1` 1_tw᡻g{z.Y GjMOwu]>T*'ܓ_QJ{7H \#<,94\J@('|rѣ_x>y^?QD$Mg-_Yq9Kyj_yo|}4*ED$ /^cǎ?$+/~޽/Jk=Z6:Mta _PlQ }#mnۃ8t^{G'NX db@ ˻|kiiI-,,Pp[`es]?cD/ĺVPZa<Ÿa64%f6A8}K/O<(ngJ̵~ȑǗ/..9|/ `b 4C8A^6 z-5Q71IqÇ<~Tr?ݻw?h4>h4fpT6x=Ha T|{VD,eXJSm`3,Ybav+z"3}4ge⿜ PsݹԾ IcIƤǀFl=hEoX1ܢLҔjo"S4 D!rGq:OU7NEفj}pֈN(X85ˣ}{Lv9!r8](c:00BiN1uZqKk1$IH񯗶Zc t=IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bg.png0000664000175000017500000000461412625666430027126 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxoIǿ=q8Y8!KNHp JQ"Ƒܣܑ"!rr-+ā’E`]ljgzU]3cWoԚ>իiLL ӵ'Axh9DZޛ,eږ'@@yՕ[n^7pu"3c%KZ_2L&>ǝ;w*ĔVP_~[7nΝ{{0(ԡ 3Ckxۿw 2X&(/^ٕ+W~Akkkzh8NhNY?y&ݿUUZxU@}?Z__c4,7"R Tmmm}pڵ޽{c$rpcc+++o C}E}sv03@p8|ӍXj( 6QJ( e a+Q0oyPUU @\K(օ,Ō ET&OeDk`X9>d##.ֲL]8C7lW}s'9ض6}[-caW>{lF5`g/ EI YST)#u1ԤAкVGC}z1kLOwg$R(@ P()"7.#9a0@!ؕ>ɖYo R F44rcm4݈4:º>nr\]֡>y.F%G\HG42rPC>,oeg̘2_iE /%KC}IpRp:VcD[/ _$y>LDʔu/k̼(@9Ivd~o!A_8,AQ!{!W'@y QH3A30sKVҪ0sLBo ^^x%k3djdwV5a-4֌qK}qYTU۩[!=P3c `;_ 1@,ld\C}̓)eSf3Sޫ7Tx?I_Rm30 q9eF|?P$3< q2iDZK߁T޾f<T/\ 4Taנ*04'#d^8L0_O:7x_.h=*x~{ W^UGx012w{廊1XD3$ jfGv< b ``Bd!|[At8| +yِaS0? N'H ז /- `_!KNhX76ٺy˝ ™'!|TBȀ%tEXtdate:create2012-09-26T20:26:06+01:00b%tEXtdate:modify2012-09-26T20:26:06+01:00yIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/in.png0000664000175000017500000001040112625666430027133 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>?IDATxYpyPHEY)ّʑdQCʮCDI^C**yH%e;v,dJ$RIE'H {yY8~jf{ B/ϩ r-*yPi,Eh#Ͳ]!v=(TayA! ¾\جq@),(f @J;x(ͣ"_JiRN&ȳ'g/2n @G<6M45GQyP*noҴk&&M)d PRG | 9 Shj=@@7(B (MԃUlTq3ZJ;z$ +y&s)]ZqsQC)V1ߓES(BgH9}%uʒB5 Xa{tO @0iӃ@Pu !-*,, UsM&|K,Nr)vy=ĉv>XBE#LNzOi$`c4z%t꫅1xiX WΙVlMr9A"e8cw25F ݄/9NF>I00ɑ8tc*S&ڊW~SQ_rQk'ĭ䡃=qK;) UȽH)LGP-<3lj ޥ2Ċ-M׮p5ϫsMD !ttz;Lw[godn)K6o/]jaj.~:̯GcU]9Rw~KHWCUh[Uli)?{r; 0+qZ0-|n/eL^B.iG<3;)ex*3-|^G&cx_O\@}]_Ӄ?u57,~)Mwj:ikDC^/- x8;H[DZ'{UF'ē:~ Ӣ%ص8'7nl$(͑lMLϧyT7,N.21j,h G]eg_sL=^3Rl eeگE[Yc*͛<%TE$9cf1CgsYy "3QAЯqjə'5hxm|zq^ā]hz5›#//q,~F8衳9Ggݷvܫ&Si~ FkS|nӤ܆ ~x7FM{TbZ[ k:E$/əmMlξNfdb~u q@3[4$DD^9:Ѕ$?ISKwGKJΎ.]W\)%A_W{:NcKt9lDJh O/-`XwrwYNLҘ)u@Ô$3:le̲3%!ƒ*E6oUDB^w_'<%K Pi -e{w;iɚLĒ}reY J5>$7MH[!zN^X2m l„ף9b tb|*e&JL!!/EUL MU0M3\7BW Whƣ]TU0᠗$M}t2qftFBƙOcM[r%/k{#[4G|wl"{wtЁ-=6I2Ӌ |zq`Obi ̦M6Rb|x*BfZ?/=7^'PP4P0arԪ mE^N~05JaJ#2H [;LL'QUޝ-O%oFxIR1].?Xp&x&G;JːL䖘N,Uj^ 4۽EJ[%(ΪMnyi)O~q1"^A8!{U,Ki mM~/.ǯ$SQ{O),UN1WViKi䓒d6${w9NTy`,离KsX{ 0{Az{{.yx5gfM͜87/^F𷟢wk"w0.$a+dRˑ)N'Wج͊18BRspָ{.Pve-wrb׎]&a UL&x  (ZK*u ;n_w# gD&c JB2QQ5Hf͍B9AStx'z|tEbh  4 kƝUr%7ZJ!nh_[oJ moG1gTMm:WXy W J8g(߾+~* (g\>n7k xqE̶, N7.W/wT}EhBpet nA2+Su֯!:Zsh{;-Wt.XdþJ4}*HXW&HÎö.gYZi\hԍnQ aIҿR2Fa:)J*\dD{mLߪv% #e^1}JsWkfB$EHS>JZ=@ƽK?QkV 2$f) fY&`$cq{c<$-K(7yJ+,̸"S |pҒ /Y ~oq0PFL̨©K ,.}C@;УjoaQ%r(J.7&fMӜ32sBtb ZFX@MRXf9^U>6,N $]zq"<ā͡fB蚂)|qwĢ(l].Yɓ Q)}'VVF[Vu-0D* <iBk%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/vc.png0000664000175000017500000000701312625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IIDATx]Uk{ϽwĩصCJ TJETTH'xHDhQUBjPRҦ)jĎ#sm߯s?{>3s{ٳ?{3ƒ <GiA${$A\2CP@sA0 zGO.O?aq 3306+ 륎?|_#o\fqZ"Am"8yHʋ9m;sYmc kR}[=?`^Β$ #{iƹ7?y6+pUkKM_r`*]K9x4vÕ_< s& 0[ܷN̟?oKKK}<@+!&V#~qA>dX:: RXC9KMq;'(\`zomOW?FZ OXH|? 09^%}I$/ 3vhk鰢vW8Ky=I_FWb%ZH7/04,3t -f$ {I1u]Y:EZxN:mg"Cle"S0!LF6gmƣ.}`enoba NRVC !WI -h.g(t7Q Q^G%`  !Lw͕2hd#u^=45aaݕg*٪lwe ܵRs|&6#C^[U{Ϭ4hRMtʳAiW*uHsx Pg+o@d i7i3ƸgşqM2E0-_|[b,ǡygbϕS9K}uHTxn4SN7&4J~, GΈA,=7>|C;t<`iCNߝYaŧ2b4%:'9<.FN|+.zɼX̧/=OGyuZ:\(-b:uC5Rjx mhHɑ&pnnϟ~|n |G3p\Y?b-Kck6xe't`;); r5Bp8 Q8诟߼S|5a8 ~Qc蠟HH/ /ʩzdy7&Ș9?we)\%&#RszGkbX $8^-y{ťٜN7 XW:2Sq`!qubIEǥŊ.Sz}cP"GiPv2C'"z]N c5vmSx2 _]Tn|3b:\llriY\N΋mi@3"6|/Ou_OzXtWܟ7!u+H`K?ZÎ_8Vm:C{g*d;)Ё`\.3X'TWLv.LʠRIGG?qPdN*;ve:$*0H:2IG2Ms~'7o^^J$0vqLWcw2Cq4fg#M ~1 )8a,ދW&:L􊧯Dwd['4 NplAp|_7癭V(HZ,Fԅ [:J:|nrB. vU>őV!P;H19E&ij%- *vɪקSS KQLw ^7YLHD\xXeZkrrմ-RW;4 8qHIK[spYIUW;vP8}[k@ ec#hcf0$m1hsmfm, 6GʆR64<oN 7f4tlmPmCh.5@Cd[_(,qvU6Sqٛڬ8=4dln:ߞ N^vzy=up3/F-Ъnp-Mcrܡ /z#$ wܹkH KX n}&RC`5{=}a2-6V|veɟ.&>hiʥ2݆x{6ceFދ^* la72p8}<]y 9p`  fO^}Ѫvs{Y] ’BH~% rHMVo E ;Bpl sF ܋#}1~px]7Cۢz$N⓯48ߏ~(faY`X5Ax/njIׅ-%tEXtdate:create2012-09-26T20:26:21+01:0056%tEXtdate:modify2012-09-26T20:26:21+01:00h,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/az.png0000664000175000017500000001006012625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>nIDATx_}?3{ϵ)I0lBU D$8$(y@O%Ji+US,UARP6qD\ *`J1`0`lc|{ow~}{v96\FZof;3 _/7ϨdJ;hL9Ē幀p}Eݦ\@)PTX3BZ%1QbYFz" "1:x{KϼOi灘@uRn:-*zVA9t6;'b`bM{qᒙg;L洭w?RR4 J=Ge *π́"`"Pc;N2:T[O^FWqv|VU[mU] 0mQJ[6wLⱪ R:I/FUvqjws}rD!bfJTڡZJ,|HIJ5 Z>=YFJRUPcoq%`הv4Zc9-m8Y[Սvs"AQȂ砇FbB~WhDF!`@V: &e_z9Ycw>Tq/iQHZfz*x v}ܠJM9dz\q@zq+ln+eӱɳIIib.tw),&2"U<@^̸ʩ-CT|}_W}kb"2'FF¹zv>yaRD~2M-X`U F+.7=NͷyR\ u]:>X] ۗF+2klU\hra_~8áZ਄Rl~@e@uZIWj6?C|5zhڵ}׸k;JRH.糧_6"a_^% QA C Z!cu.g,6bk@)D)L7;һR9p?y7Ϸ*(ґJAφR!;>>!ΑC!\Ak{EhQ!r" xy\޷ O=WF9N =[d nnGqbͪaxaYH cߧnR_Wϟ+p'ꞏOi6QZGkK ‰(>zy0.C˔ׯyK T ^ D:00j J[4v2[|K 862?7=g ǎqᩧ0%{ dE%a|+x p6mbFA(</Q:Ui'{4nz4pXϹ)n*6FK?9ċ3m ONRG|۷SN6oF:֭T6n,cs Y03%VE1x# .u:T֯gSE^yΝ8+VP۵ ۶q'DMPvV8#2v#*EWrHch˖wsi4MX,= D={X裬޳& ܱ1.NN}v$".y"Q6ʆ 8##)"', Xt޿f RÛa淿EfKG"]1\He9$^ :=WB:8pǩNNbvD$q^?_Z*,0ej.Um:j4Im+Kq?7Ja8Ygs1䓴NJqxG"h߷ 't9˅$ 1,&;ݽmۺZ,=C,=JܹB.X+%Yz=I;y4@R$J׫,8,m{F#b %(HCTNY inY^zW9*@1DW`h_jQd|&s֡Wҧ@MҒUdN>RY'2Rh~ZhH."`f,Jͧ'} =9L$i# W@LpTTI񫴰 _^zһ tFhNчn>iɰ3J_5 }3Je̘(ʚ]WLJA}qoo<=Pe8oSNFD}P$K+PlC2۸4YBdS N*Fok Hy`\ [φ֝ A-5"@#`Xq2"E@{U~+eoD}>!uZ$?[l^c]ײR$uuq R jH)b%tEXtdate:create2012-09-26T20:26:23+01:00r%tEXtdate:modify2012-09-26T20:26:23+01:00=IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/jm.png0000664000175000017500000001046212625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>pIDATxiu{g4̢F4 Y$9B!` p`|K8cCrB,Cls`0fA־ HUn>TUwuuuOs᎞U_u'}b_cd":([D֙`}"j ԌIVtwĆ"`#:', FH "ΑW)TG10AQTU}N9Gsu>Z\Kt&/^{ũFY02Ӑ˲PBN{E=B@ ,jU3\ա\ܢ4NU1Ɛ/@aߖo;B b*0C.;߾1r94Q"c,~GX;ppᲪV h efʬ)8=3e񷟵Wk_ϭ0L({Vޑ?ooimN蒲 FՌŹS}⃤rEwyd, >1\Ԁ,RxXG.c?~N{V~h}15mICE.]FX.z.W]

e"3& M^XZnFaCb+XX!@Rr~/?E3Bwgm-҂,2Ƙ@>Xq3R5\XUv0jp$Z8)#,y`(8&POY +|ܒ$%]H6!Xw~%=X' k7 o:.ha~3A0l4]iXp<\3Z"Y6ZpH3/?l^j92a Lqi K/~\L0--pz2Np;Uw(sPnO$|-OY"e>ŲËz_|V+j [S 1WtNlP7+}HMD]`rV<`z,[-*@.Yn7w'cMUSՆ?nVZmeo<`lnk;#ڧxb)Ҫc`5L2K`o/.0%*D(g4)u9_ vŭkVxP|dE./ XV3a+:_KqKPNjPi~=N0&"Y/Ahbv2bEp ?σ^!z%\vE,_JRx"`!CbZ]굚("U=0 nhog]p:2Y ] nu)ф, H=}qޮ-z%myz^<@oc0i|x:(eˉףXX|?奥ժ$zTRd- 0Yဋ^s-p¹` kqg_e|-|A? f֐[ $‰򋢬@3@܄S&"oyAAeÖ,ޘh ^xC yJA)!4XlsLg)ab-;kL?h\O7n"f{XvC@oiVv/8Az!^Ut0gzDQYҔEQ.N~bd2\ê'5@6EKBWg\dEpZ*'g }*O1U=kVEd#!UJί W@{KĻJ;tU6aBVcZJ3?e0aUޭPQHQf7)ir(\rx\鍐5ymCΜ 3&*k7=yc@UUX1c W^v H [ܼQ 9%a]eaT: n8L=\0%}dC{a5Kש|kpӗo%~-v ?+T^kVl_JZYa Ξ /K $k4rZi\orU@v7ڌ&%[Qp49IY./jycdV,W}~p#LeUnܢ<-XAK'ӲKZk>'9 +6I-*S  0p3A׃NZXp%iDIC[g@ 1Ax5Ĭ6X Y5JI8$LH8.煒m` (Y;i  <^~mFdĭ} $=g:̚{b-Iu{Q_WZS'Jomz dVQIr^ $=F$`‰anjA<2=0wݝ=_FHzpT~nsd*ʺw?$8AxeƦBIW:…s,5_.^sς|H9d7ww·Ipv9|?TY!Xs+OUm=Ra-0d#77â,҄uJB4~&Kzt*yY&#T&Vx&vph?~t1RV4f^#< o DB#>9PAMmR0KFF(̅fxo-Es#_?sf iMLh>s2G[ ĶѪz<=}pģW%}!hȒoMGW:.x y;4$e*u׍Khxe)J A9xd><`yP6u0|? ~,k6m 3 +.]ݯ_=Kdžqz@wʶOxjHk-d4Pa@Q1h~\Ag1[\yf`+;BLئ9|. )֏ =`o1'ID:P| 6D~-X(eߊ$R Ek|'G+XCSuk-O ~~i_ -Ti4I׏iOXvM%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/om.png0000664000175000017500000000602512625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> SIDATx[\U3sz)QJ-D4ƒ7%D `|3Q_5&bj|"@"WxmZ)sff_þڗs!售}ffof6\,r|pz^EiS-B3VI+p?dbm첫6"¹ar3O!sVB! b"Mh}BsW,fvoxn߫B N\ǪU_w,߸qUkٳ84gtA~ 5gcp.m۠+[1t]+O\|?~2D@ 4VV 5e"}e }gz֬y׉$\L]`uw]wwxa_^Ӟ{먩f%Ǐ;n.)t{o6mGq]P:!<܆!Zk 7l;v<#L+)ִ\zeSiG> zj mx֭x}Eun'>.JR}uhyV%׬[< FzD874'<~f-j _ ݻ7,n'焝t]WG,Z\bt,]gF(P"+h^t_+P΢W#u}+y`s2YRk ð46Bt(9'@]"3x j$ڻ8W\Jc`)/Dc+JVPߺ59ԓvM۾}! ?Zc:y3ͻZhwo`R`U7Av(*։REhcʳaUb HdrZ/TgEHUKZ(rRQfn -0 ~7OV^`gd P** "y+C3̂K\E퀮xӹfM*9+72R,BP^>Ym*GX %[-1T6b٩E+YCk2P6DB2{7*n0yUs`%V *φW+e,&h1r.V9`VEd6W\׀#y`>P)ŢZBq F-(]UDߘri#0N#wJ\:F-ڔݛ#ڮ_7vSl jttY]N@(H'BNF*;ɍhdHWBWޞz+*U@$zyGMTDI8Y2J-sԋK kiV]ꭣrflh9ᒀ#t.8ibBd7T)YIP(Bp\8xMxi`ԍ~syUf~Y~(8S,*_ƔWeCӵ3׫w93K\ TRQGAV QU\5^~Tv`҉jrkPƑ [} fX AH)J"`$9M09\'97yBEbekY BBQ SH/T.(I OLqZv.f[ݨj'1@@ !H8&$9M;4Hg +޶$ya<01k6F"cºVJ  )Zya| Jh\eo3S^Bè8H QF*ȫ,@נN3X6`jt ܲAMMI102ob?WV?F73"Eh }8ӏ:Xu[n\Z@81p*i;Ϝ{$zVr&XltKnبMdD̻ph)\<#4JFgW(::υ-.IU2O,#KCX8KzISp*6!6S ;]@%`6PD$][]!DXǺZ֥T%%8֡p/-FQ<Ҡ^E^,E?Êa%tEXtdate:create2012-09-26T20:26:36+01:008&%tEXtdate:modify2012-09-26T20:26:36+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tt.png0000664000175000017500000001071012625666430027157 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxoG?UHI~Ɗ"+8%%qa3@{Yu{[`yg0X̟0{Ye 6qdԃe="˶GdɢE~M7ݔ,;CحOuCPHqLұO㼿bC3Lk'P?+_u47?)tlR ._f]{Y}n_y/V-ġJ*ϐpU?3 Dkpw/|ryy/@ PsKR*p`0g}/KӟP%V唳JWsOG\ 22D4t|CsABa䁂hP? 𶔍t$mm\J$cvvYt]G4OuB@6Eu$g;{}xM#iE24x@J- u.  F4Jaj0j=7̂g(0pf p#!~v^J67S)BǙb~~4u àX,dPJGCg'0큮L@M+ot r6à81YyW zA=NPRYi>`#:# AbSYkkhSS ;;b||Ͳ;Jr+Q48s ]8b&S <\T7t?:Xva愣B*4D:;~:cccr9_WEFGG9| b_joCZm`-D?;T2RbN&L:fkk3U!t:MSS-f>zDA0TWBJ%y$NU^o%ċ/r=>D:\R8RkG"R/.?zDx`r tmάrT`t:ͥKcTC8$ P܍9X .i'@8E$Wimm%J V>Tv >7W ģqv{#.2r=x Ve׺یeuw{{{n\Jh 7߼(K}nyP)oC#?z!>[\Dxw=!:뻻s-%H?y;^i+ 0^` }!076'|qa";M*ի|N$=B_X8v2U;+T %¯@ 飺63N&Ÿ[pppP\脘NikkC 7O?+S0lmm1::J{{;WQ½0+!TVP1AdW)5 QV`y$BqI! Dz{A4_XŋquޏF h%~jGe- [D`dѐL.Vb.cll.ZQBLsuzH˵펴P}@g7XJ=5 IzBJuJ}ϟ366F$r4J(8>B|˭m+4SjBtW^B4&&A:ư~JB8hXPw7ts}@r <@!jN΢FG0% FGGJ\__'{==uuQx۝W7ȅ= [,RHNqd=ǫ A !X[[#Ksgbbӝ$DRwŻw1s w0A}}JRK~{Ȧ&N' d2WVVfn_ummQ[[kX.({KI$d}}IϾ{켴ăH p) x ;wP|ka͜chhU !X\\dzzAI& Rssg|-PRW6Oe>ǏJۣ0|<7n7MMupPDwD.lGa {qC/%nmQ{4.cpp9kѬ2??B <ïÊ~4(N&ؙ[~#3Br9 QMS4 ,,,Btx1?y7OF8'@_u81v$ lǏrY\SD}=;wXvgJӯ Փlp:@׌J WiRbllPL\DH&d2=_R4ܼu nA0X%kќ症r'u(XNWBA>>)1=NR$B?<}<Øbuuos-R{9r_<+ O`; : PB>]UaUܩzBmil?͑iVVVj>`d6eoۘ"0;fO0k0&` QZeN`>fJJU &bĢQRkkk)uԘfY__ƭ[}coQ Ĥ4lg^`7D>8e ^!VV8KK4Jwg'Tw^ '''y͛u -`b])4 pGR^ 4#] ñ4!Ya6rw7R)6@s7np x!0pNʦ6qZzAmW6!YO$KK@${}!T|>QņdP233KR[)p$sIK BJ}TJaݶv>ewnwxqvww1͒ڢ=EDl⎮'i,(}!%)_f pqO-)Y=/-croEùs?w94M+gJ)fffomqI=mlܻxoE4x9%' Xm\es,--U̐=B* iOe`?8 pѲ  qi)9 owA (J IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:16+01:00[%tEXtdate:modify2012-09-26T20:26:16+01:00@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bf.png0000664000175000017500000000651012625666430027122 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[%Gy93ًwvmll.66fW A A9J@C$$<|ИR"ව;Fvf5ayZ媊*So=,;B2 ADIx~j՘11?[4e1H&LV9 +YNʋduQ)SsP#O}#~Dĕ.yh{G" &d2), ,P"@{].b╆bmyN- 0aQduUS 78sXNDLP*flLdEkF/Iy%AT<'0V:WMaCCděT֕I㺨B2a*eMTq\C`ԐtM"-*GIGyf"", d3&yq,1q &c"Q3F ;$w #Rk+ϝ>]:ӂwșOd [I]6u:]=)=4* &Rt@ǐ| ېE([Il[mLWfyzX һ }# r{ ,6 ^`A\ld;)i!F|p'yuo 1`ظ咿.v.UFG`U7*GVE:NݚLt (Qku@Fj.+"o^g#uecBa%͓U#| FG Re@nacbV`c18:$^1_&{0sʛechϽhERƓlnCxUޣhШX#UJs+z:ەg slQfi7KIR9 W.~etX5VUF1/YTc Qu/Fi!9L. l${a1ڨiP  rz*?)ywfX6p,l^ 0y^;X~88jT8E:fwuaJ+Wbל ;74V1(*FDwK7rY \}a*ayDBX+X W>J"nL,c*CUU[8Ж;?]s#XJV+' )+T5!<%XJl4y]oJ~p+H"s S`}Sx^>8nea. &c \h^RU I7@mؕeixDz[)62^s5 zQ=#w^i}Z _իJt5E+ 42̍F=Oz& հIŎ8ȁ.'E,"uOupy W/-PY^ c]kmڊվpVmEQV $ot ܹep:ʙ+`'Zi%«c ̺-P;pP=AMڻ ,MX17jR!5sc=i6z2(®-q }č1Z +oՀc`q>'ceY~~)V3!8 Ȱrn3gi e7z3ڠYV-{Vïl^\9zk`krfkCnjFtG7[Dv羾b1 oALl٦"?VM Χlg8օVW Vo?Ul{F ;Vv2Lo= mлU8ѳtٶ6uwnN*ᵳbn5NasqV_/^-$?O1 = OJ:mtBYm,p^p>ZsOL-O\a*{vQ= k忞˛Z<7N\P"|oRdexprb+k!8އPOjS۩_)g 2 Ϭ'߮+B ‰A@hQ jӥǞIg ;lZ6+E1J޵/:OQƸ'>8(+޵ $FP[D]WKXj;a}k\pN{zp ~x^ɵћ@Q Oexh/|,$Z6 3nZovgmW%jjdFVy_0-·__\.&se^l2X`.rc)ZZ4+ j0$Bq_N]u{Ǯny5O¡=g:6%C;~*@UPTCt̥v'Ōt} \ GZq.y38fykjӹ|-nդMx3D^ۤ=z\{qN.G A3'.`⎛Ro[՝;*Y!+sмK1S)"NEi[V]fֳ`.OPP[hglɒ)zB3?Rklk!`ǐ]+88tMxs }~{!H#mrGXFoN_e"?fX$_Z,K$_I\oO lc"?ƾ.Iܷr`s_ n$>lnnؗ^<_^xlc`A\uyLo[ @s**pXsn0gCt/;C";B7+5ǡ5t0)n'Ĕ0u,X7Iu+"$0WV>AnY^iV魖c{;g8%tEXtdate:create2012-09-26T20:26:07+01:00q%tEXtdate:modify2012-09-26T20:26:07+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bi.png0000664000175000017500000001103312625666430027121 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>YIDATx{tTս?LQuyZB+U\.ϥjuQBVX+.j;B$L&3c$3Ize5s;o>OST'$r#pqݿ$@sʜxyF@5sfM?F"yx PJ)Tr*wW͗O>\DQFΛlݾ;ZvѢEcDQт¿8 e`e1<%L}AgXz,p:=/a! 2L-0ÖXo*-+S`o}= /6lNxRT醦dҍJd}2o΢E*+8~#Gڝ;W0jY@O^da_5 W_ TQ0HAAQSCu5nijȁ ƒLr"⇈h^V8l0b1 #++\}կo? +g_\V:~|>ib(a X~+طܺw苳N9q|3̟EEK4z~ J+FL8 @/cvi4M bX́իW]Rt1FT+Qv6  7©f00Mc8?ЅFt ,gFoSz3?y`Jֶ9bO)0 Vhw3G̙k=PXؤ̓:#J+"jgॗ੧N70dd!SN0>&M7UWAa!n@1sL.cGu zьF7Bp pE:d.u6>ۆXƌq Cmw/OV{7վ} gv|0w.ڭ-?#м<?Nl]`.ؾ!ˬ>O7YgtdLeh5D< ] Y{Rkoqw\<D?Ou駵['Gj=9G3&S#Gt2|D}:+:tK7󳦟#ŊY_9͜nN0SJիzj527RO{ ^t6r~'· wShc Jk$KAk,|f,t){+zǎ<ƾ}.:;N И`gD(["[S矰9w:F6 fǶ6 ̙ܬgB![n{XK& < 돯;OiGikz_a)ʘtl} wp س6Ltl۳9X,ed YL8'` ?:#j:j6縏i$S']J~`047nfKdˀ\֥fd&O^{d^<,e{Kwo+e:twӰm|.![PRMMG a']ĺz L*{ evoܵ%KX\2Ok-dڅԮOq GT|}h5?<ͱf ek_&2>$eHCM2 ek'G~BMG e0Un".gߋ[L3I׎ʖllGLeAĶy ]+ĺXAc\Pxj0 7b UlފGy2mn4rHgej`^rp  #=#O$c{s9dI7LRLWFFxFd~'Wy)gw:+^f?'<`E;U tN@RۦlLƹ\y@KA &$?fov6Dff8Y۔rt 3ft]ARCkCc;>w3~ŮE{i*JOmh;MVcBia*Ngcj޿]r2Nl 2D]>Du:WGfem#AxcCt~.I`ռuM%>Rbt**r?s0qH#[׵Ģ(̸pw)gp &WK;amJPg4N LIb #%V+m D+~V,VY#oNGGV{H0mٲ V q1Dw;R ?1G-88:ލ%IU1]MPBO.;ro<0Ud ;ad^:cm\A{(~:#}9w&;9޸]FNip,^e2ss)4 ӳv=#12)";jڂjoO$Juq!sG9xOąS謸pEQ8u[~RE}y(eєo0,ޑ7>W;SzSȘje >PJa`":eمy!n/ oU1|>=LqEFU}[e˘DCXcRVx( DN4wH<,vN֒[YRO+%( y @0?4yIV>ZL܌C "zxԗ!wR#Wݩk{Pɀ@%}쳱|\ |1~ݹx< agw@գb|߆]wI$˲q_Rmij|̇׫H~ȨQ"Ӧ̚%2sԩ"h D>y7ܱqIJ,#6g=vʬf,ca1Ejka2{X_j["K4[Ns"ᨧGj45eNMmnEd~UO.,Ǣ(aŴ d@C lƲ-lƎgfL5 8zTGße{XU4u.βep]e"8ZgȲ,,4MD$@ԌGMt`N2l ER:uj׿j޾$-8}<+mňbDQR$U[arё9{"\Ss e6$7ďh4J4% ĒtuvD"ٙxP--zC}}ƺe[O>U[n b$μH$B$!KggGJLt# rLظj|a 64]r%oK{uB櫯"K̚mDa::;ijl˂Fl /}pYMMMƍw֓eaz@>P6l+ri~.65n5ƌ9suwk4@t$zCI=i1S|LsiyҗIQ؎sضݶm;$60&X?ARw|@LP;RZ%ؗuʄA~GN Io%_A|5u~&DC|/$:`Рзٺ߄0LHl>L+Ie_B^h' R.QY>?"Y>? P2ƹ%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/epo.png0000664000175000017500000000545612625666430027326 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> lIDATxoǿ>ؤv!$N*THڠ䑇DJiQpC*C*%ϑP xTAQ"TҐB[(JIklr㳽>}e'v~Oړ=iѹq= >Qss/@] @ 13x8cۏZ-Ɩ-[a-?)A'jS0k"$axBaL}֧T@V=H8Sv=P6C* jѱ`Rt Wv?5TQVQLmgu[:qNA \LBT7vpe5-)L$٤ hƩEԍhEk[ $ N(s]L{q}y RqLP~F)ej*5Kby>1t1` XBڛ 2 ѷEQM* .}hr>}|rj-Hpm RTsE2o4+T$Of=KLb fls0@v˳x+ZR2?!Μ¶΁w0ٜ]ٗB*Ŋ4hFU S)cBev33^{‘G9+Et;h+x C] l.jNBԠ8W4"@fN{h7N| Ncae!؎6N6݂UKR=ZLkSǥ|&z98 #ϜyͲ<9+a_<@f3پ 4Pוdm6*sGyw汿ƾ>YsR\tJ}r2-U{]<7`jN^> 3oVYԟueFѣ<_+@psz ~Z+kjd g~vVa9h`a6r;p[+gfUub+j9NX+*##;aTE8?Dk}=) 3UZ ζi3vXiBΥ ֯=Vee>B̪.xd!j t"(-Hv\%0暨^x^_zֽ[B@6VLsr1̣ۖOim&**4 _!Hkc"˜UEVgd Me7Jd||k9*kׅ*90Ԥm>&gQ\Bc4UWz"tO j^:b.,jf*.Z6>UUٶ' ^B-Tsj~+a@ b҄:t5y kfty.[ P纪X{ 3XMy]Bkk!T#vyoɫy{h3R96R|69=T!Ǫ+:ͽL[i+g H3-p͘g,e;~qٵ%Hf{3jX{r3U<\t C$ x+*_W~B4wy쇪]gOn.Hׅge`6( N+q}F D{ӗ,8@׏J{Z_:ctoj@ -O+X*k YGŁV[. `6J1 6#7?xV-(S*\Pژ,Ckm6kુq->B:S-8 6v m#0WC3iF;x Ǣ1@1q0xҹ#6,78 %-`Wc; 0M Vv?8s `AϞ߭ 0`!%7@ `}NI3d AB Ցec$ԘօgI2uf^dSSi,X fm=_soT%tEXtdate:create2012-09-27T13:20:22+01:00&h%tEXtdate:modify2012-09-27T13:20:22+01:00]IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lb.png0000664000175000017500000000657512625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx{lTי;/ `цfC҆)AnJU)"-U5lt6Un ]75Q64K)6cc>qgc{ K{g{|qܔrSnW,DfRH'%!Ow%JȳI: hk.2Ev A.-SZ"~F՗у+κ>)cDPe{0Zϛf03d5ִE4[<أi(!R2 l\b^6HRJ,"l֕HRe_Ba\"Q jI!O /(r1dC'Mft"FONη uPsjc703RyKf[ FaE"S/ Pd⑩ vdL%xdHiEЅPʄU~x$M w`Wܑj$5L[:3M e.! A q+v3t>U!P,kA3 xl&&49xTN2qĽ8YPEeC;:hY` O-M`{Htd$ :˸}֌%NvN`y9bZB`3 wN!Y~N0@scQ>0¦9 A^ye8>.\:Ϸ~WOJH/e>DM31*4Tω_~n#<]4/2wɖU[^Msw3 ~@ ε qq"_u?\,i>z7?x9-}-$ߓn$םK?lfǽ;۴&4Ůn306@@+eE"l#oH#Im[-eR]Ru)__zΡN|ID $P^*UaW 1ks0bFaʧ?ŁpC?%- ("UWQ]RM@+;hhc00n|b'\6oo~ 8{@J]su%9%  `I pO=;MEazOS`o^ZZxɲOޘuSX1~zW9,i^{:w^ `]GvqC~}lZ paLˤ.=#=4:^̗Rba[:C<]4\6C=yK)Q-J3*Tsظb#nͲy q`1U%U,-ZJۥ6..cJQ}_ٸb#\M ʲV&Ě5|n.4P^PpҼRle ܱhn\ĥxjSͻ|`6?c)BaE x XWR^PKuGՈu@|jES4*v6]ԶRA>K;̦U7 ŠF@@p8Vn;UQY^~cI <'8uEY`5BL9Uzռrl>`&P3=ts[uZodž@弥 ~`;yOV&[PEvӜ.< FObyһxeq_IuI5l%߯wpĖP˄3I߭ B̂^"X&/ZVOO0ff7GA!&([gn b/(i6ƺ(T7I;@Q#hÔA Bt5y7!j\GagI,Ndžf|)a*Oo>L+vz_L:eC8oTǛ2ɩtG2%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/nu.png0000664000175000017500000001370412625666430027160 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyTUX2#$72DAeMQ@(KHQQ!eZ.cOOV9sԙUݭ .(ZhlBȞID{EDFDFd&j3'⽸{} w|'7*m=Bb c!\Y"T9>h*08w=8/ꢇdL,R`@8t^n<R:*DXo,'6Ѿi (^t)9?A~-vO4mT>u4-_N)B!dF ZFhBaqdSf||D+Iڜ9dWV>Ќ=f>۷䭷PSs R\2 c۪fذB,,D!DHV:n¢B򋋱. 0MRp)ͮ#Ȼx +ڻOFFi7Hܹd͘1l2ΞX\]OJ0=s'#WϽUGVW8|[ܹsqN^^ N[~}5'O6:X4`bhI4/چvێV55:W^U3Z c#\.Ǎ#ɽ^']l|W^1-UThtJٙ.R45u w3I,yo%t~m{ch{5.l/~~^sΞm:I#%4-704 .1QxBW1oK'i߿ܶmQpw=*.͇Yz Y"ZJTcTtCJPPS!!ij?w2w$/)$WѹSzmee3ظszmX"qhzkL <dcFOm݊X\Ey sクa֬ˡCBN(p$Jp,SypA%U'mėuN?w9ȼXNh_ =үMƍ-X@Μ9x1bO>Yɜ9yj~`4Aї _ ypM̽k,MiqvV ȫrā[v/_|q1:qjD'I#نQræ BDwg}{ΰeۗr*t߉؈@ΝtmۆىWN 2櫙>}$99TT\{w!h$ kMve%xnny$r{ =xŸ&[\uկ6n]5uu(Ýƶ^(lƶ-inܩuQ t$)-nVL~'x>t"&΍XB)Ł̺++ǧuuhD`;鸋R0aBǗ=Br IGRH K̙3ɺaxG]Z- 9r^wWirBAdg&@7MTWqѤR?y'q ';hRə={HD\Eq2%qҘqȘ2e{Ke!?dwvR{C8w}s%RP}p\ 7Oil^%WEVj@}N1ǎ%*BIgO8tJ[riva%%n!0tAX>ł W\hMNYCQ y{=OZp|y +:3$z})S)RtDEaD44xY 3R :"eBh]F/, rC&\.k;ikr\KȤ{!]M1mHe:тdiMXv}ƿ&xZ#s ]Hn~z7v+4%EW!?gR|եd*ɮ4h[O۩]}""Tj~TV"B~$mjb(r][~<=o~;wQPp) CQX]c9u:?lMJ}}fw$}{jeI$&{"BĻ[؝6pL2& Mm^~ k:@CC^~ j}d _HhwΏLTHcw%<̘뒹@Y BA-4Xׂ@3w%V$ 4ct(Ғ)*eضG5R1s2(4 8PoҴPt$΃dK3%(32peerq.)1bzd=.B@Ș2ِ&^ZCL> q˘k:ycxe( 8ǵWw" ͘ 2, J cb,Lk+WN[kPom2Mdg'0w<ͣ,<O8pQeHz=ۊC{:.oM:͑iƙ3.`mmuug!a óBQdUmI,[vGP==m3t(HO')zawcQ*ڸJJ4I%];õWJvvZ_ #cTBuuoH)%k=C'rgFtJڗTPhmuк:ITM"ëeZk;w"?< S];vвr%]۷L葇|+{6O7rl3V0 HkD^e˦2cxGg?#B^ Ģ]=6$q^<%Aux笫gp Jg֬q<-L> RmY|-+VX--ĉُɺ^j-^_C4714t d66!Fuk0)ťO>9޽g4%K2k8|} |*6nu:'Npww&{`~h2TH9d$%Ȃgp-cy̜9Wƶ=t+h߼r6v,-#wA\<]w I*4¥ɯrQ`̀:8s-駧ط,7PΒ%S  ]Kq.nxۖ-'m9|&f}2z<]7GyPXK4 )EoM -+WSd1S2ya^֭pgq1~Q"hNEntT{$쬢8Lf׮STWc],]:ٳ+(,SrzkjJuㇸ噻Yᗬz#. 3s3~5dfa+<+Wdɒ̙3x Ҷ~=Ws(]๮/_5 lۚ|(`qq6YYiNLOӾv-mW<}]ZJ~ s: G⭃|uO2 .%ZPb Xa.U9x|,HVw/˻Xd*M$Y^N~FޢE[GӪUa1)+B /ҵf 'A+EEΙCO~B1Q^\Dz3Fbg'WLkSKD ٶ&ٌ.D !$>bKLe(+AD@ir.aZο* GkQ)F:Жq<[+ܥ.2NM}Ygٽ4EIDĽ1|7BՃ"}~cրu8 NƠ"G 縹}8q KN;Ǔ4F Y=o}I9%ßx m[Jleq K.)A2PECaD YKRR%;:;"/AF^>FV&hhc6%!!eH 3x gLGS[oϿ+^Z8 lS,I+ei<!5ZDPVDPo4K<[Wt \Q"Rd}@yDk K+ <Z9*YEtml91)}vP%y(w$U|0ߔ\TG7B@V` F{8>Ƶ0Yc>ߠst_{me XX67!aRIU YEƲR&L'2bV $=\'%ߴ$:4}k!Bwu 'u 𸝳p Bo\_u$IӋ+Unڕ.攸X$9Y*G Ǖ #ߏ֗.*AꆺFy48 o%%@ר3;{vҬ;Jr%0|skYQծ]NΓba2К7>P/1>KMx`Z6}m O.@͆8ΆeW)fR{u~ohV"lm )IDATx[&Gκlw]1J4H@AD!" yUPA*'-E|PAA# &FIИFflȮ3;3]UWW_v61멮:UsꜪs\:Υd/RٗCҔB6r S yOa@̮rف]yso[33 ̬mYb^{C7o.qA*ʖFѕԹ@|w;׿|ȁ"qo^?[Cq X]`u6sι XZ5rw6.uS0^hB¿u{Ͼ|'o[O d b V]r>vںmnnBss`;9DvXQjm@uXSGgOt_s Ck[{ >i6g36l`4h^ؚ,#[40^ iHbVX,2K|{!a}/0{:vx݌7_ 5.ѺHeiiNGF؆ၪY5:ebeEͮ4@29_j bp[GKvՑrv1`=rs"ˌYf{6u`dcj[%ȱ(?Ծ>)l O ԠlSyU`bDf,<0\ ~+Ob aV$Eu:|AVsejS؃/Ф 2o 8}ǝk $W3tBqqEo LIԎrW9.M} 6gB}`Sۙ5R}( Èv{zϩ4R~?mt\Ry%Fw~3iF_W~`FDL8yG<,1CS-\)y޷1q}ݟBjw!"܃8p'%N +>PH."%Yw5=jLL jo\"D{2v#oT˿(b&J*_Lfxe ~K6N (Tdsՠku>>-|Byy1w<1a%_S .$ql9R sVXc [IflnDŽj5fhk؉wZy6sva)n n$v^ؽD „ x0zwJA.-UO6 b Z\ T`˔ PCL7zA `oYbxN n3؂X J 3,k]A=/XKV\k_|BVR+{lvV+hhܰ n]%-8\TsOp81I".'#i }8B/ga>:j*xvyxaIt:1ɪw`ND}Ms=*#Ymdx5X=B~0mz(D67X Z}t޸W !T`ڿk0ze⫾+Z t-) ,ݳrB:ƌ}D]4Y/IZh{~5?uSs`Wp*nX:/~(todv[α{j)ڭOz%-N~?|0Zpp8ek8pE.;k&Yڴ~ Owo9%Q {o?wEQN $돽uWV!_c}QS/9 iF>6,R(U *ϝ^F8v*CljpT]Sj:GgGSR*SuE!M2ߊ\NѼXߗkRbe`%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pa.png0000664000175000017500000000610312625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[EU}.sfd&c$D݅(KPčBDFǼ0GaMv͘d7&$3ꘙɜsUurzb6]]U믾;Uu. Yׂ4H5zhz"qOwCF H'mݺuju!H(XWpwށ~8 +\0S FSv9k׮]|RJ;Y  j#}q![V@ys7̰JEȭ^z_[~!RAG0xO!|>X@8<[e90-K)V*k~:( *<+ԾGT.Ioo/JTU O#. `4UMki~3TZPT&!!{P|?2~[, rEA˹#زe !i`Y.$[/491?6;זIzdI@{ w݇77NEF<:쑎ePsVp<*J D! OӰ9bZB .A,\n4@Q$4ҦIv&H! A1f'i LE҂ /nlSDB jđ $MW$8a!O ިWH\OQ_"δe Z0!&i²]]Ô)'7hyd T 5kO3BbC"Nhi?/߹)Nv >p%t0ls~ng%nh 'EHX7FNkD6h}0Μ~8q[Wہ$MpC {Lnn%9BW7L)MMz rf/caBr9M:>]Y7DG ^ _`pubeTG򌁖(?zvYNR_~ 34~qJ;H)J۶hf~Xؕ+Q.bb%N"QC5|u.$B4 mې߲Wz Q:R 'D޽P NJ&Szf?+w ,Wrpf /  &^fu]]'a=kJEn`ML@?v,[cvPV HZ4-2l:6;ߏG{ T`+#'q1q#h&.`aܾ#]_gk6nݲsoڡCh=ܦMS<VBL9낲I3X5!89e%4œC/@ Xa_7@ajP, #Z@ rʈ'1+C`Y&LӀehd:䆆`qz{g` Fqo{a0 4z^{e? ¦i@כ"mc CuuQV}[J…3?47- Ke,+r :jj: \r뺎LNNVN>l{&'H!e-, CGXCf)MPav6V`^8|7O a-7J`YÿRD)&& "%D4tO0HV256(2\,EY^J04ԅ^VV~kNMMMLLL1vM/0 '_kH f\0#atd9PsX Cxhh8 TmH g5l+ބض֙5nn4Iz {ŮUc IDATx?c{wo.rIhI$JDUDBTEB]_@P*P%/(MHK!Uݮ ۻc{{$lH<}{\rYE|Cu{Al*`Y]4eST@ 8|ZW)E^!cǁq>S֩QdZpZe::/-6*w 2 D'<kWX)p.׷qr²`$$DP*qBb5;Yn*/ֱc5J)\W+^ضhi(+~\R%8iiq}:y(+dzjRZP*==IYiQ{p]hZ[N.g#ӳ|ɗ8fX:6Υ*Rkj5ӧO/Y~0LĽE XJʹVZͫロY?:[֥=kR"˲RC@q0Kh̾yP&hFY*ZTF~,6glB laXm|)@!&ImGu-Klbq,``@c:5}cQ)@'RRvo`.gk4F)\cZe4TXkojyGpò3+ux`7jI4䥗xlq'Z;1CbY͙7ΈV=Yt03qN+W)RrNJ&k5`"(ٽ(_~Ya [n(-`l706ViП=4&jhAcR)MoC]RsRhP!*ܴg=:sϽ{gƆ ״ܣfϞc><88zvAl]YKB.5²'ٵ0 X+N۱0DQ 2`^,L3:dxh1ktfU+<[nj%<[f߾ sVn\T [nOIvbpG4b|ݻaޯ`$J)~9wubǎ24T䩧f2g ;w -6j@Pp; 52ՑN;5|۶B s$eg\qn(ػ}}Μ;8D8S4RZtJ9w tHMP (+Y2<$TnKiv̔V#"=*%shC DD7zaf)͌t=؜;eˊ@HD3H\tUhȴZ]<[AԄA7&u$jv(&n#m|` ",K7f =ci\&\}46`5(@Ɇƾ7$Q勞chgnܲe5w޹E)xhx"1כ6,SG>TG%#툴SAML0oݶG!+Wd…y(.̌71 D)%J{]t@5bD!h+.I} .zAJtEZk]Z={Y.wSwmX 2+,גvmfوU__ LۈD3YtV rrdn\a-4$q֗0O5^ᬜ%>PgY[Z( ; zy%S:>0>`>L۶m/-6̝HlȒ[oe v@ۼUac2E-zP32bBΒŇZ^HQ(L:e&`~%;dg%tEXtdate:create2012-09-26T20:26:42+01:00,%tEXtdate:modify2012-09-26T20:26:42+01:00c?IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/um.png0000664000175000017500000001221712625666430027155 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy՝?ό1`6 W](# H+jv(jlP \=s3=}Q=H ?^z[~|*ʟO L.9'N;%,it pk{υ'mo?d-!*qi?|@8Ɔذv%wbO{'P .j104wXTG4Ue.4MaJ6]*`UDPHE(SM+5_,^)TLô'%V#<6?hӻƔ+F 7h'FOfi:DqЄq2;"*|S TlVЖ'A`Vp)Qʣ/_K:kqh7ɔn\í7oCG{ф@)e?S//nSyȘ6bZ 5|ݥtэ&|#MGW/%Mx:ªTE <7=-?kiumz,DyדDXb!шH]()3>rf0KJ 4bb2ۊk$SaZcO{'Dִu db2y דxR<)=c)7BLfF kt%٬qptayضH$SH:7A`YC#1И:u;Y^`x44¡ wrn}G{'!Xwq| ly}/C#1oŴl{F(@oX}4!t؇{9SoxZNM$' ԅSHY#8x7D2Cg0eX:LPMCzXc ư]PJLeRo+Ʊ&&StR(!pi2=J,('9e,Rfn25Jֶ^pOMy5ׯax,'r` !X|>5i?95AC]5\=:IeL@ A `m -ꯢmlzvP@4l/nd٢V+d6]~,o?^%23bM,1WJشS)i["J*=>q& q+L3Xa@(Y_1|d-;/ϑ~< Mfd,ΦXr(d-^!UacI¡ I{G?9rs_5߾׊'=aYH$ 3@!t؍h[lŎ P]K;{]Yx.~gٱOɈ(_˰lWA)E0`\rr}]z93k~?$X{x.l`".P3BX{QR^F|I+ׯ&1>rz?]n)޵ksr]er}Jɉ÷6Ø&bns=M:c?y( Oݼz@g^K1:m"5ZI&?*Vu4L1:m{파uy- @5>AlèyH'=?JVع(bwqh?=ݟg&~ RZ_lzuv>L"`V8o}",#mwК\w|klAl~! 2]>d,H(MsE4Ə>w LǒL&3 l"HS]&BxDXD*D2Cl2I<4m?cdMx2Cdd*뛌r\O<iL;+W]\m/Gs|"Prú RV7'ҸpFrԥ?g6BJ) #XpRN]'i;SPP=#\2/\6䳊+//HDIOrFͬJ|a<}.l 8zCaQZ!C~#~Oʭck4> GIҮ˨i`Xn"g¾5Ѷ3X8J墹f(B(\׌NmZn+ִ S,* Jz }2zrEvVٰ7fj hB+|T,GS>v4##Կz90 pņ\6'׀W!Y4$ ds$TSH% ^>`B`n%sAZl}7 Ňkf%o^vt:UTI>a0-F%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ao.png0000664000175000017500000000612612625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxkUwfZv ۺB)[Db ?P"L`iԈ ?(^&^aJh˥AB -K/mKۥ};3?7;owP&'3s{y{s8&_#h-Hr `EϽ%G >hl鵬趬SDDhRK5 H~kpPSzVCZ58UGkuM[<0^ĤW8NוN˺McYe*U<^hT\.{? 5-qMDhjihh`@˪KqIBW*b}ckO:]yۖ=PZ_e!Dd0uq"U4+IjX[ 0&bu\)~p>OBWN!rz ,Bc].cE[ }lAfZEfv,6bXŅ6[IJ*di#R +Z0n*%0-3(X>L(0o QFg $:h\h65 "oW2Ox|p$KsIzT`10WM^ w(*eȂ?Xoe+Ԍo03P/8/a/VYuNBsi@V5[jU 3(]!M_KsE@mB&˚&$Z]j`›1yv柱R $ŧH {X BE ˆ:)Rm ;u4}yVV&)2.`ק+\jPV C f)t'PqHfֈZ_Sdm`+P ]U> N$وd[`VKfb<[UhVXAaStiI^L`N+(]HIIm}Xtѝ 0uW3_c0YKZˌK+{<.G2;EhZ<`ρg=WB0w}0Z62'2L,xq}87Q#C(fͨ@ X<1KGFޅog<杬r#+'ŽuX_<$y&ꄫ7)+o@>^g= I!m;=[\06lžA]0?8K4ӥo}k}57de˴LB *|V RZ:|0=S?J%ؼE)aӫʛ:M&Wva3]M'eu {7([Ј(? 8>eп.Qv[/S0Ϭ.]Ϩ%(1 \DŽY]l2CB =\vvLkV(7\D9mkU^(?+2Ld^DpDXK}u7OYgBIe#pY}oз ({͓fh!|b#8l_  EY~Y݅Y)H`eݯNgL6Je1+VlSz .3[.Jd W,E"k jX6O+mph.Y`\`"R΄z'}e|ͯl߭VLu¸"J<xuf:riB}RdM܄Tz+s ܤ恌V5,*}JFB98Z9}د Y|y 8OeA&NzDD|pGrbfٮ8NeYSWKjX:n'ä.b^WT&uy7m* ^Q۶u?cώa8D`m=--- ,˚ܳ(߇nZ}Ç7{?bOZ3ЎY`sA O/4 wٰŘj# ;E,cL c604OklwwKO|@+QzߊYWhP^EB"0$xɑ 3c%tEXtdate:create2012-09-26T20:26:08+01:002֘%tEXtdate:modify2012-09-26T20:26:08+01:00Cn$IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cx.png0000664000175000017500000001174012625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy]W}?s{$^&ı,NHTJ+RDuR*JQU% (M PBJ'q6gsq2{g޼{mfU"ͱ={s/No$oR޷Bҍdu `2&4g-mwKz/4" "",]Ÿa57}/U3HxWU5k}]W_XzԱrn\ {KvNpa89)x&"K H(&*|OS35<[F~m+Ξju yq N᝷HnsygP3vMQ,no0uu$̑z$t2oP}*'OqISU_ltb<齤L5:i7> ^GxtEҲTi-&)NL~P$O7Ԃ\wѷ(cr>\c`bO_BLI)oT}! xWUze* b 9g)wOGjeU40 " !QMA`-HVk#G ||67L8~M: EA1-0kDj OdhcO+Z M K”Fe]ue2e|C9p 6NM8Jhnlz7l5tDQӨ " עx3Xt].">Am,mW.NϜs:B4e@:xMxQ М)6tS|ja?[(eZMh8h8u u#LMc57P x v{D!Xބ`kRwV0!ri֍WӸYQk(`i+c\VA/b?qu_>˞^A_48mSas2FwTclb޸nJXkekg< !o;ARfKb^kgM[`$.KP,=| AmܨF=Z>#ÿux8Z`.J&[?kp]64yҕw,wq:6\i5$ %6ۮ[Q=/WolU+aw(X, W,-:?LڨHfFY<z>E1n[D@!g`U\KQkXh0đS:e5|ls0dZfʖ:u]G.1#^2ZCX~J!z/{w%Ҋbr5}mp-ȧTp\qgJO#4M` ;2V]M[wN9s.H5uS^5-ѰPf1ہrI򑑀Ɋ AlJ(:GG}\s5ᩢpxQ8YV%`f{G)A ՘ |fFY}_3[W&.'腆ׁ\c֩^2mf4Fg<;v kp9i4bP'DPrS}X`^c:-l7Hsc(r|><rtJ&ӆrrÝ3.OxG#lwa k淳w@ ҹkt_$Ob[VcxP^ bLx$\Y\g !V=t1_,;6;B&e2h"6h[b[t3g`ҥҹkt &z2=kM:xRPNL1Ƈ6ql._m ,8J'|)vQAT&X):L@nh [32;j-vd5ؑ2P$8aEWn~n?|1Ƨ³ؼHW ?yGNc4qV6?@ɟŀ6 q,"m,j5?q!c:9{p׏frXaǶ^~[ ^:e88Ƣ*`UQk:~ {ϳQiiJWwutJ>oX)DY:]|c[2ş~qdpL}.x'a W MdKkāخ=R Rgd0ϕ{$**GxMF3*UϦei]_HKl{x0T=>Tɨtr2k1[`Hc<7zt)0ýJ% F|,{~r ժmX;ѭW\g71SQz<^SԦZ#VPKpPZm9O,pվAgl0؟w+EJe?OR_Ls %kZРMYQ[3(u5f(\y>SA`jc˽΀z`* 0M ynxZ'q`t1F- +B,U?lc;;cg2Q4Pm47m_ۢЪvkPCʊr4E^9uN&[ C=IYjvv{ /~q|Ԃ6 +g`ۣ˾iĤP)nQX6GOO,4l؈~Pl8gn߂4ܰMKkZ[\-{> `fAltE FPk "<0FiPV*l3(!u#P-bk@%`tɉ"+p+Gނ:*M/Ť/Vq0M˂^6j6ӛiM2zQ6%tEXtdate:create2012-09-26T20:26:41+01:00#Z%tEXtdate:modify2012-09-26T20:26:41+01:00R% IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ss.png0000664000175000017500000000762312625666430027166 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxu{fry$%2eZYg$Ɖ%H 18K N>D8 X1 )-K+d$EI.CޙzTt.WI~]Wェy5+Os+D@@Mc_g zqJH›E->w,j~jYjkƘӾ ` b^I(л}K}~frfM˃hwxrvZc}xwc)@ zguX/CÔ%ti`UwpE'\;V4qZ˃,ZcPYALR@zzG~)Hge|υ.dl 3x/`fO!]id U0QG^'R8"\W*C[L84j bp$҈}Ȟhޥ剨NG['# ̼sGݸ `C:WD\  A*{ʦ;iLj88R"ʬAY0y"( !4`.|ywo3lƥ1Sov!Rqdk!Ώ3DlR/!PJBQ:WPpPA0.̟%|ekK.l\}n9Yy]8%JX qʔ|'_F v99 ]V^Tެ xL:j`7nVT=Jy/WCc!*Pq豒kxeqQ2֓Z*n6zv}?.pʀ{I^^~j{_.5Pr/{iL7 f*A`(, q vH]G]}<6AEqdfD$-Ѵh8zRXyեEk_[qa yᶱܲm?wrSF jrtq9ƀzq‡ gA&5g Kh)M LD}w.ȿM [qs^qXu(l'syJ&ȕ%hoCҵb'=&# h콠\OSNNWE:}s'5658H5hf45PN{9÷ǝERޟo_& ^Deɺ2aC86Fw$OVٻtG3d@9hnNCJ3{Zݧ/oddpA0Eqf|O~S(E,m~bUG u)wn73v(PSrs{-wozPJ8D$XqazaaFOo8b_K6 =`1[xk+Ap9ƺ`9x Pّ$̖YuE@aWm'ՏbH N4fyO!20*kЉqp1Rߎ<ȳo܏911ƉK;ٹJag˛l2 \;W^:'}b 2N=T1"&@x˫Vjǀ=/~~vj "&5cՠ5 FGGw{qF䓙,l015<Lϯ =s{6.z" 'Y\q߽o[{`HcGTVx_WJ['S%=ڼ֟fPN4G/ ǖ..lÕ<%{1F".WW0jT4+ڸhdꊢcGx)Rg.4Xpys L]UWЊ³<ڷDAMbn5[hB7RH[-,:*V| Sl+hq oB2*`wXAK54@,K 5sNqH`޳vqPC 9ȇ;^njM ˮTX+"$/ Yl> ~y -m{ߴf#R-Cds 9UlkVv̺˴PXPOkB_p8 Pu"M!>O0v ~N"zJ<yin9ca8A hAMOZk 4Uo`R߈0ȬTښ=/F5cA(.`ZSW5`JX;hy` 7[7lhNqwc)?h|Q<X1'ԛ408Ax`F&fh0Pj{`=85h(dΟjrXBgϞYAq{fDp˘ucpu7 }Zt j5 )QuYr&ꇗ%\~P2 l\odVw=z@چ꤀/._[x?d݆"m0Hky`W#xKtP -@,@ Uw&Z!sPzr`z)~F#+S kBغeQ&ş|j_^X[ِ:%)V?ODo%t,%tEXtdate:create2012-09-26T20:26:04+01:00(%tEXtdate:modify2012-09-26T20:26:04+01:00uPIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/aw.png0000664000175000017500000000643512625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> [IDATxKuUrj(RI$( lHĢ!@ʋ|lJ `IX,Ӓ(KErq83wEw[q9]U:_ԩGQzI}IeCA >y 3=@?唓fg&{^Kh pwGҮRRR *tvZ,1ms뺓's1q[;@ $u$։g;_[c3k5J)Ze5RLx[QT YʤuT= "IedJ (4Oֿ]2fDHtkԀOy/?2cGf$ahS6q" +l)UZ:J]ž yq9h_|,IΪḼ̌uo?M76 %J qo7r\^kt*C\: .g_?~v@+78MZG"Q;&'M,˓% hwdbq E4HRp*a㾙!Y,FhGFfc#u&Ў$0*&TJkjη8shB܂UC;u26gcPҎ)`$3ge5i1FN'}o]!s3KJM>saUu^}A׬S$I|qS(MWqj86caǯBAz Zq2*|չpf87٨pqF~ , @DPTZ?3X`^pFҰ|έ[E9r ox^g]̎AŞ EtܚHDkZ-S>L$,p8@)M}_LNNQDTaPiMdE^q{斔tnӻ}?|!w@NU7yS}ϸYf"vX Q"FZtx \>=w]boa.hy$ y(]4hȃ8֛99ϫ/wpsp)j&I1[9v96/<ҙp܌&Yf3LQ5U"j rb Xae;boYFؓ_^<|t tJWY|5dade=4aT<ɖ B_l2DO37xeKMtC'҉! wCXR Ξ##c\wx+AAvn t ٓ>xbo> ttPbډs3H(1輧Σl-ҶԐx.D`D0FÜ9 qLx xG |u/JPN 8JX$bb6QWvXvi#YN{0ӇWRҹgfui+j6h$qHfkuvwpR~Dq8Ͽ9$ /鲯)]M*W;Wts6DabiZ I_]UA_+K]{c`.Hm* X 3([X?ǶƠ^{z] O";A7 bm¼*|yuFA~Pn%J-,wHb,fv_Mêѱ[YZ#ָg;,̽,l*$fJ_i\ȕO\TzfK=) W&V.{kVxn)h^: A FB2%=1s /<2=;D sPZu,p(tf/')jtxh\AG [7x5ֆ%K%N=G]=dpϘUWR.0m}ޅ_77nI:cu'!|oPNn}]M/3usZ.\I d$5/r̀u~vN\?;<{TSF; WF!&O-좍6Ȧ1ҬQ50Dgdx<{c.W26&DL6H(_IЄ"Q 1Jn9AE/+l;&@L+&؎ m`iNܾNAivR ,JLvp]ù x sdJ; C$ZL \ݝ7na1H{;꘨:u%565{xÀ I,n߀6Kdžu0 ybrvjҺ%90^IdD6u:hFl&)~sHhm|l(e 0#էsCrC`'`x&"4GפV7BbcC Xڔ [xߊVSXMRk0GḢAf%tEXtdate:create2012-09-26T20:26:26+01:00 %tEXtdate:modify2012-09-26T20:26:26+01:00QIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/hm.png0000664000175000017500000001305112625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>gIDATxy՝?V%-.A3( J DO̙hN1*hQQ#i}{KURF㙉z˭_ނ/KR">ksLff9ʬpyB@! GK-15vWBB i˖Nmm+^ (6 .t~H H{<5iBJzK&C17H߽ѣ)fP'd˖HD"J9+BR߲ޏM03zt{uזLܵ{qƁ)xmcnɈw9!7 +k+Wn)R ˲(,,Φe+gfi]ZJYgSPptFڇR0zl)zY8DPN]=i#u`$7A4Mݳ̲, #h٧~Z5#@0 @ x 7l$wwȽj"\ʴiZ>kVGӴ(-KOa@y uaO哛kh70,Ӹg1kBW_eFp]̚Dqx >Ç/tYc3T<\Z i~ka5)e3z0¾]{ gyϴ?r6Qhɓ9b,)SF߿Ν5RJ,"T!`x ԔT~EG68r^?Q;i>:gR0>)  6n#l!B2dŗFQBd\i/mL9BVJPwd-߽n_QU4ix#y7DG2zt=VɪU܅nu0M|w;/ >QZ|/&kѲ8|Gy_@K0۲` *B/"}$'lơi2RJn}d7EǴISi}w4< |_EE,Y2Slhkl(˲JaV$'0fKH .… I!C[wu]fB(aSu]vt 6߾^a* @ t8+zFQ 7~W]sq*|3gS0gMΘ1ŬXOP[v0DE̜9+y1W-vF<Ŕ{/o_=&i2.R0.Eb׋cqF =="%ɜRKMi^  }f+wg+rd'{. .$sT}~9qnBD!Pҽd X@WP8oF ]GyCN!K6<  Liq f(r| p`%2a./fΜEAA>_&7amHO1LzÇ4Ѳl] 2Mw:^Ae9+ƓvTR CG+d_{@j@> zk_4r':}a"@sSęٔ䐝ET쾵!uhkv.eӈ20hjBHIʰakA@ UQ",&$.IVޛ$9v%))d:^MCJ;_JXJL=Ā=N LgO-%I!"E w1. \ArIM{̤@_-+#_ZMCT\Eq}0 \'⩐)@>^pw,Z X۷c^,/cٔMkeL5J!UDLe~;mG1'Rz,PVllM"J)?C.LSQŚ'^0,::=tF?kIzǃ ǁ0-\?4O>Iv|>2;R,R"SRlVE ҵm'FرKaZZ[TJq- 3i&x4t]%T1`?6|ěo*!ej<פLXlSI4< ۷1v ?CN1jL۳*O2bDuի騬;dJ0g4?.;>8"}qJrN0#Z\4_F-T SʁBOPʞCE^[o[[Pm- KR{uu̾ YF _KWWWhY2;|1teedΘYYIڿ1X$O 4TOSfOF9ZGLDurs3كޝ>}w5I4 h5~w7nϛ"&{wgįDVDR`Nio+ٺ06/KJn.4Hٺ9eү@)+i.0c΅B& Сz{034MLΝM7M!/N/4/]gy4^8kҋ/:2"1QNmCDؽ87fU3otI?\?0MWs;.TΓ+ߡMsN$JŠAgqCR[Q[{Çۣ:O @T`ƌW0eH4MC[Gݯn&O"cmҔʁ}\}b ,7BJAWW?={f㷾u&iYY̙ hXs3u_oUc+'jn~o3},3'eee9,X0Kary'G8~~D'/5%k_]GO;$³<'Աxq3|qAZE-Q{ .8/,9:MYJر%吕FNN%%9]^AL Wp ESOqh| &~BTnXJ `Dc:jWD7NO?ٳgŊ-ڇG 70w F}*t&]aX]BzK/.֭+Դ/x5޽<':_|}7ϺWUZeˉ.9p/,Y<6}¢E1~|)FQvի.J -Wva#>o'Νx睃(e*x4K 9@qqwz ])K0M+\,89u׬+++c0llfcBr9z&\NFEE)W]uW_=.~y+l{J]IbϐC#"L e[~ih^읁m&u2)3!䅝 H!4\qW<dkk7oWDh:8]ÈpN#:ioiS5Cy~cK~_K;s;C_dJA &r1Ӎώ&K3Ӝ OϪ/,%RLs}f'tnϗ A$bg%kY}Kn!Jc׫ Yܹ_8MK8@T+>sL@|}n'3]go ka=>gDFF ȑF%7(c׮֬+k >loo|{*agA S:cy#[`7.΅k' oc:x$ݴu3`@:HI8v]X[ϩ%~>$rPP&G:+l+x$\3ŋ/( G ?Dnn:gQ@uu= a&?㭻8I i0jT׷8!1})?T%4\DID>4.^}/gO? ]p k=V?‚C{̲, 0ag=]غ;vP]]O9}θq {3,6B)+嗫Xp6_q[8fβJmm3g^eG(.n݇$ۧ  ɉKզ'~۔"`\y'hnn! `HFA5#܋>ML4T;ےeʔ^LJ4!j:1~ Cx2J Lr4X֞?C<8}t￿.2>BH٩t <fGPiRS˯1癦7{>-/aXS::TUNLB(i.]oz?a vp"hJBLӴiiFJ^"D¬fJG#aU IDATxoDǿon4ICӪR KoP ;GNgp*!‐ZqjM mqz<;c{c>Dzg<~;7 YB Synjaʼn@SV@C @ R;x" Q9\{Gf.6 3(y?My'2D_N@ iq-9lI,euنwyJ)TEL=Q_K| A ƞZJ9O}Z=^@"4ؒ9Fש>UXFåQ440 xÓUw}L+DJ 9H$ʀl>ot]{W  ("B1hL6Zt%F~+(hFOH)=@46TlC';S<JW hޠ ȋ`2HM<ͭ<.D`O50x "jb0g.֚&Lg,*бE.@} C2PJPz7*}mE4MX3Qn~dVng5f:3x㎢M;o31.hƮT|s+}SkbR^>q6/a:W'1D$*:pto*ށ2JcW?6nE\߸-!Iݕ|$4Ű?ăz>x2d,'ka2! NPke[۵ Yl+ -e,R[`KY&#V.ADz M.MlzRa{["I$F1 D4^'eH1$%ocoŭ[bB$ ҟF CX\w33wπ*9YAJx;;{ɰs`zR@?@;Y 3 =%an 0I k J8Txh} V%&s~v5].jt8+UR }*D&#M0otO_rZ"΍W WܸuotXwϚ2l]cvm5W/:r<덡|,9ӕ)G4-7V9>}>pøv3t 4H745Z}ap! P Ny\yf8?KR+}n@c|(g13  =45k})Px`#KK-G=u3U `ӌ]n5x`DO |^OY6i$gbkcxQ}J#\^<^`1+] Dhw?2}5f2ХA JEћKK.>(y ~xpne<w4Afo8jyV>j6(`h͡&,w#9F$B/dzb14C:u3,>׊ͫ1p U8p\qȿu%tEXtdate:create2012-09-26T20:26:09+01:00,%tEXtdate:modify2012-09-26T20:26:09+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/np.png0000664000175000017500000000713712625666430027156 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx{pTyޕ H  $P1?b)q?R<gh$؞cIlOvlA`(bA X =Wqw]i囹ڻ8 n-%A*{3O!9haǴ~\w#K ;&ǣ t6"l2M!~3ֵ֛(W9ڵ}ϟټyhmGhze?rgOd1!>RJ|>qny7^޹sg-tE]rN/ݮyE3`!H:A^T,@Vnw\& [_dj(԰uC_cһySq:b`_%%&yڶoGl }!2/&i„)SH;_w4 ߎcZw!Pe2 & 7Udž4˿Ô) Jޡ>0ԟ ЈkӦgdX@s8HnsrRSlӉ2Q7SAje8Bb߱ijěIVXu/Ho~}Ҥt3fo1G" 7bk:vv‚|qsy$ٍo g%ۢ? }".j Y(Tt#8^<{;i ithe%׮lh@Oŏ4l4n›6JjVʂ}lyC][>8=w;0Dh#E{E&Lt!0*+i/-EwH9n@_y9/{&5 5 zÛR p3ZjVQUL\.54M|>---СCŇ>^O+Sʊ AѣT._}d23Qsg9B%Y  ކU/ RL:m?.Rf=KYQ0yp\?.OF$4PeMC|x𔕁hB(T.4=TpܬD; 'r<Rk4k)V.yy.l\.Fm؀k|>iG30d\T m0-qRp uf2CUeD)ag>"1 :| VqBc @&-9ٿp2 ̆TGGgE w`U7.ľ@giܼߙ3aZ>JSU ONK".Ǜ AO~'DA{q1s>SJiAǶeEa/6śGd177F >ܯFŃZC@.o…%K8z4月TpyZs>`U.]cI&a46Rz5Z̖T ܌iC._=/a!HhÐu,UE4_} IW *~3I[ƍdYC't^4͠!(3f(@ YYiHymS)ٹ`s-& `R76F.m eL3UOR¯S_!#Fbk%!aD@ XKDըjPy3Ú+. J^ Jx]I!_3e%'" W3xfP+//:"]кAR^|W2ddĬ@ Miz^^/G%W.*@"/ [=4oh1(Qru3xRHOw&40SVX= PaMs  ,~Zd2<$o#pjo߾SN]h 2ݲII630jJv8u xpHJ *@ n;>V$l(JҀL`Mnjt8&4TAGRD3p: BH4}UUUU_y }G6Bdu.``ՅVAx5hMX@anSj-%tе D0݋P+ GM(:_/2{Q}τPaxͪ}! B3ly*[2y}}%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/st.png0000664000175000017500000000614112625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxkk>2֛7,ņZEmAj[WӔm ~*E#ZP U+*P()֘Ħ!"z3g~3g^ޓiV2ٳfo5y;o,=Ld}NZPϲЌfTY swԖ^$_."",Dm?\8"NQ6׳OQT8=0:%NԚB [t,~9UAXׇIp υX~14/j e0Ј@&  jr*?X\2:n19Pq`OwU4RT_?g0v[V&NL,uVS'"xaX}ߨWH ʽo pbP,D`"Tj'__?kb*5:\ElmJ+Y/TY6 D6Ԛ 0` ,t.#n׻T=KVM0 5ArΓ/{b,XZ-V+" 0 *N  ӥ6,Ncgnc֗(-Z2 Ly pn}PI6 *|w',%-صopo?ű5Js д Dl'jDfqﰠwvS?NKmJk1bjy.~smnD>q(Y6\%({|MX0 *t)#K O<" Zb>mKS`:5Ϭot-ΙSCb3(%Z&~#ܳz #K ^|?֪5*jb<>y|:q~DEt:ß]m4n<aJj;B q)098۾f g4!@c >dz4xn+@eB?grL[FiLp#{~I2D$3I[ _ 7hƒe3nZШ*!0'RiJ8 4^EbaEJA,D7LeԸT02rk ծFE X @),he4r@MqUt2{HW"A`eqH=W:0V aQ۰4PM MT Hh/۱h*8I2LL>rL2T\mS(^YqG:$c[{ (zU92.8\~9,_? _iիaRR)| sp#&;KJfwͰk!]/߯exMl̶R+lW8R -x%;R1 :r2:9&'a68|j5e^a|xym+aŊ"9=t93^U`Q*bhVÎx~reʮ]ʡCeQUo7njʾ}3o6V0,¿ \pw߅M={`,y:$:7+kѣ¦McriW].>_7"Ѥ+)jLC>0UedǰtM*زIдn*O<ّI`g)0;un2hÕvɇ_0:jW1w.ZUf&P_dN4yy]p3|+mݥƸ]Qj-u[+mZ7OVPz茺/~V/`1y>`;Se PQ}PU^iʾ~ˆ&J v,Up|qBgN!oݫ=^(uN `K<~7;qlgU5j*Spl5ֿ_HX[.׵~ؙjg~}:0Cqlﮇ߲/ի q<>S=ގgI |zx.dX Tpn~"I?XnZǟ.o  u#c9PQ>i-/4}5Q^9")in`v9aR @mk w\EZ6"*)+g͟CM}+@\-MUx0SdӂX̙Ucv:zhD [ ݙ ={J(SgԡWuu܍i}R6v?Мcn~M8˺ )|esF* B[ *0&zt^ߩ)x/㚓G@&hjNS` ;k;h}*WSbsl6 _k{ &V~2>8LSFW ONgtV> -zrkjV ` }dX"ᑡey te5aL|jG ]i+Pq$Z.Z\7I죄cx Uh@4Z'ssb lSy8HiD@D]cU7JY{Q;49OIBC[sU}cx!e@9og WUӘ%tEXtdate:create2012-09-26T20:26:22+01:00ݎ%tEXtdate:modify2012-09-26T20:26:22+01:006IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gs.png0000664000175000017500000001607712625666430027155 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>}IDATxytՙ]dɒl6v@c@BK`y'Nf&I&0/B̒6-KeٲjmVnu$83rϹ[----SBRHc9\r@3r# T2U6o_qMRBf_;bQQUfuF(oܵ9cm?G3,Zx8p4"lL?r>M \|g|iP mL(?kLOqRtRu2GJ=n&M;xXb#Anc/x̤3gBQ@JS^;L"BU'N vZt!L*fSQqVeDF֕r-\Ywhر,X%- cʹl]RVUa([0дL"Uv"$ * _%x{|q+(WRYYICD֯o䗿JSSgV ;%%誊iKK)(E6@ \.W/Ίk'RCzV/;_t %uS' vyvy/ao`J #8+Uu*Nra#Hp괹wq_;7Ν $Y]kx.'SVh$ C:D:R$,Yz!S*[D?J*H2kxhnF$ɤ$Љu.XC-a$WݫXV*=u*v.¡V68ne~ 4Yl ߩ0TJ k!0z:raQ_8Fp_y5"Wqr~3{ZUEMMryٻB̆ 823U]@K! ЉS(@ <_Y1oo EpeQTˣGC8S$P[7*|)4}^!‚2!\i5N媱e4 BQEHGy7;cgs*[P]ʲeә9o~AT5x+ه sjAJBҒF?)E `O\ @Ք>|R>K`fB_ |pP;wC[GtfT[}=F]] W]5ƸeHz^=23_aB\W.T[߸u7u}}-UUT>> _o`ǎkHC5Gkm 5O!E0 @Gʤ4͆:$0<Eii6ŁZBEQL≧7o..{ωM/~A*P<JK=\y%hS Ifëd)^Sd.7a<;'WX%@9]^{lJ[OФa`*дT*iRQfӍ_%TcPU5+ҐwRbD=R1$֪~uU=.,7܈/(.f{&w߽UU F v#EpzVޘ(鋛v+| kc˖\.k? kT n<+>&@E:ʹA#_#ɯPQH9S@ yD@ U2\.7nC5҅ fj{U($taXvᇯd:_Bv[x%FQO i]łb(JzcAE4e~X2R׳8)%BQظwgaIӿʹ $?L5נZDW/XMQx|q ]$="BKR$T.wk8tP(swFw!@S̆k6n VeTs7mRs+_s# 5C:1eV4 /vԩ>6l8(v)`z a'晶a;By鶨 1Uqe=0DYNx©~7*<X,%@_V4"0iDBd$[fU%UyFo7_-*RjʩP\QPHb4ǘj6 0 6Cn?O=};0n3;Q$(Dӆ6%k,~Vgz(dĆ!)-p Xlz6"I{𓣮>gu5%]M;h*'pS3@5;O#%D"HDJ'RB@3^U{.宻RZɪ[7\8'O4Oɓ+5Bk6l8o~sw9JJ8NG?»x1O>IK/ڹ)_ <^c1|~X2BW~E4"NX2ۀg7YuF+x, 84˵ʟ `1Xxt}t)Rols*^{ +l[CGG?Vk[W#"f&ݫYpA733нf '>C8.^ckٻ;%SpM~IeJȮ`/s$]zO@UU޽x7Dq8H}k<^WbQӃӓ\ E j:6mja)Xm6?g[;GvRR7S.>ID9s84c=5}S9aݺOؽ;Dch:>;m@dguL =J`}} ߾;: Pp_$lm{| :~?dzgZ4'So @2Co½++ib 4m:ݡDcMG(۹iLKkѼ>f+-~XMɒyyoS,5m"He4s 7qĖ-t>wA^{~2X7}4[/L.(!(nxehbg8+L*Eh}Ķm8KK[%~~w$X,ܛf.io%l@䨰ZdsfNQ7o*q)uzFU-hTԡDڹe^5dִoҎba˩(cWYRp/~a9~4iA9gnN^iޯaU/U9ǩql5:cɬ)in_G|XlCIU}Wil fG8Ch0nB#pp8 >%K4#rl?Gbc9h y8TI1&N)aH ]Ύ\qt-C/Μn<O8"Za)CsHU D[3J_w 8uE[kjF}A=S))+qb' Q=';Lăh`ozSE~^8ѮQF-/RZ9[ߗIh'U" T!јF.,1CE*E(F:Ksfϔ4ɐvu1tuM [*jHS7qn'MaV_4+k)+3%lG̚{/tشq=B93ő&W(lBP_MfLg.,C %*GdT*%q$hv jq>lr㋻h*/],⦛#߷"qӦq^nyUO*HK9u %dV& W"F0۟*o~L7C"OD2bDP0p:!aSi uQ~X6w:M}3rA}E,Y:YGiz7iutW !D  7@%BXX VNIPh GuH۹>LJ&H`'YHCU)h*N6a8}.(K/ZH<GRN7|˾J'P4'v%="N*eV/ 7Xԙ5$ \۽(uoUyD5 V@\i'$mt,n+ok_'9AkkX 1d|f iX,m)lgNg &r B;۹5̄qaPWfzmn;4UvDIjV:H# <xRGNQQKYQE iL@Wj$^aCI PJ\i IJ!<qF p;T3"o76 i{26J4sK$0Vv[…-D(~&ͅ%9(#(br|+G 6AgBa7nɻ݃\18^x2Ů#`x]ǚpm͙QG"EFNyfl[fe)<~sCH娻nJ]E'vցj㝝tRERBOtn')fn37%й=} zAi}(9[Ĺkv&`C_q[֓-7h̛Ampx7l0$8ӶshZ:kI%hbhH~pU,\tpR&d{cJO\ʓ';aB:SQ9aOCulW/RL'Di0a έ#}#6fhRkJoGO c -^ˁ[BJ0?L!`R,ͱ^uJ]Zj΂_K2A 2b.I~#yM9[ JUm{+: pr>I5 2,].V4OmnspL)$K9],E),/BJUMZ,=kddž#E(jTUu:&+>1Li Èu]oNi\#|lҹSsAP= )#t RH*lT 1E/ ;}R`3:$GטRpF3 ̾. -Faτɟ/pW=i?bg5QK n S7%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mk.png0000664000175000017500000001122412625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxYuoef!)%12ǔ%ہe" '0Y35_  ıExe[lmdڸ"ùK/uPշ16(*r_:U 7o!V &0A{S otD@|5fÝk/YI@u9a=Сx"-}}F1@U:_8 Ͷ6w;@ACLPU:~mҿ=yȁ xx1K4_X\LvvX1)x ` B8ʲ1 t]V G|WaB6O"~ZIWDSUL~hozW+/Y~H=54@,nY=Nܡ3 Na dNhQ]%٣`\]"L^&y}!@JFYG >/| tտo`ɾ_\^?<,1s) y&B6a4xLl\2*qG Qs` f >9oV[[V0~8s1+|уϟe*+\Jw D``yBB̲E2N/Ă=0Q$ +_t(qA@ΩQ%ޯoSL^=m?!tUIJ{%k4–B [vse)1`)ބ 80ƕ1_]=g]Po vMC ݀z'=^8 BA4V`ʶ )AǍSÂ) ďu.O _ w/Y\IF5B\;ޡ(k4P!.M"{A}пd[_ _ͅDe}*ǥn>w 5(=%PJv\60ZkѸƟYԅ5BAz8#4%;0qn'T6}⫩jSpː>!hD"]PS͍Z[ x_z{]̺ @G{lYٕ-w@ j6D{׆5 $нlJr Ə/xX4,[|kzNZ2^7>л:5(/&?PJp*سJPо |9VlQ L~P29 OfǺU'ÇJ(A&HkJqjvn浟|\ڨ(4 8%[/RҟCn*6*HntڨRp 6ҹV}u1Wg@1Ww] !d #hHJ#;tw`c~u5Ƶy1[QGBʻ4{7ޝ`QqzJBߞV6Btҿ W_8^][Rf](\7ۀD)~_z7Թ@`xlG3HYtYl7MN)D{fK):њvU޷\oj%s]iCEp!D:dнkkVHIr9eוA{>/C?t)XHU`!mU7nZ}8Uv  *Nx6t> J9.DKп:@`v4t/.1-Ou,ԤQ owf#_yPlbW'x)ng+}ǂm{Ueua[ "E96 n 2׀UEWŭ )h,ЧF.G`/ P7XTwvzԋ#%(,Eb*@%m ەM H/V%#[:UqVbWkj&^71<%nmk! Y%RWҧuLA١e0AY<ɔxMsa [a"E: $p ^_1LwAR0n` ȡssg8m=@0YtUpP/VkTȾAlк ݉  q +D*p@DCcՐ n=g.:WC`AtqBкMu1Wwnfϩ9EJĮh؃[r ^ rZ]Kf3DӨJ8V_NW,_nxaVe xrfgBc^XziLj-Ͱ76ZA(@SEG=UAR78CHnv2;3ra΁Ώ*ff2P,HgM߄ ݓt cR \Iwbj>d.nG ՃU;*y_Y`n@%)YOc0Nb[HMpu{3toYe8t?t4p(7, Ə:רZS {iL]XC%\IJqBFua0oV[VSp t/A G+ an;uCŞg!F߁e`ZG>.7Ձ:BʑIfU*~u5}ÁSdG ޯtr,5!{Y_40eV|5,^ G=1;u5S\mwOfp @lbWT R:B!W1JS}EVʗi=ϳAO@B.x KZq40 .؍sp润Dg5;ޭɜ~0bvi.K9 8p |_1T\|,|mh5H Mq!\ތn 8R(ā87ϏTo]S_딥WzxecOhlbvz,+m修ZuJڐm`n>}7ei)/( w hg(]eJvwTRSX%Xn'=ʴ[ SF_|+w)]!5al\X6[3ed["1$\$Eh/< j8RF0j咶O@? ڇCX,WCtEl~` \M EлO)ڃc $&?wHoBK [VUe4!w&Q7. } 4 -^?mš}rn~] ޾`G2|t v.8iy_Im^ۜi~ZM7W(>&߇/K#{miȟ=GylPo%](bl^"Ӭ]ga` F6@ F` *.ʙKpF+za:s>8l0 ?d~'Št- f}>[ݏ^.<@!˅wVď?A`aSL}PX.hKYi.3ַꍯ]{HOGb+6;r,/!` iF/E:&p9a"^qs0N-6,Q5sQ*LqIk9aƈ?Ʊqca8]@P}lTnXFԿ{LUla^fI}9o%7dq8Ƨk9%tEXtdate:create2012-09-26T20:26:02+01:00%tEXtdate:modify2012-09-26T20:26:02+01:001jIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/at.png0000664000175000017500000000367612625666430027151 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏEǿgvaYX!LTÝF,% !@.0'Qk nS3$fIh p[q0x 1 s@|\ʣ}֟ozvNda >*R@ע>/<g~@$~^Zz_)?<|q֤j"J׹}sOk<^$ĈH`tӹzU[4QDǺDIJy`^]ϻq[_!`i !B=ǙpY3UXi}8"B뉧يW%!ˆ<(a8ưkSAJe( kzƽt@ oa%IXc-ʷyER 5lr u"R4BE|F#զ>8"2r3pc1\L ;w~Sb//>*%`gg,>:ͨ 02>Y'DDUP3=h}sJ9@߿[B7%`֭h=۶R НW1 7{Ʝgϣ/,8]s]hLa[!񭖴|tSs~,ml*b)8XNO]m lA mm{:pF33gz>Q$Ic}ȇ@i:I/_ 3lR*eVS'ibkaR8,)Ks8,)KsX"uf$b^@OH_vw67 AE h ,w ]C,%Qb*5Y4VXTH6mR˲.ȦZoK_,knf(Ukuw) "T\uq t(VWm}Djա)x&˪m;in>8sΡ3 5ϛ9eQuN^[9ut$L[PӞeS&m [6k pB̼LsэfƷw9*& x@Bk2K%z 1HquYBrW4<%zc^>TZK\ΟԚzTuZ_Ӣ>S!N$CP3Сi4]6.p-#ޯRa8 a:ٰȃm(YpiEoZYy(:E]lů0)`R61Dx¤ kY4>d{͆vl026Pcx;-X a0Չ@MȼlӘ ȜnZ[-$;v+gŶBw2ՁA K9Rm%tEXtdate:create2012-09-26T20:26:20+01:00CB%tEXtdate:modify2012-09-26T20:26:20+01:002'>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pn.png0000664000175000017500000001604612625666430027155 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>dIDATxypUW~s}iCHl ްN;5L2T%LM25SSNW2N;n/ ``0 "X =E9}=I6Nn{; _oе E}ėvL k.Ih2L t4.0wtҒΩ4AԽJ)Ƌ"q6!@ HO4F6a5}A?8@h%<9|t2kAdm/yP%B)L\vyO,&x}oPi;Ϭtm/.]’ 1 <aSբ< Gw'RVMMݬ|I+mLS"1mwZT0v6>TO߽Jn5*f̤.?DDReA4MahՔqLLv!?L0Jq-ZkbWj=555[|ܵM̀ (`gPjBE>/.n9'(quSxzrӔfQq*8%9)>θr*QVVFqq)Z%{):uǦco ?xZ5ŏRz (hD IΘXk_D&ϡ헶\Yd> |I =lO`HEUfI>ζ \lGܔ蚆cKhvÆCӱ–$"@:]nQ %FLikU|[b1r*)$EV:'ٯOkG=2NeʣtjFL_+?e֦^=x|Bd;q?0()YTuw:Ha<HJk)QHS)CGH@Tqt=ݦ)L:sgUƹ,~4}DT4O~zک}FFϛnwϰh+=Ra…0}@ҥit/ě;βU-+c֖ ڃwagtYG'0Rw./oi`퓵S a$HS4BK;o]#7I4tFg!t *è(`bOY[ Z.q^D`:4`BjX׎]ysneҙ9fmZKýȦ.^#Q twa 䋛XGv,.Ӭtb7 nidz3gB7(ڼ&+S]&F/CאRR 11@$] !+Y:?C?O 5]$҆==g45jؓyK{iF$MDRUCΞ/jiC&Em^f$/ws?v>_]K<)IJ xpXن4)B͔(% 4!4 fO | /f ~oSԭ[Ih>lHb+qoYC-IiaK$O=N) ,v ?K*nb(#"6Y3%\8.y]J5mJi]J"MRZ30@TTscs64MKC./?~ONgFіv}LZ;HEQbw)ܰGsyisR} OLüсF|y>ԀsT ׯ0Z:^)iNzRa fX$DxC|:ٟeT/‚B;wv5xmR(2^zh7۵bB%H G$JnͫfԚcơJ)LiDtuvOoƕP~?Fn.ib躞Yr' 'v7 ܩ똁Q[6/4G}AE$cAufB&2~Hd[Fou cE SY;;TF@ݘil0*5 AtnJDH4"_5)2qv;h1En @_{L%Pim1CXIi[j*U 0=!2Y'钕7C*I!(]%vu` ` nJa  )EcLnDD-##ۥ8?.{PPw\AiU/@ϨU@Ùa HBMJiQډ"tr&Tqa !X@;-i`B@ g \6JIԸ0̽"gBMGr~¸rP^XQ@my~ x`T2&PXXaGho4Y4;vj:+bhyN&TVNEMP.4s24qѰl%3~6h>_ʊm!0g6u!z"v HJ…]D"4=_xego s۸rt GQ}ͻ1Fn DZ,YV<0{A>31,RU,;:r𰟇oRVY7yRN'*1IumbA?P8FBtoday`v%<55k)).a5|cn)ε+9O`٣Ѕe(%a |_2,Cxגn Wv8چFD*%4tBPYZ71{Z;>X$"?飥c'λIɸ9̯쫤I>F䙦(9~|~DS̩'D9Àx,nRMdôWDq я%,U8q7ܲVQ?\]$}t]֔>M+ٴ7~̾#WHŒdd;̈*?F^nLꧦZR*%4k^̌qbDd5aZq_Jw}J 4a7MWPo]Nq. ]N+ 4bw{}]/ZCuR%%Wܴ_}^C:N' ٙKn,skZjn139~s+_6}]>kmO>?'RAnAEI A~kرF$EIOr3h#۰cX!WJ#ߓaxnO-HU+NV!PRcSh˜UʖPoS^{SIMHsۭsYz$f̛Tk%B$/Y11FJ?P(+feP_e*=0^H.}7WR?$,fUPڽ:+wfOx"lFCs+sˉOqh:@A/}5=Ԫ5|*qvhcI ,K)nOq?\3ӏN*g#J(;] F>뚁uS1 9EAlyQv2*z}'r.q Crh-MUKQMH)8_Rv^p­l;3͔l2R1B1$Q춱b]JO80}`asު XN8X H&Ȉ"}ȝZ |L ZK /KRJ9s=\-gy7xi ±C{^ xICyb{D d ^],L~yE#q!NKu`Fw$YS ]\d,d$DxFɆ!9VαfL1˗\ k",c"1۪8)!C -Jh؅RFZj(!$Qi5m_)%F%IKT2ssHCv1 $ZPl7y'Z&4w\Lv,wT,aF4_)ՙ&fO,%#+Aƭb 1 l:PRzoc(BJ,_dQOQ؊zԳRq9|{)wZ631DݧRxGK] Pѡ_QIE~b;jHdajRfɔ._yWjL00eDǁR;3 znRY ^#"b">J0F2chW:"p:  [nx7Færڼߢ]5!DxA4[o<Ҵg$9F)U2C&p0dSTrʾKu7"V4;a}p!S9 eZ o O%iE~zҗMʴVK@sߡB BFd4ϽH~#, ]$aA0XXэVo$GaG=B!}YlWeuM= )%tEXtdate:create2012-09-26T20:26:24+01:00 %tEXtdate:modify2012-09-26T20:26:24+01:00P-IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ls.png0000664000175000017500000001053112625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxipU~{ftߒ-K'> , ɲY˹6EjRC*[JU>\U ذ8^6>!ے%Keu#/{g46f WgFoAd6i~hSY Ga{MB*[zuދ/xgMM~?'"#D)%l2}o7(BP8G[|)24@K{y/VWW?]XXX(2J_x]o;D-.^ PQQQ~iee(** .Ȭ\-qm"dǚT(/0^\N,xZ&qY~b |‹F&zۖ?մH)9vsgE- *z^ɏ<)0\xD\&KЏ WPC]W?_Nq'9yŷ^(+|p`EV']U$"ZRL1Niz,h$H_y.GQ}V-Fm# 0:؎EPW Ox=A^{ v701AUDbŗn ,\FrcE/bI -9-DANn1V’_*8CJꚯ\X`*L\ M\sJ͒4mfH)@bP6g b" /b=Orw^z9/Kh:Ɩ-#(8\&h#Ҝ@?= rz NqTOOi%Z4D(8c_K;4م.t@Q7Ȕӳͅ Yx?'@(>09OݡrJƆ(,lN9#1\K?"WGb| @ {.!E(utm[ٳoxUGU7 ^(x&u2 ZgGyVۛ;?g| ErV:[B!g{󛢉A6m؅n4@8{2.)af Pс cC(jQHx&] qZ4~Eibx􆁋=i5~gB䭑  QV%_ kuB%[&$" Ð(4tP8}7̝܂/ e( VnK "2H)}CNPCk4#!i>)%s_/obE$׆SU{'?*k}J_x  uld L׫ΓBTL[2j`jkx?BQYȋ,\m54 ׸kܹit-B4k 9a<1ް4YEuA%V tP(>$?4D] \iǪu)`]gu v5p|a)8Rvv䕎F)bVdI3/4Nio>B˙:6vZ =F[~ -PyY~i;5DHl1~ T&BSv sWo.&24&\i!T_<A]eS|=MuN 93(?o. =4h #llȂ']uO8]xv 0:pU랣zjIs  pwJ{,l3Q=Ι'avGn=@5X Yh5oxο86$|aՃ@e,[=Xֿ x+e˝#VhoNR航avMV5/q$pl#K~ҪŮa us4;/xg*}冓v'o C̖ k Mt9sDy@ioI2j`A[g^Mρ/{2o}2Vo4e]Q;$@a;bV(m@$pHArV wqjg)*qKC!{ w50g )9yW՞%-x4\ fJRu򿠴8[ ^N|rzґKr?J_jdl9JarkJT+ '(; R Y}لod0Y6?L&1VcJPB!,>3:'8x9zPvFw"ae albdšm8pɿ'aѧ9vCJ.ka7-C'hv c:X ; z$pun iLV.t5Mcl=ʦMq N%%D"N9`E24qh3һr50[8x͐u=bWE)dzxi4ޤȾl Đ/hAbVST@Py m>d焖~xYT8-q#ʚZ9z b y;3</vF dm9pݐIN$*Da,*dWZeLog,0$'.-utX`H &$a N¢3s Y%I3rC}'&}N]pA0cf~q#a;EAJORa݋M! f2zJ/7aJܹ꺎ihH)m"z}7>g:*LJ&p4ly{ vS~跻 4HS)@]׉bb1Ѩ<* %|iUt"JR~LsL۞Ode:)}oZك`0:55%^c<)eԇ)^T|Sy7Qgz":,98fo\DEsg/ ;^RRR( s}$Ħ8z.?9=fqw !nQހ k.WuLJ3@ بgbM5h/ ڱcNIyw-.swzS=qv"1wJ1ӻ"M5*r|;N ^5 ,۲eˇH$'|Z ! "7b% `*gP '}ߩ ǀ0 SJJ9|UUY_R+!% V@!Dƌf];."N^U ςXe-@:_.,L0hbɆV  1UwԂcH@b0-)A$k5bNBF@u1LhStgB|sUBݱdCY]EY#7CO`pʥ~%tEXtdate:create2012-09-26T20:26:13+01:00%%tEXtdate:modify2012-09-26T20:26:13+01:00x:@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/is.png0000664000175000017500000000701012625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> FIDATxϏGǿzgg;^{Y~)DQ" r" )B\EPN9XD!BQ A?7מݙUCWuWUw̮m4?{zu5p7Mw6]{'$"q+Oqg9hS:htđ'~D#""@󊛩VbGp$.b祗iu!3<<ǢQuBԔ0^OڞXRX!Tc@ƋcҠ@ !<`r o (Uҗy^i\o4. 44WaL+Ȏ7ckf<&m?@OqsUTSo2~Sr+`Ss`l@SJA9T?R ju|Py!x-|ٍM|hD;Y8B=ڌf]Z'ytsd^J0Q+뚪ֱQb^a!y_6̅A cSX[[àGqGqߐs(d 8M#~vF춼6H&ydΜ^`z+>MՓHwV sݴ[34Eg8ŋ]rp*i!M2 Q* ꁙSusn{ 4RJi,͑9T3| &j+qǀV ObschRI& g?)¶@?$%ͳ8-l \%+e=zQB':wp$"" @_ ztNfñ=%_)sĕ6x(=4`VDs< EbCc + yS*WWVMSAV;\{%RȄ-@P2V܊6=~˳wnTkr@AZJ5̝^ jHk1 UOFsifwska{G^ȦMW}" aV-V?qȕ7w67j`Q6yyD B5L%fӵ=~T*GeY#S#hLȲYAɢLV^Y* DU]`eB (+vѰp"i`$H~hcI!U*/[J47f P$ iE+[F#&NR9a<^c)]d~~MTs&>͇%DYSPvc.`=7ysޔQmŒ "9 o`:a2aP\d O| W'0ȇ?K6rۧ#6_Cda:ZFr IDATxMGgzޱq",%$H !Pn8\'$rD(Ʌ+l) c!۲7=;;=UCwuWTX1UtWWǯ^{xa t>$ D>@V@@ )J@D }5ǏcA6C坫\$kơyOaR:QB+eg?\3939Wz? xw֎\YY7_lWA@Bd `9FQ= ( !pb!P3zA' 6O:tQCl 1C3#chtÇ_/}:QPhwp7~&ďV;ZZ^FՂo d&n8}" vK YҝI]It `r_?ʊ8Qle:Fo>!nc2I(m j$& :D"FA1D ;KJ!+:tkg5hK)H7{wU;+BDT8sG cs@{#@H9K' 8@R uRC AZqm!0Gt`U8tǓ%J-J"DFT < W"&-+MNvNlXyd3$I$K:H\KRNiHva8&#r+GQʘ4}lutbn36ªHhx*]#+*ƦtRq삫Rγ.jt NlJCp\̖V'4SvIK7;5H)'aɃOkL6wk`UHtMhrU@K@SMhC *'U]o6Mv+f@~uikɾ֨SsxS"e\MDIÙGxu*RWVgU{2|mlV82'9K93APde,*Z)+rngέ53H0D@8x$ayqV ؒ jKȥN&!s~ :VIPռI8 ̿:l=cXXYA݆ ,HuG;'3Z-]ʴY3RЉNh!ARdVQߜxC`F:Q`fS"U\v!™ZS׹ŝV6+/^}vOn2;Ғ (Wt/afh;[x|>^e߼_?(X?u.@wC:5c(B+WV $ځ@+3jl銇y-Vi@mB) 5oʹhn6Yj캐25ާi>;N hV̮3'Dw6AzH6*&_y V܅7&h}כl?žh >L-c/TX%;N+/ҫ`:Ӫ-ofHk(Oaym=/Y33i_\Aݾ=pu<\J)8F3ʙuKɍ*?;MTա^Nץdf)]g]7K帊 IfVU{__&.'K ;{zs}wƃ'Q2nWxЪ7'&ρu)>D޲v?ʣɫ@ζWIHOI,'d}p 1gtAW*#8IT7,CɰFՉ|K%Gڣά1jd0@Qj0oi (\ٻX1] =k0Ljɹ)Ӎ9f&# aw;TXWZCT 3qV% ,D):qSXF1#1uIXXefIQJAA $X3NbXIIl6ڷRڪ@$ MCCffvX{f5$A4byZZňFHD$1R,p* a+f1c̻/|q|mSE!V׆5Qp0F{ƟԀN a@պ83!dmJ/uTL> Tsߛ? mL`mr0nʕp$`=>U$ջP B?=ߞzpȌ _љ.CP3oykOns++gf:Ξg{\>6d cc)εk z~ [ ekR>vtiD~Cb̩DBJ7p7–,;TuƧDtnkR\l>\`vXBk\dpfo>6dӀɀn/چs(#{>Ø!}tT:H?B p?A#b"[$P||h+J f*\5Aj߇^pB\%tEXtdate:create2012-09-26T20:26:14+01:009r%tEXtdate:modify2012-09-26T20:26:14+01:00HIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/by.png0000664000175000017500000001011612625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx}pT?fl䍼@xAUh[ZŌFG*;cgD8N;:3C{:_((ڢh[HT 6dlvfwsyv;^;9sw^9|!__StAd.Y`&q?$HtuZޔP+"`746Yzw_ul+./H?(_xAwH~ yX/LO H)2H״wt 8W@?/]w,DDqy9.G9y藺Xt"Bl&ōr'X4UE!^}[oEDDhF pzaʦl# 桇G9fXA-̟mp?OEi)RUc ꕍ+)Ě(*Lo8(vH-v8ϨRaL&/uCʆU  q1g'܌ىi\z- RJKɢ̎%mM4R!_jĕt7tuoܲPf =¼3gRf ?TUb>VYM.2X`L')BT18 x1s3 W[˂ov(!tMO=EI.&6[* g6FYJH3#X'2FMOJOsYHUeYl.?fYJJbJj,WK+VUSCEv͸Ygi)o|JlN'`ꔙ; 5%g2k_hO\=S8g7s~6==޺pOr|̕L=5T_QA~M z?3(lj"JDΝcI,dSTw,m\zǎ;xgy9;ta.7n:;⬨H2)jN JZ<+09mmP8>ΌGe`oFل;;߼݋'c2s$%Dyn xVX,bi6|HEQ\LrYGJ-[PA˗mo|i:'H.+4D, ,VD*];v]nz_{pWŋY.mԍJ[8Y9J1fK}7COcw:\w68q+|]^/7 %m^s< W6DK`Wm-6Q\\LAC=;w2z j(D÷M~e%+Wr7+zJg|V+ņHZ&Dht :o|{aaB}{,^|wc#3]_z MQ,~2a!Dkśldzi݋ tIWJus3S?g."}}b2ps6}n3x ofmTr&.<_.[ R}}t؁ oi[ߢgcEak)UrHx5(,ߡC6n$K%Jn ߟ{5k(5 T:X`2n'3]+efeLsiK :r(rD@J-g% ksbȀ&3lpo6W@ܹ᪭!9 ')Z0`OeDzY5e;7I,-YVA .5kעBD=Jʕt"n9iIkYU2YM3j&C%Gw8G?=g,.wSz55s۷oB:WKSLLbr 2&+DK8Axͣ([&с:6Fx{aFOFFMwKiZz7g /Ӑƌ,hI`T}76mmmHkf<R8pEmsa i&LY%MM!8_؜N[oeM7ѳkC.S4o^2ug%(a!Brg& urdZ@:D>`aFZZ#ʹ^֥ Pejl!Bg:H]K$R&wD!A$Z[xw( KϫлkU݇1rh:)"A$2i=t=TKz^!M"%c +pBý\._۷#v ̡WӟR0s&cg,{ӄ&1W:E2VsD]Ry=qM.qI41ذ+$;>2Bkkq-YBǹ_p_Ӗ܂ᵄ;>B4@Qn">&LQB_ Sᙑ&u6I$P3:!~8ESTYChkMY^gV28ǃ&Ax{}5Ɣ%y *Ɓ|RRCWU0BMxΥ7A4EA} *I"}r} Nw^Tλx1CGPܧv;tOS?M^)Uݟwv&Ls7m2N4{~)Ί *_ep6fƇӆ&ʉuH\Z+&0:< n7r|Z8  :4cwb?M{/l ˘Վ˾<|Wf|` >rWu5UcQX8M_}R M!7x2LɴsL"V"O?ʵk+/v3'N>-:߉ ?e<KWY 5Lw 3ͅCx<&Kҽw/… y gJnBbCS%R%^$Z,RiEߖ>N45AtăHxtd^)6aVӧsW?`K OL݆ x,aKΉMciRJj&[q%T#[2>3'FbhR4 -& ""B0 Fo5-{hCfUNSP/}1Zb7n#O=h0iXΗ_FQ|\سol+\z5*SOn%zO',_MN;~ Af.aon\ZJw^=^ʖ.;#O-!Bj[o#@͆F} %ŅP] slU%yTV#m|dDD#/w-H;E ѓhF(@90(H߭jlŊ') /N^, $:19Q`,Azt$bEkbV"\dZC|F,֍#-Bja&K6}^/! /TueI&*  .oX%tEXtdate:create2012-09-26T20:26:17+01:00j%tEXtdate:modify2012-09-26T20:26:17+01:00y7SIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bw.png0000664000175000017500000000454712625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx͏H*g&,0pBˊ7i"VB qȉ׽#Hh e-!@IBcf]/W'b>~e^ {aq0iRH)@[i~GrhJ3Yh@_ym}Q {P)Y򝕚Ҟ 4)m~Ԥ]4D ""{_?. ~xgK^{@kQJ,p6*ǧRprpCg埼O(YzZ];g?8}F,/Tu>SpJZ/I!`G`{ħGAwU,I7x5u̷ O/ !J!M*o4ueQBzm#* EcΜ{+x42 ԙW,~J>2>%@/8*]Ys[!jVo RM+ @ {jai]|i3$ M`ГHCQibwuV Eiqz"F$hB`xUe"Ԥo$u_3s|N:ˈd[e'S}`) 1&CTsmu,@&B1 ro<)=wrVOQ-_ ]*\_e}(jLT[;)66muU2}N5L(?y4LkK/ܿUkyM7-Xi^Qfk6)T՜ /2j F0Gͱ+{; ݬVU-|cץ|}rN:8_XJ~*q+=1ϗ'&Q##߶kUEoݫyS b\P>!`>~ aTϡ`K(G J˗|2׮]dp:_rv!{/go1f:DQV{?fgggWUΫ 2ImPUWh5JBӲ+.8c ZI~+YU>M1M{_Z|0 N-Y**FauuZɵ.hjnM+=^"iY7ZܨU8<_^ jҌիblr&js EMzUg`F=_mt_%Yϫ瞄x".\`mmOwYzzncp8dkk+Wpvp<_tnJTj8b{{[|Ć;ҋ@OƸKs IbG1;{1wG ;Ὓ@/3b@do=rz}2j@Ɛ<<)_ IO" 5 iGDz;+H)ˇj]S?i$k=Iv|L % fVnFۼyJM $I,/L8]QJ)@{\\zVW+=mXvmLsx+'eih(@B>978H+/ Ӗ`0|:.?û;rrZCl[کjA`ք wQls=I?m0/  j)KY8C/'?D?#~7)0Floywi<į| {f[_4_dž lAų6d6e0ɾy{o$I g m>A\ϖ2ůuļM2H#R󱡐^(s! '>ϝ#`/c<8D@{3kݘZ>^O҈;ї[ k9y6:+?V5<º6 ?/b%tEXtdate:create2012-09-26T20:26:11+01:00k%tEXtdate:modify2012-09-26T20:26:11+01:00+iIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/li.png0000664000175000017500000000703212625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> XIDATxYu[[/ݳhvRCJ%ql)Ab ;ADp 8@ȣ#׼H81"q` K$"!5r!gY꾵u,L`詚[U9{ AyPuν rsLVg pݯrZ]gfgJ?9;<<;ovA5JεўI@E$iG'O$ Z?}p{=,YQ8^yzߩզV*U`AĮ,K/߫Z&}X^l? j{? BY6\~h'c^ZmD;b֧bVTf+IX)@uz}hPַ>ǯ{4 z~24*kX jQ-Qq00 BG'ZJWq8VR(( Z-רUg_X)?nۥQ.p=Ww m33Gc)cPV,DT խӏ& *b;ju1=nwGب)6P ja&P&mN|Dţg.`;ȝTg'!zǨ-GXEae5?+^ ڣ ]3|*^4CX9[1%c 9lLS_?y)X@xPND@3&Pu#i-lM\MDkr[Էo} 0M=lPB˵hl1pmq) Oo[̃keo 0CxIˌa,*yH!^[foaqLc [5O6^TSLM=BnzJTQj+gׇ-w}%>J%b!X)"q"U~0TE*\ŗ^abu//=[,|FuQؖ9>sgi4}TN%*$߁eKŀPssO;~I`&6qc~=egxW,S)- 1i6}~8=?>6׍cEAE2,skܡ\vfr((-*((-,ر*#EI :m"rӍl#@ \R6[[ KHJtɉ ~Nlw|PHzE)juAQy/`'NK(~7O09Yec<##%,dnnKV99ʙ3(U.\A96)^;@5vI;?@iOlgF ?~Sq8;ɓָ{wg9FZdluFKjΞd ~ +|Ķ-`1Kc3ґ؈fbV$@BX_̷y,.Nh6 }\v]һGOYXswBqL9&z Ed~n 望Z͑S"^j_E=:ǹsg=c֭meP8 M,/ǹu{ٙI^}u@©XbB4=cWrnJ חawoK_~T7fhȉ7/N355DXgnnJ'|_./](f(q^{/&dIm0EF}qhi66,/ol^ oe؎I`qj~ʸe)Oϳv,F,\6`KEA P=()X]ĉs曫\Y`mmmF hm8p O++'-!cIK@}?zJϵяxeeZWa9%n00M{wgv_]ٌa\yzn``Bn P,.W+/QF-D6Pƶ JQ*)Fj=Iv@rɎKBfʃa1$ (e&LDRBy"^>H< Cm&dZ]Vɑ=*_2}myhʅ!lPFqf0I{Ԇ}]gƤ§:< XAp!S mPܹ%8tAf2;-2bҶp*PV;Ħ%G)/S/ᾈVSZx{W%$h/퇫( n{).EtE{<< ddy"( )=OcE}[Z J2Œ+EQKK<'@]@N7fAu姌IiֻG%/O#PiB4E­RgEr%'7ȴe^Psb޽pYPhbmI=Y>\.n|G(/S';PH{€cW_;ejI\UT\5kⳆ{$/XЋ,&h9S,;!(=dƔk]2%Fᣔ3$,X֤҆u*%v}|+vZ_T"/_tHޯErʽ(ko6%tEXtdate:create2012-09-26T20:26:07+01:00q%tEXtdate:modify2012-09-26T20:26:07+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/hn.png0000664000175000017500000000566312625666430027150 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxKGկݙ>Z8BH@P(@H8!!ADk܀R !J; !V93=q~c=Ou.WԈDyH#fY"t,M鹄=UJw>|YEVIR*ʬjFvt)$/|zxaCyp)jW.|wy-)}ΑcVgq*,dR!yTL]UZ*0D:iXֲ~c?j )U?QF6jm(a$qmiYeJkVm:C%2םue|>r~F`d}%TcS_V eWf(E( 21$Kh7 UUG6C$q `X&VE0\אGy򯯐ӕ,.O+Z($f".[fX*Z-f5qGjBJ`WA$A7n"*h P@0VמSiQޤѸ 5L< %6"@$(P0 Dj@ Ҟ ؉J'qEQzH Ix7BDqq/f!,$/q2])=fE'pb$t(ߐ:y !46C2| `yE*8F otsFt8r##U2SP=' S~@WHpaū.[X&t{-dx}j3x)HLH(Uh 0Kf?E4떧6^<f{KڎUuDn.Q: O Сѡ^mX elb- ی(rmEkDcV9+>V薅Ia AsP?h/~n{1Qq LCSW\ Qrq6^΍>벹y,W9 7u| NA& PZhc^hiVq%v]8Df?-ʾV C]ͱWhxz|EsdoZU,Cq S]xYo}^iff37nQ-W &-_8v ~=N^e6Ht5?{e'=K 9~e\\ eG)_`VT6' c& dt!T` P8~)&F6{*ݟo Tvb 3䠛օ[oq|z wi7Κ&^:!Y[.Y, |=sb(B2R鶢9Q Dm3d<\KfшB77i{cp[ZF4(ׇ*' $2qR,)͇ <,#׈벓}?Stt?a8!I|?TTJ"H5H%Nr^:8S\Jf"+_$d**/7VixxyeӳV:>uM ,3o░1 + PD$KGN-QJNl]wzZrM6@w pDE'8~NJu"?S'!Dн;-P4h  NѪ$13)~A%^s7q%@>h>J{vGx'[. /C\@HY=--W()hխW=*-5D8(rp[w*fl1ˀ&P+}]\h5v({^+9&R`.ݓ-+$.Dm\Z3Qo: g>oϿe|Na:QC&@m-:wE|] >8#ynI/1ys%͵mo~ cü/J$0,yCefAkbfAQBcâRZim.YRHdS!*!soP%εFb4!I*,mc`^ Iu@!Isvv6hhE@Z%|VZH^~ˋKTZ)8^Q5s&%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ke.png0000664000175000017500000001043312625666430027131 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>YIDATx[Uzs/s5xf,5’kH])" icHyc3#]WkڋdzsN֬Y[U{< @czR\f5a-DGr+c {exx#~JA1r-ٳŖX7b&SATiV@ZT*s<`Ww?`4L4722 /ZmEl[+τYkȑ#ݻg}B!Au;OnϷʍnLcXn/"wqG͘t+_j|yuIG Ll++u֜={ .OSO%Q)#x=ݲ]=\.ϳg&&&oJV:vsZk^qgyoM|wӻsJkAvnFnbPv;}8r+v#ZL'Vۆy>|:}X\ߣR01>ZkIŝ01I }W( Xj(tsRpewc8L0jXAy hs|Ct#N=!0Rg9^~=Μ=|W=NB ҠHV @,BH*lXJ+37%> Audb iP(2}}f "* %FTf |˅EfN/ea%~ mL4`nǦ'9Zpz;AFj -ۋ(7 oNV)M*@anx_a\֘hAO Bڌ|d֪\\&k Jɼ+mi\=vcYf:xb݊,w(1KHt'=J'G8d$@$f,b ԲoЧI2ΪqqLI4|K.8:hhdm78W{2Ib,c\s)6s%S>$ݰ5/@44b<&%tD09) D 3Ku*˼o9˓OɁ/CRцPۥ\e#n:Uc)<7JVú疿z|w_ #T<1B^f_xQ]^ZHz)nY<(GLv (j~Ơe=v1K=#B=5 7^&&L=+]9'!cq #! A96VOjtmS!ZcD"cṈ &M是O:1؀J7 Zei>?,HL؇IuO[Fxfȕ&7D&Ҕ.JJ#B:>/T>>>*(*'fR/HbYWi`I:Ui4_N&'lJJS1Be׼ԧQW>ZڦglK[-Ғ f4(Ԝ;I(,L6 콴i`У-zp7e)qy].!δХ,z"ML{Hy mlLK5da1M1ti N )Gj ?vO=ޅT6q1ۤd::,VmtPÖ&FڱQ-o2)?h(OTلJn,uy }p/7nz8pOH>'_+Yθ3.2@v Z-.7=#6C!O|u?Xc'?-BÚD!wJov{`zws~(ǻ1\J ͉, wOP4ɽ۷؟&SV/]aroe;IӻCOg` iuQ s}˺u3*:0+I^6=AhGtWg9 *(GmzkλM2R9CX?% U4o_)hRe//s;eBYV*JU*$D.y9D`r~].Tg|:9ƱǠ(ϐ #bhlwD+ H?^}}< e7Xf˭PHU2qM`,̯qM4O5}f_^>*ne.w^E ͙"#?|m8S;V^12{qYZ@Utƈ:&'yz,'T{OkG>O2 cѠ,|1 s﷞ OyINCY5+̮py^rDX2D/p]xކ/~3No2<^XY{H~C7<1~寨S%)^8>bYЊK1^0+X\0ǁxӏ+9Xrhvb\^6qO=\\d9fN}/^PQ_ <2"ccaMk!j'KyaSJZ̽vVr1ׯup+$ @ܭ^ fNc3Ŀo3AQԤk_ƶ6TBgXMZ;;JB_ %ٌʝȻU}75šY¶FO8rh  `|ߢ&i_{≈/*y͛r4.#EoJ>%LvmkƊ(OEnhƨjy((W7мdĮu{mƇ,Ws:DzMf͔9[,|91 f1^55m/gBz^3|wia@oF.~ꔕ_"® 9Toˆ8 % 0jY.%E^$t_`v?AE,!ѱ%16 4\fKE:l{ayOLSNב]1c9n "@į{_Blz:TCP◭2ve}WzվXZҹ~%0TU7uF %tEXtdate:create2012-09-26T20:26:31+01:00)%tEXtdate:modify2012-09-26T20:26:31+01:00X,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/th.png0000664000175000017500000000452612625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[Ϗ^힞ٙaaQHA!B"G]qXhc @v"( h4`aY`X虝PUv]ewo{Lo?]vUze-ky|QUaՉ9;b@V^@C@@~IK?L@DDH[%iH Q-\n1˄|F+izt .Ikw!z$I6h@xǾb]ץ>f 13HL>;+R=1\ZH~yrcwvPP@Pjae 3t,6Kb2/Q4χ @þ=_mvc3)Dy0AYF_T @0Cq|gkq] B/M!Y2ƔkZ܍v< )>?/(70Iy!jP&Mɵ엮U'IfQ@RnpY }(Q!rSʍ͌0!Qupb!1+0D,}s$].\}lm!cDQT^ocok}}]23") y2 T01 ks v ^G.)k5KJjE8ŶsZ#m"aw ^1.M^@DR_X%2Uۃ[Ee1 }~2 ,@K̲Sp8}o6D"D !bEP, @UA[c;ƕ<}r.m6xZ^G'kHܦ٬W7AvC`a}v- JڣOՅ "lc0cjƫǶq3k;n6X˅+Q 'r۾lǮ,ώ}~6hVV\Ŧgr[Baa>0C}6X5 a1P< E%׸\$]}le0;u諂獅 n6D"}!QRMBs3\ ZGB1>;64 M =&|rcQqݦw }Y.o4-3. Cfvk;Xe]౛p+~@{Jĺ &?KDs 1] W0m򃨄}5'Z5sz|cX{&FwyH[]fM]CݸU5cى>_tMB0N@cTz\ BŰK]K}vzP B^J2o`[D/cOX (8+1}U^{)L0Wplmm#6ofXWܷsOq]Ȟ#,=mef3Nq||7z[7K/^vww$ P$4p8ĝ;w=HsFIdDskYQvpa;q#5K'deK\m'"Lcn䣏&dEbK!C̘9NGG8;<h~+i_=ǹKѳJwgz#]+Fycm~#!fcf2DWzMomiQj(ޓ/]5-V4ݨAmt@ix >M fw?`64\J;g'~?M?ACdEW[[eOȲ4ڄ̈Mom2y C(}lTv\pG.Ge9  %<(O| KC(.@hm_9}_8򱡉FCQ[ j:*bc TJ8g !?bG1VN'Pd2[R&&ʏͶψ͘<6@r-B ~H2%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bo.png0000664000175000017500000000430212625666430027130 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxOFe'!손,"g@Q$"n9q3p B D$.(D6*dvٝ^.ryaJ].omnM 77As5g28⬋W9bBd }Z͝+ iS0w>junQ]?>*A DX~,?zY~c^rܪ,11$>4ΏS/*ZΫ>l޷'?6dbOz %"E׾ZiU@U<#C͆byuNu$Z `!G"w_y#M5+]0vb7UN`yK(b {?صc;PS3W(s)JT,^=շ\[F:Ax¡KV{xWAC]EA-ϩݷ^&=3] gUq6:x댒ܥ.L՛ymo&_ˬm@퉎̨TBFP_o nڇPU`JdBFvnVM3mϕI{iX1Jծ1e{cyY' -` Z7kE:xWiOkF@=}nmǯ8oƊg10{E6[kw s2@^/M nT=,猄i"O9.P.J|ukz惆 ʺiC `+cS>!0ǘaseA>S偢{Z=@'oZaI O/cWsjk]0(>ZU/G--toAAji<))&{'g{EQ|ð[K~AYO{1Ļn{MkSF>6l[V@i\CuIskZ1xqDb k;K fML;oK<:׆P::Ɂ!u _nUo<%5%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/za.png0000664000175000017500000001150312625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyW}?3;j:˲-,Ǯ36c $@R@mS@%e  B`TFےdZ]C+Ξst_x3=3&ݯ{u^IW$/ѹ/$3DL .|~E@1>{i@b&!4ƒ'k– %ks*b W 'N@] #BXcΙqT)OUjIJcUŀk:.+Ӛx[]C=c "6"'yD/\][DQZ[{]+(o\4/V~t 7͢Xk]"w<kiߛd[4)(2'[_ׯF&[ Ԃ1 |0>7Ry¼(rC<-Ϸ`MLuK6y_5dh혷-vI==D]25aaO:y fw沋us$fDK#~0>6aOrpeMnhwͩqR1ưQQM5 oH_-/ rk8IVy5[t[vdҗ+«y~lv uyq`Zr(Uc-ꋪVS_ }ڟs\O xYsFbn-cX}>S/ujvg $,-4ųh5&ߏ* 7rx{lYTTuNccN_4BM*u~c􀑟jzx%A c#0uyk.+Z/C{ S΄+mWXfdr9F&&8u?y>uYEEH!3r۽Ȟ[7D,<%6]gH6:UL|cfe /E&꫑`b7"rȉnBjƘ M m=/OG.CɬOMcFdS2@;P|`f>!}guMRiM4 Icr4>ݷ f*Pé]C+{$yP~ 湣V-8 ;`*iMXD Q8T]y*5oEJ#l>t:b>;[F a1h q\]EpNq)F Dv GTR [ ?jOϮ^9(t9r)R]I$!aB.mL*%)D9WB,ZC VP#֢E'("S8dF'ȳGa|xt帪yD:>IȈ5FDfEJhdںjvrU,՞ 5h [k,U~DzS}m2:#w.: b-6Fˋ4FTE\* yuSZyZ5tυ_ae%oN$XQDó4d"}Ea (<pQ*;(Ix&l{M蒣ل˧nÒUR55IB<%r SWV6hꖥ|dӍe;܄닑 %,###:y_XqR@G?B:딞S}C2n Ъr[tJZ^-Z?ݲmKiO\D,.[s.7[EU.{ s+ PjxxU?ON7)tpTQkYٲlQi%АO:mg".v{6 hVr:1T=~ Df^Vu-y]*q"n\N{ɻ Ĺvٕo+ Ӗ8w=H*[dBjQc (h%6'v y@9~0Ԫ''c|jaIU'Q,k_n|k)^_8~Po [Ӑ2(_'դרlVW{]UZtUQY?7G[ĖV̫o?]>s2:jA"C NtߘO)T~\m{,z|t~cKm70OpfZUTc؇rĖ]X}x\Nya{e߾PuJВh9]:a{,Ȗ- з=ܭ-bc-rW^mj@G}2a.A`XNpz$ ]LTS9%8K`^W-.%P`[>~{uWC:0|;)Prm&ժęWMuS~[3-H%i91#T:zwuͼ{ֿM4vJ68}:׾69UG%ѼZw0+M+LQU)JoR7m7L+FsW d­SAg|?)0Z%1EIAOjeXIǂ:m u&@*T1׼3gr|!FFl8e&G3yr='J} ҽl^_4HHq+c^zz2O=vH/]RD|D,v}?6OZ.`yK;טEIj*\Ǽ-D6k,TɬOɾ 8 3Q?i:K;МQݐSfQpÛ̌+8ES4LPcJ5NuI ϫsdq.c)=$8X^d1| ˇ#}-0嚴Rz3xz.v%tEXtdate:create2012-09-26T20:26:13+01:00%%tEXtdate:modify2012-09-26T20:26:13+01:00x:@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/lt.png0000664000175000017500000000507012625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> vIDATx[M%7=U+3ә 6AJ Ć-!Bd%Rbl"d0|df~ʇEٮk?HӴzvr.eL79սTk(Yᾧ9yҬY'Z"BTϽP|goj"@ @|pdxr$;ټλ@͑̾|'Wvgt&--#s>frxexOO_o~Ȉ:Ӳ0;"W_bO=+߿~mSc D2&_XdDPf Q(%;ONjwэݯV׿o4|@!m~7+"5v7fёI!S bb<z[d!y/ׯ͓[ [m3@ *圲J%ߤfBwN wہ7HDT,|V<j}X@(-0L&0#>>Skk) 4~S:L7XCVK#a #q⥙pA[&[~.] f~?yX,-:{r‡S-։ܻ'-JEg 9LMt/'XB9^#Syy}6xv֮쭰% 10ow:"˷-Y^im$Θ+O׼\alI4H aIDATxyy_ϼjYI$t. %%& rUqU#RSJIR>qp0ªPFB"$-ЮXcW13{v$ꚙ|}}LE(|@y/1dQP8}糄t$N@k%״W_f`וwˆlĿ]D ~Dq7lj.06lLG};=1MluwwOW;>}Y.-( # . Rr1Abc ZkJZ?C{( cl%ԵmML6c`{U'^+) "UieRK*M`y& 1rf`bQz_ bEڗgR\'vshaرxy)ou-⦎WÃl4ZY(*li Ndyi Ɉm㈨"g6k4ZLn;Q%Pb(Igoeko3 -jN51 jN CK}*ŸٴK,A9:ZM^gd尌L DGfGP Blb&ph+ ֹƈby}1FiMB3/܈T6mЙh;AkV/p60<F&?dZm< V3\v'MuǞf}t*+1r>@mWGl5.r0w[ESr4(qG/nmZ$?yde܁{{7mvbdd\`}2ϾySYEcj)U u:uqz.J o:vnr g<_ O"q``_:|icR=Wa11c)='4\(k. -C8#8&*Ok cwo[,KYX $-4A̤&ӆ^ E Wz| ݒΏ8|S3ǪCi}5s-<]g¼ !3M[<oGMQGbٰv͋j4^|ۧyGE~˥Xض`\_Ci1qAfMtY 8Jzc$o3&yr<c2~5=.%|ɰ]Hj=#Cp#Ӣ?'YcfIB nӦ\O,im]s\6EJG?\Y#@&vw:,| L†&Ҥ yy@l +qe$f&ܼWZ4-2^'^6r\:H霦kby6z|18L@t9v{<"yX :q } JbV'2aC.<<DҴ)VD&[.Qݰ0{CH% S8OK2ؖ빝.| f[pS:f\"ܻ*ÆY.z pkBk" -==d6m;Xښ/;`IRݧLnR6jhge;, ?魉\DF= }D: 'q@0Po{xWȲfMs]0 J7"* q(Q/Bbm] Ńr p @$D%L^$Hsi& r,u^Zۉ7NU+5eaRfY>׉ 0 R]ZGT$qcxkdwzQ2 L :}z3͑ֆ9_\fM9ϖHX91 +TE$ź3|mmkg#G.HbYgӍ aUVAZ2_uOudhΫ5% JC9|^N(͈ѧ{ذQ>0JF(B/XTJ^ ydGW"2L1<3gU;K+L/.4&vA,0<_]jv><8x8WcjMy䴽 80h `Dړ;GZ.I8n'pPwp_C+OyC(lIap;8w~<|{1BƸʕZɉ./-) '6Um# ,T6 9pq>6.^8*>@4ZW#"Ƅ+ LGl(JځKʲNo<)FbbZ.xw KQi-^@gmw\hC3I83fCa_uOPlw$X AAI'R ݫG qTJ~Vz6zlŠ `戮Rxq(?*} OGG%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/my.png0000664000175000017500000000676412625666430027173 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 2IDATx]u.w-ZJcIp-%%7׀c#E.\<Ԩa E>h(jC hҠZ m$Ɵ\Gv$K%kYZi?Dr3$RPk x=9=3\u "9IsL:է7kYZTI3B`{]*2̪~맸n{7rv{i7+<`lN][u.Yǣ*cZCA%3RO:1HWH{}I$09k4|W*Qvx69bUq #[lUxS|\X[YYuq %>>d0Ac%,hE~ۙaA!FvJS-0S_TE%vF~W¿;T ]@ (Ne)"w*okII{j?^TS+?'*% z` sCaKʶK[߽_reE{ʨz:AhI>1{~;Ħt.FOn,b_eҒn~Ƀ1,FR]cߠ+^SyoFܴ˃_=ᆉ&"[v'9rt+#5.^HEI9(@PX8EFFGn84JD@c NĝXn9ZűH)1%u9n>[nh(uv3s~P "| QsD&)'ɠ1/=)[_mmDeXX14H;c ƍv}8L:yv1Lrl@!YYXbAga`q.ӓ58},-x͑c;8p;kW<(.?787?NP1F YADev "ݴʶ}טԾd1&"0tX"!K='ql{Ϸ[xݼͼ|c|`qi0΍#SΣ}#]t)Δ\3hY3'Gzg_S?ދRu|oez{c4cc>sLo m=-E;.GћN 2_9;OӜ87NM20J;[+NRDac #έ5jiJ j%ug|+BUߎۊb~F,,¹((7β,xz Ƙzhnr|6 DQhQ:~( uR=k`!T / GIL/Z¨ǖo5pҘ61ÈzW/2/f.1"W4҉%ABZ莘 E=0-I1&M֯@c02E JPL2Jw UzZpΧP(U)qmK1il'L{vyїNv4kUWpg(=8;)S(J])fD#y*#gg\u 11Gi#eZ3[@uW 2Z|@Z=VԖ=?ԏZ( kYhiI{#RZyYMTy5\}vT(Խ&4/V &4V:lە)ngИ531D"b!1&K㙔Hl sԷx')nxE1޽ wr[,v5 cuўb=vя{ixZ>,ڒz^DƠqXow s'q\ŲhTD(OUʳv }FYzر"gg#aAa$ ץ*v|-Ekr|!6RV+P6xyolK"CW㥜Z;=38;4 QƤ+"yxAc0ef@||_ Fkkt{0黟 mNߓO4{nwr''S,X6bBі.o8à![W@D"0n6`an0K_Tv`|Buѭr֚rmgvEѵYo~T*G˨QTEiC0D(Bgz}m%ދ/Ib1tk@^q%wgxzKO;~{4u08~ (MH݆Tq{速0% &%.M+ w+ Y#h}~8/}-9yD*脲l˲Je=oAD${CPԃuX"( TԫXzWU+_a|jJ% XLҴw/~;p`r5yzz 51.bxF\+`P\FU.瞿ƻ1M%di=4"5uӘh?S@V`]gMan0j%/ԛM/~|pW__SJ("[iGdulPj4U&*lC)79*m0'FE\e| ˨\bX K)"\< 4ŠaP}k'j_}bqrrEenT"bwfڡ~ΐa!~g{cxh37Gܜ"(! K˵ƫ|cGM6"6`my۷m۶C[V!yb2Qy gggOFQt =zVZ@)df7ԁ`)ou>6Tϝ61-& }I$s/Z߈?BHCb<:[^k}|Vlz_KZ0Jm}i=Q9U$\ ?ǵ!=%tEXtdate:create2012-09-26T20:26:22+01:00ݎ%tEXtdate:modify2012-09-26T20:26:22+01:006IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ws.png0000664000175000017500000000504112625666430027162 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> _IDATxMGgvgGX !p"r88!Y!R.|E$ XNB\ "$˂Ymޝǡg{zv,'U~ {`>L}NcLl)4ɳ.h 1溝SW?aMЕADo:'q1^~y;w6KIi2Op+uq7Jwr-VujGQ|%7 B ?wĄ'kot°+DRhI'8'xvKw* U1wZGOra⋿ݍ0.߲z_pfn>yz KK+~Dż{ܺu0櫫Qz j<0~ !> TEvFEO^?}R}/Q0)éW>Ъ/JsDOGC  AԽ ĵif~@ "Sʒ̽$Y3`U AHQ)78L<]n"6n(5)4w\WFPF5&u@JY~[uf4y-K)$ABkK\SO PIfDkb`8PҐIhxW}t*.j9W Ru5dĥX *QJ%M,i2Ͷ.ie^W | jNs'zu!\>NmUi܉O[8q`NM n?צS|8G_MK bSYZz`VtR+zqQmC>MH;=iXiM_xeOT X Ɓel 2^[P;H JyKSA*>-k6Hՠ֧7m2X>@[t&y<4lF-tMK|kU.^3 4VN9e7&XWy iBQWyϜ`ScԃU|z> Z,ާk9N|aiX ~t.lCi|D'ݩH)WHC'=_A]ILouo&mMS-`v4>=ЫZsf"1gJk:eo[&5tV)sbk1$ o\sA4dSg+Oκ:8 ulA/KI<ķ֫:(nHX6Lu["UFaIDATx}ugf]/v k/ST$ JKCVTc0 VQEIm&4 ؖQbS[AG{]z}{ǝy;o{G]ܙ9=kᴜrZkO,Is8<'}%f"y&OBZZZ577/QՅƘfB =NmhjͯQp/ SX<&:{0x8. XDcLf :sr=a4yޥFFGD<ϣ\.S*jL&"}KM"8b/+++[Z ~F'N6ꢐEǨ~kj*c.uT*Q*lx։0xXYsݣ٣H* ¡A pfV\q @cLVa'2ୠG:hN#JӚi Aqlr]3g)rPfu9\X!|"Aqh򄿸Yra_^q|ZE'c[O'p+8U; :ѷ͟n 2*S:,\:mSA!w\R ΅sWnJ%Ppr Mpե¹݂jSkOu^'p%Pf%ȮRy\IP=x ҸpUP (s`\[ϟS jl7U: {Qk+QvrM)%e4o#3+" sRZWGܫPNhQ#iQغ]֯ն*="0bD"1*"vO(X:d@S](m-aA{x~гWqdbm_:>*ZC*ۃ& AFS=|c\Y&J|Mp,gOCn$nW 5NS2@LDTL\P `*ڵ&}8J }L3 /}AFdu+[^PzV'9e|ےS^z7x D "V cvIgtղ 2=Z[MKc4E"&"zww#A{xtD*+El=*nw~BXptN]0z,n .1tE$<س`넮+Y{۽B$A({05D"qQ5#;{옷/b^#=XH P^#`P j_^d? b)x"X"tYƃs>m4`| P;H$X|D4uuI`1&8'7 ")2D_cQeu qܽaOƊVh~{?>L+n|thE^Jt?=RiQkzপ/Mᾭ(zwB#0RRg;U4*8^cp$>X>w~)ƪvZL `JͤAT3C~c- ?:}Kݰ6,|`^V!Klu'2iʋi5}S>D@U]k23 mܵ{=KMb>NW@=M=+Tr4崦MŞMޟ{tM"/o?_RXDE`Z@_Yʁ|RD/:m%$pFb;w<3}P u[ ԚqsK^hgV 0.9+?)/cmw3ŧ2`611H:O(/$H~ (m^:c;#65="֮eW^zTҹx^8GN& @ݎpUz qnfXwYƍ6o4BR(tRtT*Aftp#gEhYw!Rܹ֯=8ug%<6b ":&RƘě]=0ҡ:s.댆NlAJks`5k([5O71,b}(2bE1w"ӆ@f57ݥz*>va)R;I)n0f2  C6pg'o*v`qզ\~8d&\ F5l)..*5r*mgӲ|9ŝ;ݺuݓ}`a#P#tAd$Ωut.bQ`8dž/T=@m&sSHB%&axfI|tVR)(a(غfJL?>~+vF_(a+#&)db%tEXtdate:create2012-09-26T20:26:02+01:00%tEXtdate:modify2012-09-26T20:26:02+01:001jIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ie.png0000664000175000017500000000534312625666430027133 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> !IDATxϏFǿkvg6nf, A"H@qȁw 7PnHDڐ#b3=_֬moWU]yz YHN&F:grXcw> n<ݹٞ|U'DD@?AqWol] UV9o Rxs"sB惓 @ f2Ͽx['.1of㌌1 `5К*T¸ws_yvWrzat:X%t}|O,@`2^xpƌv0A :p5HURo;wp1-X9 )yh)EQv ~WwO,W7ZvtM͉&;d\"7Fy0lQk=C.9J{ b@Q+1"`Ɔ27sw_yWDS83;* fL(% !4մ XNvbH@QP ͩj\5LGyFok}d &DuSN#2P9]Ձ`J6Hh &f Q"*WSZ-;_pC?D>P f,tЌߎ5DzJ .1>E>COwo|D @ҎۅE E謣ţR~Vꦉ$Dd:"CPPSҵ]PuJ{M,fWB(r`uBF;= uC2t}EݡJ"q ^#MQ[T?A'MEek!z\ߔ ~`]1u[*b*KjIrDt)*ELz;p>z0g[7zuְ9{Uo'~}]7հ_gi48S4bq_.է"/]`3U bmgç0l'GzbYI~2 uaz)+f'-k߈V)$q>R0vP+~P޶цYlĀ}`(*wl"V3o9SUAJש&mrn®UŮ'_,(H0)7ݍG"+@ɀeqŭW+Vn Cngzyc֢?bd%Xn/ %~`9Mu Pp l~a ^p!DY4ڔAKEV#ϗ$ߚ 8ϑ{߫(-png%RU@Jǣf[a,=sb,"2-Ya񂊽 E! G;OP;Ԕ-!xw̸h'@Z `:1·z1'8:/Qn,8+OI~HI/΅/nm;0<_PKVhRԗcIiow,.%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/gu.png0000664000175000017500000001153212625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxYpy3; `q$ $xHe,[bǥ(]);qَ!c*!/)'/+C\NUqWD[bٔHS$ c=f0W.TYQW-v߿3 4&iM\BДQ6B`e^쳄"B w\DB4MZ؟*|ڿ֮Szjqd\Uh )s)O_p;{-v>xp"O%F4͔/(Nk̛`&UlawP_KS@52`x 8|zt^|w舘>pm!"R&BC27\2c::(W'7 *U+EKom|ozi`ȾVa?&,wr 2܀u",$E|4$d~ $r=(!'h;بNm’=~q?~.IUn`CXd](OP*)E FP4 )BR잾)e[8(3}4o?SUBcjC=@)p$'d_oqm#3@^AA !%",!ŚPg* Z k<| G_$Wrq׽2N#\#|B5h!ˤ#5HB*hr@ E^: v!kN-`^Xд3V9q;{wb?4znΗ^cO1P(K-HC7Oklm8" mb-6:C;@6&1Z3'b+qBM HECACvN[Z@ 2/9z~j}/s=Hd[SQH(&lBg$g`9$ 5M.s/[]sd"ޘ|WXb?4;&乗YY0_N7G.)2I(׹)V71$,/W讖|p ddA*=uEnds6ku}C--N/? v+i"-vKWz=-J2_y+g(.JjFۜݽ.7F'i2Jsdt"Gއr]nN0_Y g8sKW)^fexU<гg=7/qC*P6 tc:uaT"L/?!%]1@'P(NϜ( "39Rupb<qU '~ 0 BRf@.%O ke։1*6ه홼Nau !)uг|y%wҟTTG=‘h{yvMnh6sJWh`[Pn0voQ~HkԪ EZ{-γc/OO2Ŕ}"tf4F+7IYr{n27u[ "M NW~mWF!\)ҳ0 v"vVQ>(KeҕK䑮˯/З)\(ܿ v ?7Iauk v:n((1^)mP'A 3k#5Pmgv|[d.KmCk똝 H,6먰A1"u uB)%AJB oM"!gh'^\%ĥ);!Vp"X-%s#%,E3LU֡'ذl$Q*=0m05^\Ag29D rno݈rgu_7z }q֡x 4%)bnz {I&nsTTEMZP.kHE];]H98̌P˷>Vіz? v%\rCF o:̅Mg[nd 304Faic)FjJ5'^^E/"G فme;Q62v*@--t6q}E+5JNMj= pk*lDfnTj) WX, [P  d@)wk8u[26jmk̖)eWkxȈF'Nγ!&a* `@-us'4 YPB2SAUa}q:s9liq֛9e)+o߆zrLnӗ%g{WVRJ+d2|k'(7`a#Ō4(XMHzPM5]lz̙a*َaalӅktRhR^E[Aq,[z1OR\X)uYEZLNɹq$C2]SQֈu_!GY[8?~\yLWv3yn^"cZg%fZ d.㎜dWxpaei^T)i=q@c L"I[N&mt5=m8|m^!->R7˕zjh<-\Ɨܼ| fG(]LJQ)he6A1\~dϋv`B5&lb9{Rk 裧 MROIƊYvkjН|}oesf.E7t}\iTG \'l `ҎKga,bx=^;?kO2jZ5Z2U^j'ɮ%tMq훧Yy{=D6DmLh)BТ#dc=92@"A`5iڡGkzxݏk9kkfʴgK2N?co'=BDj!ueH:x)@)jL{'ٟD/C_sDnHFz:cSfu }IrHShΩױ 2)P5 [=pR)<ώ>~ֿ[ڮ 8ɂ^Ru}zO0}R~_0rAY0 A4'_0"1Ù(gW\ʿprd=w.r_bz=4;j&r儍7%e'Q{xʰ hVYl&"^BSlN?EKc7c-Wwo0g= ,7R4ŷ[gFi^]4&cIQWY_\s-<+cnnɣ'ݵ lҦ.-Q -1 pѸsijkz3S& ]|d4:L95tkNU\B7j)?bXAkj()jR[n':oN{dZ;Vno myS.Ue^E՚e7^yΪWE`zrC~E PuؾMj;Pl@pL/e}ҭ, QZ@FgcbbJ9ZW(*c49p@(kX<_g=|tV;Rg qѺ̻^^z X48lX?P;,9m9e9;$dzsG ~JAe^UoW/y71_s}(<~1t^20xlp0 4>u ;J`X 0XVH3 $k|!l2>u!zLćͳbW%NOL6*&/ NI!%tEXtdate:create2012-09-26T20:26:24+01:00 %tEXtdate:modify2012-09-26T20:26:24+01:00P-IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mu.png0000664000175000017500000000455312625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx͏FNBȮ"Ep8'D q(io9qo1ā]ِMH'n^e.w{,˿[r vivi&T"0N8l42 4BH_0lgLdA)EiXqj;-VOZoj_.n_e@>/G_{ژϟKSc)|Po;M׳*Zβ̾Z=:;ʲo8db"O$_%{{r5flv T7m\gYreH\~I"?od^"_8kiʕ,#]Ws8%mmD0iZ<#GͲ!7D>qaZV@kG`TnCw+7 /¡ͧE@эCgAWT!_dˆ"F uʛRW~C(o򼸋p N(P3껌}7[nj=7ke0J6B77<|NS k ,s FͨK:p_zP|圴-8Ur0KbVRNqBG|iut/wmFVzeh(t }NI!Aq/IwD'jluu?|:k+e唜%99K9~倛xh1kMI5[~㲖.AzpZXr֬yS[dٷ1f } бِSғ7m@~J}e>ǧXo1vv6]s:`a}h UOZ7 uclD:O70^@C]kHgѩb@jGb5CO m.\\< Nƀ \ CqQ),s8͔Y&F1}@BE/ܥga8 ߗ9d?:/BzE1a^k$ݣz*Ţ]CvgT8x EItaFa}|nNViDxT\'f 7I$`ޔzt#7f" 3,ڔM{Af,'mz8=L ?qZV A+OPVMP;/@KNz^ ;9.}3`ýv0 $M'鯗_XK<$̓5s` Qم=>}t<[%3`oSU_\4 x6 P}: 8- _rDB GQOqN|t̝,)غI9E|SH T|-zia8k%tEXtdate:create2012-09-26T20:26:03+01:000b%tEXtdate:modify2012-09-26T20:26:03+01:00A:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/dk.png0000664000175000017500000000541612625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> LIDATx=$GW=?-HHR$K$DEFd X  $ O` />}t=YxiNի}/W䜮C.sTЬw̺Q)dv/TDٱYWkU|+ȵk/y}`L|o0Guu_nKŋ?}]` 10ybg/n= t46"q1>>֫dϱ } Z\]t۪2߸>[_l@4d?zu018@J d A+~>VXE+*yg[GG#=Ξ>sr=i^voǮf{Y&12;Z0 n"%:P_՜SNj XUXAcYf ɽ'Dr ,"%ٲbLq"D[DsD,0^YpzJXы4X ZKQ6Myxy1YN&L5WZixNPh1}=zDRyDVD@-Ɓ).BrS7FATS$Q7/^/hpYr^w60ql74#whǼw!q>VQ}ua:*U62 F̕N ֜#;pWi!6q6}PE]@L#ўv5cytkIr<΁59,^Sj; 1s}Fks*]Fi']Hw>檝2 CZ__eDtFMT?2U]>ĺ"#/;724A3ցN{6uTaoLZ%<'zF]t5^4ɔ&'.ۑV?ї,dB+[jƼU`oD0WUQS**)=~Ml^ dz4vi,+&>K &k&Bcm,Q݅@Q[].{)Iӟ|st@<ش90oɬ $o$lcW’ ;wȞ{N߁b=\?pwͅu;?U*݄ALVcqe>@VυS'iH%X0|4:_~)4FW=ZG# \AMla`:˃PdUsaZ"hÀ>i~l܂X_WYuARr@f *X36-oܓKؕfֺ73*o ayS ]zQJVDEYuDn0꥾4EGFZiX'۬Mxȵ1]6@^QPݤ^"536Y'rcyvya[iZ1N8:}iD, A` ;)psz[n~Qt+v6aPQLc<;`ĺǑ6JZzfW̔ZwORhiE鋝+pK`9V/V'*.E~,(TѺXxiwisQ2ߋ_`JBqW5K pVhlD  m޹ 9BJX?w.fz*gPJ&0Z%O"E]#dگ#QNUׅQBryj>`,,EȐg}3!''m)%Z4>&}Rz` X-ǨE䤬$@u^wťg7êrIׄϐ2MnnFn݊E; Bask_a 8UO.O?nFW5V i* ̋/Q /ؓck8@m_ 0,{և'F֦d4CN֫7a6{y[+(W9=Pn٧iJ8\x 77cj$(A~t:GȝDR kJ۠1K>nMe<)<RHb}QEr_A9ͦ%tEXtdate:create2012-09-26T20:26:16+01:00[%tEXtdate:modify2012-09-26T20:26:16+01:00@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cl.png0000664000175000017500000000531212625666430027130 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxߏFǿgsMvl6!!M!CKxBgG(-)D eɶ$mM?{3x<&7l39$=IOғ)DDmd)+O|/" j7?nl DD }'xyU+8>;o|㆖cv>}zkz |mlBІIN RR1}n/6633Rt:jyyoN:˳g~Af 9@Q1v`?owdx sVh 4I%wOR(0 zIqܹ_z{^ ѾƁn4F^hP^$bҠ3)\{7 p_Ӂ̈8`߾}?>qīgΜ'<]` R:ڦ }Mw[IŐsg""03 @h4n'&''n+@ @Y  $`s0;#jyJA #+({:&LH34Hop0'l34$BEu &SkG9HQfjf']id9_ 'PFRJ^ (0Ő=ѱ&DfF)HP१[!3 *o G1:Lx a(@BuW./b}u([=P7X)z#~xfFu\; f3w?^B)6w>5,~f 1DkW?˿+uc y\}-e3q_w䴍-lR\IDae )VNa*$_( 5;>"KxJ1O`LLVOjm]댩ڻo:~ۋXZ;o݅kRYı{&%H[tZiafzu~ F 9ܸz=:ߤ|$g -,-/@J1R/ьM܅y/‰HjRw'1ݻ!$GLMKp@]8aaJRg멢@NVfي`%NLhVe 4p <8@c&3vп 7@@@i*zz#1ݭgmA>I¸~8\.8ٽyLS@!7Jf͹9Ύ})D1c.ى?{DR)tUD>Z(yp uADžo{n{3pXݖ4$ab,KA?3:&ֻ ;r bXAwyN! y|r1M% epK)Y&.))* KG"ɛ3G#9)\)l%}-3 #Ha$Oq;qf=S_+jfvZ9,Lp2RmGV Yo|3ϳ503C (IJV7?y^\h+o`2=6X.;<3qZP} L$c.y^o?hy6}9g@6DmRKmؔ |qz>m'"se3{L۫+@M6!o&kԖ=u?Uxp_):Z}\*ډ {gCͣ.nDʄ-}@yy\f`|v.<Z;u*0`wmd5 hnv/TE}s.AsPW 0s ҪTA˫\>d{<{ԏp4{lmgazFzX?Wحu?~{M?(@ Nr L#?"^f_Tq_dH^ь Rm &QN J[WC fќ~eiP Ѻ.''Y"tUh ke$vM! .9; 3P* "҈1A@hɾLVsk͈nUܔt)MYHi{g5TGKO4qLHXS %nƥN> kWv⸔C5YU3xז_p:< ]xA(= :6Q11J[ZpemNJ>k`!NȮn(FCZ}T,6df`ĪZg6J`f`Es9"z5bk"^[l Bl":H[+p&%_m;?L/ȎdT߮ OңH L=LVy%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/be.png0000664000175000017500000000522012625666430027116 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxϏFǿ{oތY2 ( ) B 'HGN87pO@8p8p ZU؝_Ϯ\)vqUWu ɝɝ RAdN%8G* 3_2])P֋!ъZA2oX`%d5M%~|$P{ ߊ?@Dd|@A,J˲,?UwB,Wʲ ! A24GK=H5Qm A6^4% E)o"Ѱnɛ~pm"Nu A`AA(0g]C(mwFgi$jSx0`C2 kNk%U&jtYU-X@kzO*qˢxڠ{R9m_<t&M`iH c pJ,yךhh\/)9.y|KԔ8edyZl/;Nja\Ʒ$an)$y%X8{vN˼s(dfVvqNwusF_JLtٹq[!njq26 G#v+*42 Ԡ,T Җ龮@Ԡ`qmVCiSZRLt=q V$?,"Dg5ol9~Nzi)xN$D7ba腈)=jvdo p I՘eIX3XB{0SV6*ޘH ˺}8ڲ"D?ENp- {4Uc]OeYh\pc=s1KF Xhvc6 pn:L:v1x sTt6mpSm{xs墵]5[Ӡ9wAE (᎛YcU E@Z|od^3(A-#vH8[Nѓk`\ԁgU-F‹uaIp 9AXs :й^2@1@ ) t@ 7 ﮽`MMk 52Ou8a+g-rǣ@/}f"ßwDgrl vmx_ȅ`ډ}[K>hNiĶ%Ja32օcg}NM Fldu}]!\7 )="6 {TyM: 3b͸\%(9 .̂43 *'qӘ @5ܰ2T XtfqM C}:h["B{Da'#T³@;zHJiœ7޾ \q.,۔2q&8s`Ziٹ ӟIZKf4j?gNo6uFOdz@-"],n+|co֍ K p:/.!y/}{=5KD䧇4ѵ+@ _h (gj0~op_"N6ӟx1}l2^/zV\qq@| 芭k惿iG>=O^%_ Ős''sL|l(h^5ݧ%ϝ^ V.:'N; #v#7b0ڱnVYRW[z齰Qdr9,o$wro?oj%tEXtdate:create2012-09-26T20:26:08+01:002֘%tEXtdate:modify2012-09-26T20:26:08+01:00Cn$IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mh.png0000664000175000017500000001304412625666430027137 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>bIDATxYygÔBE*;|} n&`VX)pFS#-=u M4:!!,qߜU$uC^ҙBGK)*,RH}̔p82,Go轋c@YUnOzE8v#P!"-ByT ۫HeLѴuDgU/t'kէl`cJ!HeꙤ+c1%c]˓˙6zi~eA&Y䭆h5#l]wJ8:]ug(J$ۦxoo H/LFk_~z<Ty-^tFs1F*m0MY&!DlNA:kbSRQrD<8 ]R} } )5$mp$C ¥IRt|nξzlvpJ^9Rw()%LWBب,& gٔ Y91RJx w4b%::˱4{ ^7ᐇ͞WBp@'nVpo AMDb< QThH}S^ X@9]V`n[ūyP&JQqw6NM_"MT`GwNoQMkcX}ط#|t4J%ٚ ERpXU(B(j_KmhBRWJyS[սj$u|$ICp뜻2sG{io L8ߠ*hp6LSQ;=H$d{w-kc)FMEO ,ӒR a^sp̶r!t|{CT\ \5a/J)\&Gt%bo>O6'Ė*ZC|YFUǥ3+.yT(u:ߝyljP[]\GLv\?-W={vtI>7ᠧq׍й|kGjcf/319D{Ss+OG=`Mj&9<ó}C4HU6Ohnփ }T:< _Ƕq@'.]ppZ騦). 2>]W*‚ۂ iH$nZ֨ȨF<;nu7%hNKIer2L^w `|sqN]AitRa)Sݶ|{k#qWu>RfXf6PIiJ)Ѽnz۪8}; ݻS@:c{w- sFIsssiӜ6Ds0b{7#$`=i%µ.ߜ"]B@\dz._%W7/$obl*JCm]54_bz~ӽ<5Q(GK]E8ͽV2zߞti֟P6g ba)cd]Oc קiߒP5̠ k6:[ `z>c1Tw+]wSkW_T1 ťTl 3<ĻP[#Kq,>41"T<Zt0B1zi ]>76=DYU]R(޹1f\Y*B7a6P*ešcQ*.HŻJC.ߜe9&Hs|rs>9Ǯ-^^}`#vXkُvԥRLLx_gz~n(W2xN{,%)J^^aoȟ##SVNX[TeKRF#QJYv5.WL4#ׯ N*FݙN]`vaZݳ''4m/|}/q:<7ȧm ;7͢4N$J"|$ cir,_viEkT /֙aN_`>(,]ZTm.%i#ٔ.o AB eJMn*o ZJ`*A'99iv2q vq Δ Fx0g.M,̨VC+vMlssRE@Ua_{ N k(n,!kJv/E@B!\->;^"oI";l-py8N!pJT8*wPo'Jm!R R$GZ'xt j{P U 9ɍ;A>rXMNX~ å)ڴ|tV\~vv94P\w8> SEqO{W'2YrJM­|k~$RrrJbHk.uBO4*e27vަ:j:ͅAB[SӷjHer\9'p}x"[KwYmT*B+V@{X2*j\Zd!hdA' h._=nA>t-qέ_TTY\kz~\k%.q{zWwbO,BI uPﳸ-%&Y.\|rsd* t ʐ( :X*պ& 69 64J' %$tNqF[_ B+&p]^?9۳Ҧ-qdZ:{Z*(&W \̻9Pq}>TZ_w Ė1z#-_)Gi~2'qgt'|SiDrb}UY[ 1ٮԖ&͇o}k'1xOfjpzX~a^mT_%N+: UkQZ)'M Ǖq^~Z_$w_At)M8Y$kZk(]kzji'W~K&%QҴ_2D)w8>~`q&$.aǗaws֭o]Sa{Jdi]TF%͌0 ݥ8tqlH3>}ve+kϨU:~/`~ J,(Ke׺ʵF2ʦ_uIvd85at4B w~1Rp4YjK?l}m',f6E6ݝ!k$&GFGV?l޲$'q- q{4‡q$wvO~V*@ifȥ1xB8畩?(Iޜu>1bJ jMPT2H.+`HQa;JFݥkm;ۄ&rZ>Vu^ҩDliic0kLS>bB>W@MR Xy^ 6FX%Aǝ>G%0$mQ nsX5B~KX!gK>CEbٺ,4ŇW͏I|kx<'XZ_?F?Xf ]WZ%tEXtdate:create2012-09-26T20:26:30+01:00%tEXtdate:modify2012-09-26T20:26:30+01:00'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/aa.png0000664000175000017500000000366412625666430027123 0ustar fabiofabioPNG  IHDRP<[.bKGD̿ pHYsHHFk>IDAThKo[d?gRA`Fˬ.dcA2ȒFj5YNjzGOV7yXuܺ"g֨o)y!||kj4xyݶfgEfykjF}OmAe}7cӚ3g,е@t°;^+Swj;Nt@б7jƜ =uck2G4%:pjF~yk1q#I, e u~Vpt {?/Kf. x盻6Er dbbe;lI Wv[~yLpÞ|ѵr g7 պvWҐ3;N_t4رQ;ŎPTAI(AJYtxرIWNвY>qP:gqF!PdfȀV-cT[ ]@~2| U׬&yP w4]@Wq]qn:wԆ6|̋WVL\jY$M6Ip"_snF]kt9si*Ӵ bjԘ 2>F<3ar8Y&&Gv h߹CӾ#)qxFnVA:hGufTUN]bTi둽29Ő퉾]dCBR ɛ] 92U/;^#"$9gThX>KTؗ.by/$ - xzWU.U/dyHJ8y 2Z"FmU/ "E|}IDATxo?k}(I(%&Q"*AEBAU 8T\P\W HrR%J"`8v׎ݝ=~ijF}>3z~]2y kByV@ӡ<͏B6l4'^Jd0養ǝg7)rss :df&j#o7Jk,EZ{hOzc#-?VmoJ4FKRh~~[%`h=il ,̡Spt!YQUq9#8`~ &cJ$J{%--w,^hձd? `*"<5͢^սehΛCU"m?Y|p&ۓUI6kϏWRڷ6wM;|t#2F h6pc1l ^%1Жmڲ].M]e>sW̞H=FriL,L '-Op|qK(w8)\r} W|zS^o~R|}vw晇᛻p}:+] ;ycY5^c"ÿeK;R u*pjjjōQxe4 +^kYDK:OnbX" V \O(l#O兕+nW<]:o=rG`h̴Ⱥ',{iJ8heDj^   ,{i>k")Fc`Q޹^k|f\5E [(*VD^RDq$ Ρp%tEXtdate:create2012-09-26T20:26:10+01:00͘a%tEXtdate:modify2012-09-26T20:26:10+01:00 IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/do.png0000664000175000017500000000642512625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> SIDATx˫%y_uy;s5,"id[ND"% !lP06+/ /?Ydc1{"L"CBBG&ckuj$? O0a8@\PǸf-Xtơg6-ov'`ML@B'J`ʀ7u2,y==dLQfX*ڗKÒ|}#1Mn<0] b F1ExѸS’!́O1Tf2<bVJ\*MQ-FOĨ=!. ہS֖5yMqTAiR&j̫͢N>OX bݔM MW" .K(@yg {kB8T%3\$ tC.]}Y` jAg&LP5lJ0u09!:[;9NɠZ Seni<7'93_$MWiq3,!;hCa3#݌ԯ.,MSSWɧm8iUoFgGcnZY5hj% Rh-ɭ8$[Ve~|Yktݼ 5?S'< }Xl㳪 l"Z*Ь*˰z"TQM Q86Έl m }YPeVsntZ /kE*sx%u# &nB\EUZT+Gjˤ-*of`Q޳M맗,,uɽѮ'XL^+G1<ݽ>O𲛙Pck!13wv]௿>sU:;?%\ZB-w셼se0|~,g6V8D}U+&ZhPǖ0  Y<k}VW{t]8J1?x-рϬܟ\$+78u<b#ps&<'Oqސ{O2Gl/;?%v1K2u)A{;as>s:Alu+g;;:8|4pc׷/k1lۼm^.W\/?b#`ox2u'YYf"?\g!l~[?ɥ.b g/\q<##ڍw3nKDeCP=E<&5/}=XY=.]I2şNe1qݼͻ/!Kq¶YZ}ȹo;H|i^c}q 0:8 9J1`w oޘ Q A` r6l$;AWwbĠ6ce1^-R4~zFhaƶ<-c`VĉESO ^e?Gp4.崕s>S3=j2 F3nEz)[b&IL ^ H4fk04"ǜY-퀤p%e[+\ pw :-YhY{=ہZ odº,V[ wzFP3YZԺq1R>EJ yANu|m/jTbN+Hi ,OFeyBMO> |XfЯT5,=R3@VO̸?,O ژ][\-ViK ,*,AkԌJs7j5q;]=2m l@K╶1RYؽW_5TOyc WckC4j5 ZJd6abUb RŤ^i+k7[OQ%c8FbQ Ey;7Ѱ@bۚLXqy-JT#jX8chXY+D1qk8;,G6" 1/zo5ZՑ"Ajٙ9ZO*3j`M k4i?n&Z1P1 @0q+ XOUTՌ1eoxo/9uP#' h@$"mF7oj:%Ts.o{?JOOnD1ݻg6AX4E`CLW^:0T_J*vџY-4Hs׿Gxa%bt]{}x|lXCX6s=tfiI\$(9?]fqH 2;~5ȅX)č,SZh7H>x&8nSxL03 DuP;|1 ` nabi||hq#&6zbmӤ;[e,Ep-ځyp6\Qov'.aڽ!%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ax.png0000664000175000017500000000736012625666430027147 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>.IDATxˏ$]?zc31`c0 >p!8ؒ|bq@%$^@f]vzytOWuU>!#2#"3c uuVFe߈"2Yxgoе\$~qf/q!@;gdS#H7 /^}7~UL(*ci`Rk/p0 H|3+v>;B-dRq{|?Q÷u~`1n,rHڷʓr2f/7erU1 @W)#-<q-7q8 ׎=>HZn\_{c|1GמkN@Ҫ2!~o^YXdgӛKf,cթW7^/d'L[5*e:^3^4'b!e&sf9T B$0 D$:tӁjÓҜ0!yadvW+ϓM$!_Y֜CT`UV.A/ꊪ@BW+]"z(TT6gfLt[ i܆jT5H6FF2;&c`kQWpQGTA@M]L`=XI&|P"Z1Vq"jxp )WJ]N W0wlf}'h5iH0^4DɍH*;:ngD1>m':`j5M#fi*(B!@ ejL6ht5M6{"e3}+B gXq 5G:HpMu뭟 lTǾۈ3$D"IrkdS=OiE!lRu@UZ/&=2Ϳ5vD}`P7m]ڸyS5[7 \⚇lMZ 4Y6 z]m$G.h'քi;:w%!qc/DfeփFG:߻}ʩs蒚PERヶX۲09(1[ox1!s+1jH|l _i{0`o' _zmiēZIky{?)وi 򛉥\8ފ+5B>rڿP=ExI1X#ǢM"IUrZ1?\\Zҧ- A5[0W1ᤧF5 514>N1| .0z 7@tn^)Q^Ȕ '`$ފMҸ HVzE%SMo^2 R oux::JCY^WPxץOWcxIzmm\قV?-\G2ɘd,]zo8c< Ict\pd YLTk~*U2+K'Kg\si8qHx̐qWR;2#$1cO]~c8Hu-~{0bʕsg<QA+?׀UỈV#mO}q=;5ŶE# qڒ󰛩WllFt˜>|m%PϯV_~w_e߱P&`:O)Z]LCUN3[6ϕY]|^@>HRi+rf}^8XKTO?TKTޞ]U= W&fy8,L- 0PL,w?X2yx6Vُ^_N%=/m.LBe/9x1S+G(ls#FR5#Yqg\7sNUH(<9V?~OP,jWoUU>pЁ\fx}+oS ~'C>𑃷`ˆ~'}Ny  3lCԠg'1Gdj%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pm.png0000664000175000017500000000527312625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/mv.png0000664000175000017500000000716212625666430027161 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[ۯE|gw;Vz-ڦ@+BxKT@ b ^b_>c &`˥BK[h˹w۝3;k{@ݳ3~?m[VnD7͐BT#OPhȓq< t{r'*Q(8™bjEϥs%+^9b{19Ƅ91# !1yd;l.]@ExjuZIE1AKA< !c9[JNvPc>s:1y:1A?h`pJ @"d t/nD1LAgk^NJ`7Rld{{/c"j }_W|^  qDVZϠrӳL-q/YYmă}˯uwaymFDpЉj SBCyr 9jBtC0 A ،eV4BE  %H&S[ЮW b"hg>S̔NY+_ guZx1zqdG1`Kt@(VCF ߑ0_EQM&(#T:%j,ΕIOYH8pel` )u cc " N۶R> ")92}bBr$`Ć'|T=Ů"ѳTlv%3{1x `2xfK(SB ˨/UULL 1p E>X#fh9-Pm:B)uz\8N1Y E79UI9N8 :5Ѝ[L[rԲ<G:y l[y/.ی5CWi$CХ?>[s1YO_WV\{!loDRrʬ-ci@*XQyj/N@0^%NX1pފuëЕ]t ېjVcܚPٵvF|L Ì+ cBtJ@' ہZa–D%=P"fƿ>މ]bZ<| 6/وU0\@ݯ+kë8; Gp5sv+)2'ũ@N] f'ʕFZ:G*} 4;0\h}E5`-sg˚#)6=`ɾp4/sjxi]͜2劘RTПy aۆ 9B'.CDcPgp=t, գ=!ɨ%3\%3NBt (_hYf{BN N JόT;!jstAc4k6; L@uwȞǔ[ҏH"ti]@6[/TIIg&~!H=o]B 2:?HLo,M|; \͜8sdDõ[5 <*%j{Kh*LMS({s9]Z1[wQ[y|ᓒYϟ?^0h1e!K6TcGET jϝnh@ρ3A$kDZWGQ>I @kCliA|h~+vjJ Cʝo9[F۞\/%tEXtdate:create2012-09-26T20:26:09+01:00,%tEXtdate:modify2012-09-26T20:26:09+01:00eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pe.png0000664000175000017500000000407412625666430027142 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>zIDATxoDǿomҦTTB.U \@Ƒ^87pqGBHā@(Ep(UZ(Tdk0v<;;;Bd;?>}3۱רkqE9GV&_d^nA"e!^ =DU]Mf)׉}(V%= `{lQ~f_R^%2/9%ϲSQ/Xop5.!z$1Vq̫/H!2p pؚAiSEm&@0+BDq)@t(4Wv!zv 8=$B?I% g*RIR*Mc&`<-s(%"=" Lt<&x'""R!enDLe)^d*!)n" a\-tOoCgmFy\  r63pX`<`LtEٜxL!X']¦*]pU?1&8916T軾 "״ PFᶾv7-&*ۙ;n+}0C:5Lx vn}FXT`&gW>e&t/dpmSW'h{DGh| `ij*-u ե@ 7hȥ@_h dDC(@*i2Et]kQ  ڨKuUsa4߅ m qc}"noQ lR+ۢuKRu Vw!áM)0ZS}"U bxx]XY v^hv raD׸H*֝ -e!+]Y6 3hmQw.lBUXc^C0D\sk39͂i`D50u@B E&.hu@9L󟯎E$Ҿ du : cum#:D fU0irk9tAeMD:T]3 [95c3aWKq7(VT]mzB 5Ȑds&DX˘]6fPS m1MH@Qd5rbmr)b뒾n ()$4} Qz\,ɯp.cMY kPQ4(մɤ Nq~*3[ld>%Pkv nJ#W/ 1%~s!Ιm 3z9^ %-X솘6F!]w/[86 >dh<ϯgJ/)ӤTv&0->.LA~|: `%ʹ͆fK$ (z#`!3\s"۷U0i^7 3`r_ʭ,` 9ȉ0C<kN`v!-2'9]x6@QCIav#d^`\q7]Cn 1F }&) sQJIrW쨪0].9^غE,sSa%tEXtdate:create2012-09-26T20:26:41+01:00#Z%tEXtdate:modify2012-09-26T20:26:41+01:00R% IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/kn.png0000664000175000017500000001120612625666430027141 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxyU?/99Q\uq$PFM{Z@#=&b +V8"c!indm; bc~1.N %b0,3׸0K/_}kO LG0X6)| /*9  ˲t6ijPC@ɁjU8J(0h^/Ƞ]'???N,6|(X-| S<Z>,Nߤ 0M-!R=UN5%jbHܢ83=Ax$@W J8wpH/.*:ﵧX,RQfm[Dr1I:3H(lGg\C.] !z?Dr,՞ N @6#>.lkOZ *Q"gg)ۢ2Rh4fUsE3:sc`YWB*DtN@!ФQ0}$O=+/!tvaܴ _$'lMiZ3H]k( IsYHyaxAp( ?tU{"z  )z?C+a"lg e7BN>39UA;HW^{qph8{";t4݆9繰k0Y +!lA@Pg93#*P.D"18k1(:;`kރ`(媎(@+86;YTv_=EA}<~ӆAL'ƒO(٠,N%f5iqZ;'HVw*ՠz0?nSz z5C,8q"SL矧]Qq:ozT%b b/}9y \1Nuރ˅>r3ژ;w.'x"{/^{-gy& ,(̶_5\<fMG 87)x]dpN78 ̑0k NAs3< yZrToi{rSWWG<SOeʕ޽0"ݖP״S&ÂypT8ؽſS(æ:@ZY!p<l k8lS]8zvww9駟@gg'dYLQppu¨h^%}"*JDr=j G ӏW {tBoTUUq0}@ۦir' Sߜ(pt'H5#|~?4`)T +!jj9 !WmnBlR'2|f͚EUUUȩT;cDzxb}[C̹P02'huUn଀P|Sހ.`Sh`TKdvzRX%J] (Sپ};v"L2{lhlldÆ q ƌ3Xz5IYA"G,\  viJZp]m ]s (7!6aph8nt ò'NPRt Z;úurR]wVbŹin(Req!(xRrd$. S @%݄x(MضIMҽSJGNeY|g455o΁0~5fLOBе N3%U m 6o1~,\| G+aAPslΝ૬@* ̂ F='X_ p :r./+sU(K+S.5UcSjC)>K߅CCJQvbdY7 &gU qyty5܀">$$ O?/< &QʫDu}:;;@kGu~9O??밀!sȣ(?!d yriζ#bÇNe>,4I󅪘&4.6  vpQ)y_bO4H%#E3?<-_9 qĆEORŨ `Gp?})Pj1WD[ aMY aS=C ZGWK,?7yQh ÂyųTBC:EA蘷D 猀TBطO XF8TIZ%* ϯBۆdRh,&N>ؿ\hy WJ"`{VkDr^0LVw83z‹/+~l7 `ʩ0j騵:X WC2{ nBEVAw\|yTsÜePC,~^|04#Fː!CغukZp, NRrU'EQ*c- px'\1*Z[ǞV,{Rh"p4 iӦqT$wuevZrW@u Z_{S5Q*.̍/Ҍx9KrC}nwX::0BwCyڹmƄ  HH$m gMWW[l \m!w[ `@RɍVQsV7_BD dD-@'W.)?X?!qa7ndӦMiy\lܸE&Ja6X̽^5ԍnsŠu54> tP,>t̢Dt*ooÎ"+Ɣù-b\s5<쳬_>pp ΅p)np.괾a!W(gEXK|?n=(0 oϛoieø­]bв zvEJe-:x)Q@,_o>.]}#! σES;`hG|s]!?%%TE%E;oJyDV֬YeYxs㜼Bиz T]Ɂ[oQξu쫵v`OnN6ah{t:UWl[xOxoDa0PmƌrJN?ASN f/uwϊ̑G9%`% 3ρ&toC Y;Ŷ=@z}9Bqӵ0TWʃWa&P"mxʳp~oQ a2a2} 5;fa[@g|"U `(0>zVXƊRt}ZګgW@M>X{JϣR t-J٫hrk=)R XZD|C6kZWBӄS`?”k'^ d}"&`2oZ C>>dQ(]ky_eYS-ˮ.$^3dpFR];c']ЎSD(ٰ熒084ǤRL%BUQGY=:[uZm>ӳö}hgb@H!w wm@ 27zi׀H 5AN!fu R sW$M1"nw? Lɻٺ %,$ECbUy)PR9SE&EooJ6g%tEXtdate:create2012-09-26T20:26:18+01:00"%tEXtdate:modify2012-09-26T20:26:18+01:00nIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tg.png0000664000175000017500000000637112625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 7IDATxK@ceo7 !9 CP"!eVD,, ;a`"dP"DJ"b`ό3u,SތRp\OW]}[unwt9]NoTRH:N%qL+8ۜ4[ 4BH3M#7$DD'ⶼ׎Q޽}EP-kUy#ik~mUKBm/WU[&Fu:҉l Y I@t*(6|c?OU9x>TlDNlW6_5ɶ2bB9Zeo `h3[=ynq``AV D6#ov ٴi^(Rl56LٶUݦŗ=[; U1Ɛx~~%YmqtYs1.&~?UFDfx0=7ڷ< rv1tOޢ@VjҁB;Q-n$Bo:a%(O/~}h!Unf'DHd0Ƚׅ@CV}Ȕ@YSʞ9^8VZQS7j $6(78Leq$bL ʁ";DQ5/S"b#Z!+ 9{ ƍ^U+7BTl @""1LC\ᥣ6D޷A_vyp^{nK nWn;闻|:rjCl*fm" 1yQyUm#JVi Ya20v -4L^6T[߸\8bE2V9 D42-R"HM P5XֻF6?0ΝySxzvogŜ9f>/`HY=LBxc6u?[z6Gn(3K8UjZ#"ou)Z뉚e,&zsum)X|-?(o^1ה[g?@RϏsnW>t|-k1sYxO~)DQҩѱU]zTkܪC4DnR0RL'Kky{ [`|d/rho # nź,jzEGX.V4䳌ݻIs,g<駟fx(,SwY nұ S#x y"30Б]X= q+"P}ロ|3m0}XC>$w}YbKM=c-ٷbDJerjYYtac*,+rH'Qn+ωcӃ\s +nyD_Z `=_*-[/*&t VɣkX5B3?܋/Bp\(";u/LL`A`lDHt*]JiTki H֭WjPEQcé-+yX F}z˲"-UAӴ\ު_>TVkZ`Sq> ѓ'XpcqeZfC W܁kEx52YЕ*M@hk;ʉz1#ҍwV+WڑBwǐN)Ǧ$UOJO8h0ԯ^(-Wԯ֣܍9W46Zu~Y` jEV\{kxzS)a}ۍCh€A\=*acr/X굁 7:  xF>2xG,7u<;klwERr2_Na萧UˬQW4ZڍJe i%  !V5) 1_kke0 O}&⺰l]ZJ9!>@ս0T` ay7DqrՕ¦ )q +JHl,wR@vPc]#&Y^m_[7ͪ )ߦ/b貾W.{b]V3.RvƯ]^$<'$".yǛE-u疈2Q߮#%['sغ S^"Ff6¡kY{0px~ r6L$ iųsְh*Cȫ’?]3+'9I>޹s\Yf(-OV4C%5v"܊Ex glCQ18vaf5X# E2l!,hi::G@LZNW@eHei{udCRW\T몘曦/BU,zB؅O/QluX',ȼ91ԅY` _z5ƈՄVD,D*_ O_ XVenCӌ;d6]{wLxQ%-|yPѕq_a-LKc,F;13+O, 8IrŇ0,vPZRhf!-yF|lfANY ONPre0N$"~tMnu!&\ߒ&1)9ʏ3.Kʏo.UsC$TSt1{fL1%tEXtdate:create2012-09-26T20:26:06+01:00b%tEXtdate:modify2012-09-26T20:26:06+01:00yIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tf.png0000664000175000017500000000527312625666430027151 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bb.png0000664000175000017500000000740712625666430027124 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>EIDATxۏuSsٙm%(E6eQR΃[$` H=[@0`'HVD2E\.Rp;uP3C {թNUu7Nt;M>AH:I2&gf9elQu hMcZxؘDs"""-66nŪpOFl ӱ9r-ѿ'8wy~lO}x[2Z{D|x^ 6k5vyfcnWB24 eb?Uaig}xlD hҾ,O|Yʵ..`Iz֦$Ɇ'#I^<Ȃ1hlo4Fdh#sŠf5,.;]E@Q?_qws \֖ĈFe,6VUd74=iLoxȶI#IڌF4EZkXZ:UQ4>r"ʠ VWoPr?="-"##2FsIrŘ+l}Z;EZF(u4E@LBV!:26SjY6X_O -Fl]sH Ȏcލ7l]D9ۧwiz(! qZÈ^ \5(/Uۤc{U!M֬an:9e}`F;;eyvu(k]< ۨ&e : UTlʀs25Ьϫe^'*[E\Xl 2vQKץderb)dZ 5xd ݰB?=g/O_?4p`,'&*Mb1w,_| '*Dq@V ?Zhiݴ'oW1.z^椃}7c;_#wom/uXZlޕ9{-N4g; }pJ%Qn{ ppd l>S. ߇Ï++/=/s : I8ax% [4,3;` πbUATb"FG_Uux8p/< cnig'>/3rM: ?}Lj9ν%`r޿;֦yN|񑇔w+[{޻r>%ʇWm;-]eʇ`|J3Gg1TS' !o `+.MSwO?}`S?xWsw 6t;҅~fNk!|H=wtaNGV5 Zu A -3vK'mN9 Ϣ/DA +TAW^}3W| a )\7x&*pn ΎrAWUTQRN1짚Q~rN.}i|4Ͷh}vN032Rթ" 1u/~Nk^S"b>Q^|MpSvvv(elkX3 |^|?<)h#Qb7 dnQhM$U9MUSy;XQx줲6Pr>{KZә쬫& :V~MX;)Pl$Z7v義󱇔-=L̰Oh\n : ˓dha_x}@7AHR83e4rOdž6 9n0kMB,=IdɄjT*g >;FVoÍU;v_\^WW-}uAf"^cmCU}EU S~a.ع]yz(\+=]ک^7$d&3NgsSbd=qSرv[g`eU9yD:c e8yZa"lyXo}sh4Nqx1!ݒ.=*Btbs. \X3ܕUA\x͏^S~;vh[69!FP][8[sc C ߞwx]RW^`'>׹?ˢZGb3Ȳ+GƠbs0(ۨBs$,'!/qsB ~&c뱶ogh5wms1LD+pnmW*pm¾tg)Y_Gmm`y^M8]-m^Y?8/SM;}婯k'"EN- ˳Q732yO\4emT.X7xwV v-@WAUNrx1x}C딗JD~]4 Np Z`L&9R4Dcሤ8E_ _Q|(S-5&MJS\e*L] @"kEoEۅ`_;R?_ߥu7 0XZ[0j*6~)#M/xʯo hnZQ2i @#sr"kٕj_/weSܛI`ـ*6ecC%55:|9"Ѩ)hm&jJ`{\djڄ^שݥUFŪ]xRY ) 6L8Ay_W7KUB!MGԿW >+Kn6P QfSd\ŋq)0ӭ֩Lh|ɐC(Ihpzrڊܧ74˜6oƅej>8L!h_z\4mQ|&qsYaq7N5iQ%UC^4,F6"jl._wLiFqo1;QMSuFscfS N[d8jznnm`i.gz1t=%[QZ}?e4Vw+S>6Z,I&(eG3*A>\q>Blϧ°Su4II/ۿE_pdR&YNG{p*7%tEXtdate:create2012-09-26T20:26:01+01:00K%tEXtdate:modify2012-09-26T20:26:01+01:00M+IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/kh.png0000664000175000017500000000750112625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx]%Uޙ3w>wvֻ]<5vd9Ddy^A^/լ\(s+Y49HH@lP,D̬̿DQcɟ LWDe+9G p?M޺ 3 + F cevg}=_1" ֥QJ&!T5{s(I=.7U M2 \={_2#q4K,C1Y.KbTˮp̰:6z,V ܿI~}r3VfePICdXdKwBylt 3{!# +sk2M0fADudTm|Ɋ*8UED*ʲuHiD2 c@ WqhַcVwIB¥)$cc^f51+ ]56k3"꘺``U)%q@TJ4cD^@|*؅ xO\UyVjzWa]gowG>z nWdPCc ѰWT[å.A:#5ʺq=KF#lGW:$X,V9&Bﳁ oM,%p2Cep$$$);" k􈈱XQSLwɂT#,Wj;l"K <Ѝ;R)$l2_Ϳ-'#NacŊT0c*UEx x*Lδgs_|˿|s6)WqK"8!ŐV$,g^ @[Ӛ _Y("ѧ?ߡ{{ID$Lzmˀ^*NLa e =VDk1k7uIr$Bl)6ۼAh*}k l+I:za}dn\zbO?9q{S`ms`a }אlQ>7t2Hea7Nҹ vcs ?Y[m_W %t8,y2_{M**)H2>o$!Sa̫s[HGVnϜ&~)s 䙳ORه8p)zx"} ڥ)'s2W_*%>nowz50XS;*_}S  )O1Go^~g?^ *׮ޞ,F#ҷ}YfŲpMG66H^0/ி\-ݠ:xIg޳I`MGZ\y?^ݺ9y3?,-a!L)fuY^&xq-*O rfa3 feŷ'H߽?_䦀Rt/*Yɶ"uIݼ!>ӽZギg~e?$FLu5)zz٥Q;>i5<69n0.,673gݾ%>tWҾr=u3Y؃67Mt0v R5mU]Ih:p(@ g;MK#[ I:W(ւ1ɤlG#oUݼ?;5uԍ;V $Eԡޞw=Da I$$=8T]$Awv|>Ctg'kz\[jk)"@F@?EffgB/۷χ^jQ̍Db-no\ʍA`rLЃ$?z %cR8M'(m1 R+ %:d~[^8F|0b-fur'O"QCT'O &K1H~>^?c޷ԝuzѷ51/63/ri1Uѝda襨i2P/C\0Evyyckk旑yve(-i236 >|NS̙>_ Ti"^_c UIcGV߬1a?ݸQ'IՑhhrL|"CV˰SPmQzݦ-Q鄓N3_|94{O _`u[cOQWSk`Kt@)-}؍ WlK|+RHB~ Zno%'- ̝L ӅY\0oEփO-vۨt:>$?y啀`޷kޓI0 p `GP9~ 6GYY gjK9qy;gy[fV戵SJ@5fDpzrp%i _i/;ކcĐ} ?޾;36':.BˤO*C%ܵCA"M$n75|&8=${:Os ّ@'1̄0(zU#_ʣKPF8$ֶܾ$0w%l9b8>ּ?[p2B͞:wG?&to]}RnOȧ>I{c2a]A">؛]9EToI'4M yf3Od @k ܀Hw[&p#o)*^Q/[>wR0v2ƌȄk?Bcnc1p$mp/O*LuKr0%_"/dZ%tEXtdate:create2012-09-26T20:26:40+01:00-%tEXtdate:modify2012-09-26T20:26:40+01:00p.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sb.png0000664000175000017500000001061012625666430027133 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{W?gf# !A $Ĥ<%ZSe#Ī[VYjihY˛BT*T̫v,;ٙs=Ek~|sz>nos/9IT8&ͷOͅ&}?h耡iKk{iZb0R x^|qL hn9ߤeR^]ObY t1YH*ӾR(S) 9)Ϝ0]ݖ`VD#$ cåsK$[5--ZTúu ΐNOb!@@D`~ޠ>Xzk#l# ϛaƽDe0,ঐRby_1$U-2瀼21 $hnִg*EDss#`ݺFVh011ꀃs,$B`@5Qʾ{[YZ?agϚ*:^D?0MJXGGkrڿz793j  zڅs7oGYȂ)jkubrRJvg۶Ƥ~ ~kI&!f%b`'9p`eR)O!%>aO19AʁCwIGůIP_ hwiN)c NPtSW_Dah ZvctTaY``7wȈOʑ*VQGA29!Pr7:L|y )M WTRIvc-P F!tMӄy'Ǯ]LOP2)4 t&'%/8FoohGJسgwiPya5sDI',8!/, PsT+U2f;ﬣce+Wp8J R_sM%KO=5ȈŚ5uԈKFŽX@A{QzOOKf&a/. usv]8Gٱ_oe}}))С,uVS۶NKodB>$  M)PJEfpV;B/,Y/4Ao}< À~}o ]vNaÆ, L̒NZo5EOOR1jR(9+D1 }5Js|*h$"vuhjH4 tn˂kIN[PPKb|R t]02b(J.or#Q>`_Z DDBpvl//4Nx?m)9lVhe{ڴrnיߞ?[Ŭp<{b뷙BUjJ WVS > Ǐ~&3$͑#ӑlկ ^+-f.54hS45itue9|8KstN\^)ęALK%Ö {<=F19;ݖ-M5А tFp 5v= 7x g~k*A{{->;3*2T=ۗVt=G;2[s9:uضmS '2,Z(f\w Vp}sطo!jZ*W*J`Y[ظ EHY 3Rj(fнa4)5%K[Wϑ#A,Yʼnyt] Փ' hk46jHuw׳tin孷ue)2+WY5eNBBpUF*mCtb,_܃ P(ii tN0`ӦFl˜9 ZZ]wS(('yZJhnW[:t^I$<&'%۷gJ@ r+1 ѱ{K,r9%/fU*oexXߜGk A##M7000dbk?+jؿl3gLﯧ kÕRv˗w w< ^E߸BI*fa[>GGNo r%3u&b a:套Ƙb_yeˑ˩8֒fCk|q,_(΍q*.xG8ѩ* W@9S@4 $XznfY[,(A 8{R 98az1q^qBSx@Vݕ`!k+..VEsylО8.v3 Vu8x7a"]x~/X@x`~@*Y^x)o\p:Vwy>/ 8"}9 8EX|4 1PjqaQΪ [q aINQ@ LsRx.0?;#ѐJ\zm?Dى~+PJ4Wwc/7^rp Tq,}*?6kFRrKP*ej,\֎=6D< pvBUAX~'@m 'hJUF^ךfCR\FQa nf[*jWI5[)W6:N/N9]ʼnҎźm(#*8\ 䂓2 @+8ߖ96K/jQRI˂ܴ\zz\)D%^Y5&-1&[;cEpS lIDATxMGgvwfı$!!(($@Ppȅ3(#pB  $$>B";"vKޯǡ{{zfzgvwu__UWw5ܷv. M2Ɉ}\ipܽl 4Kqh<7~|GXgFDheDmrt鵴N۟j߯i/o^_:^\ tA+@۟mwʬ'[?ߘ#`$FlGquKI+hZ4.ƕmb%lMp5+7d =^m}Z E#LDԬu)i#FKe0-YJ0x+smz}gn]e@ןm}WFnKƆ ǐva4Vc@6JiS!B};Wז~ZTe&;+ʣ{E~Oߏ):e乣B[eܺKF{OR ȌyrV o0 ` 2/&*cLTp1G^ym{!#LbU6 KT)RP,x,X\/]hzvc@BϷ:ˇnre\OSO>8MO\(fb6`0׺F+=;*bB|;W<2EX YbC̐9=>"'H#*[ȏWa#n?b&Wnx䘌T`[]2HOj^?'HUcqJ͠Χ\[Vj2_G 8]0@*5$W>[Im5H|[-4 e/;|қ۪M3i߸ylbD-aJG0 UYM>Mk+Zp[GRz~T`R\WݭD -S"1qAstARXn\`Ћf@l.peyb6Wç Ϧ%`$=s_,kC3k,nfi.X Z|oO{8*~YaieI.tEZVAtי j4xnĿ'VBuoi*nWy-u'h;Fpki+H5qj{=`:0֎uv !&f5|;X l>רeLJpnbzu#{ƺUƽ8D6 k{!HFpd;A}0DZm(Ѽ3klK#w&l*zƖa6b} {dˍ(hMXuNcq\S~at;ODVe3hApd '`Uym^.򭹽Z`/^i]ć_yWr/^J: Hl}a}m|UkLN+pbf7 *I R$ FMߩ$3ʳ6|شsY3ꞙnV,>$V-I: )U%‰~4[s(|tsK0NBYaƶ%qkbwg`qVYrnXT ojx cC׷r3p8b<ؑ#f3Zb zKW=p=CtbN!;2p[Ʈ9J$cm; Km`50ԉTH>J%gӷYn@GРNI'_^ۿĒ( s!!2XTӊ}:.rx%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/so.png0000664000175000017500000000551012625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxKG꾏yx~̌0 !- (HXbo D BH I !!89g̒A> P@ 4n{| ߽clGf[R JT(T/qN> ASA@t~=OwD) hŇ_\?X{rێfEq3Ptu*g(6[ fh zܵ'>S?>dbsh5휝w,}orvjTRjNΩRApdRPb~vD/~;kzR'TE `}텇5͉՚FړtgLyyXhQIHD JhMF4ݨ=籅TkM:Ʒǭ1źQDv)lGsJQ3TDAtDMq{b5{?:&UޔHE16(D(PdL"CŨi` XtqQaFߖ$RebUܬr"V[ﲣk>1spA$N.yN EvyKs:NxE"rACgT>5AG)I2&;kGqU՚3ܾOQ6ye[47b{kg= B}}[!JvQ>lF4:}V cWY;3:'VT:O} _PÕsLu6s5*=Jϭ + i,;VNw]pL4=7>m Iyq~v 6> 1i}>v Ał=F tA4ܷ@xv;HZn֦.:vyۄ">O>=l;{9(.ySS07JB0Rt$6ݽ /M'vZ \pC26ZRiT539=o#)ֲVp۬cd]7/HZVt/wkh+Vme:V^h rƎ7CJCg•MǒA*K~ DSލ{v[麷H+ .-pa}P\߫֋@SɵN~^o/G[]yaײ'Rし,F>Ё(X9r?{We'M7\M1u_2×Ax_+]E*3B!fhafmzz]4[\iы!xeٌޑ㩷⶷A,X+wώ )M>"LԜY^5m9?z ޽ qw؆Q r-걃wq؆!zO` z`+V>)l}0S̊}_.ECI3(<|wq#S@VIA # `_ٓ;ۯnjZ`yZnFc~p7t90оB]O-A^gA$4b"k׸IՃr1i)8,s <5mggf<@ktg(xFl ۬E S'_Osd >@#D /2uuY?W_¯'M-Xv\@eǤL}eˎJ8 J9QT0tD9 3~qիZA>;; A'NIR45,\D@ $=$ӑ?S+udJ@4xu^( J!QLtș2B7[^>7!huнt;UK~;kJwZ&M_H.qln utwdcM@:tsxbfVIz8NSx8ܗCՍQ˟L֯нxrکa}x(/qzW걿'*{TԚ@i o{$Aw^<)WS!`XM'@4sRЋtE[[;ϾA`X+60qc-3(n J)-:ظt'$IpXNAn}c`<8.;)_h4I!].gs)5*>6LG)L^J|tI\K\ H=ҏ1Q7&߫u] [ ²H2|Q/, k)D JT`}7e+Tݾ%tEXtdate:create2012-09-26T20:26:06+01:00b%tEXtdate:modify2012-09-26T20:26:06+01:00yIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/eh.png0000664000175000017500000000575112625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> 'IDATx{UߙK[v).im-m*P R*QLEW "Hb )/ (h RP miKvwgyݻwEj9sww<A9(WdTc@qGY|hnc& (Vlim)um3[uvu mwg'b!^(^T(²~έRJť8YG 5S_Vr]۶rCDmo8 (NY˺"_*ϻBA)T Шi^54@EZSjY8\k=Wew6+e("UxdI(cїY(%"_, ۶FLWN-2 &D(+Y ܫO+'+<^]<fyݸhs PVn|J"hRJ1"gL3կ;) h<Ep@As%"(<+dGDf 1RJPSYTO.z`N@AƲ$㛭/qK5ɳ*^ ws 0Y^mtX+Ǫ/^d_y* ,LG"TubnV4/ pR 9 X|jؼoP|et'-"GVy.XOS1 c5UZ\r/E뚋63:nwjUh3<$BK%S;?ϲ˨η0]3+^yq 0' )$~=zz|ޖb+,JxɚhlI9`|/4({1Waz##f}8;s]xy{B8N>ZZ 0&u.OCxixv Νu;'@g0PjM|6xvLtxgSt%Fǁ.8,6ho7𔂡A.s|1;XfstxS7:WLZ3fx1bsϛwa㿠2ر0x k$wzCVMʎc}6<sN<<_,ɓ=p޸A!8uC&c۶Yp}7̚}ᢋF=xoԫ~  ²[`l J1QZݽ 7g_Y4pMPFغ2uԀYNc7M]EXxQGoYg't,8~k_6] nX#ҌCme LPhTE8 XؽFV<zC!mq\D[''OCDDPPQ8 4h:`/d?19,6f/?:ZSRSqaώ!IXWyp=ۏ vAգ*&i8sӾ^:ny.h0{-;QsOPk\VO.Լ1b^82@ohm^=ʔٺ D4*u|wڶ?9; vv̷Т45ٝ8Y_&?Z6Z#c`=w][O[rp$O4>}%X8p*uȵO,t:PMn#ZXe[a0LdžQ%S-:жN55< яh\\F};w 8;vHb@ h xi20:D?:.}`o?W1ۃZ$sH|e`cq "I Gk̡ 4(#.f`0u͌0&;PϗUl!e$lT ۃ2S;V%tEXtdate:create2012-09-26T20:26:29+01:00Q%tEXtdate:modify2012-09-26T20:26:29+01:00bIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ee.png0000664000175000017500000000376512625666430027135 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>3IDATxODƟwxӴ@wي%pBS%J#G$>w>T*T \*iPDUvi nڞdc'JMJc?K[ҖQE0Y9Vc5{MA<@;N瓷o2D "4}@BTM֓2O})̶P{&7ۜ=&[!>?>g Apc DT'Tsż6Ir(?Bm|U@,A fS/yDN'tdU59];#-MBco8<g1/L Q|IrQOE_Y-JWmH D1#ڼy_InPmDgc/uE jU:p< :Nt-?#Ʊ `1  9'Ʀ" kST~ +9Gy@SMDI $HwtoWu$Mb*OLHY`%cS/*+ H1ZLLU =78z1]S( %s9irYʕC^ ʻ"ͪG+X&OFU޼V 4>![y=4xz[V3m56fg\[ylz)D@ l)l k`Y\IFs:k3xeRd;j+US^5MLok&cm~ٮUun#Eku]vzuZ%@]u6&`crm=[h&lZUuΣgoggkB*z\LofGӦgիT 4LݎCo+1ׁjKu/#(:Qk6mgѫI(գE۰tzmBtթ김5hhP{Gd{ mpu&hqݨ]u;۠NYm=:@TBɍoԾi2mՁm E1GW5h/BBsa Ο?ӧOĉ/\جZOT!8h4ÇqE\|u9s~A4Qa8buukkkֲJD0B#yBߵ\ٖ% dy\cA:q ϻ5r*W9Oelk9G$HAJLfVsq8E4[DSN(E8nph4 Ð xW.6s0 !ߟZd ?IDATx;Tڞnv7EQ| D SR" ФJ*iRd}5E0} mF_ ]iBRqLJٌ&v'"xnIξ["""`NX'jL&2{Z&<4kWUm1*~3P|#|~Q~*v|_1 -UD2W*K\MX Uśbep2r89w\ OxE=%3lJ\Pe[;p`p>G qL{wN=#l!dAd2sI@Utԙ}6Xc}&"f+]M`r+B0c&c 2 VB\hm%B *Z"l/o?7IO"{xX;kpm,-lK$͟\7`"kwyxЙs܅Q=I9ɖoߢ= 2<.T:@7a3wAķq⊖ 2" " l_ظLOa1V0#Yybs46dGi>ܳ8t-jQ@̋k3{;+S{}d %qO!75 7*sæIi_˄㜨E"D2 !5_J5l <ϫ1 & ?;rf۞?-*QO&L=~w@9{ƛK0{LdƊy*f< mŃHX 6BhNejkbLى|%UXLc6ripEm 6W\Q+jDuH̪ rJ?hWյƝ]u7ҷo޽{}:`~_Wlm4xp8pppD""Zk'2P+"fE]S:RHD# ߺ_g)JZ[w1Μ~v{1F c}@CWmoo0|? íz y$"薪ņ{E5^=ϻ]f8(?pC9GQ WtH ]7<'WJO>i'Qaa)aH]g]x%gڄ16_,Zu;Ti+2[u`H~]NB Cb%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/nl.png0000664000175000017500000000434712625666430027152 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>%IDATx[Kܶ5;9N쾜fiv]4 h.@B].M6Y$@] @BI؈{gcf$dAR")J#yd8GOG|F6 = ܦ-'Ox,4)%n&޻}\{% E>b<[`|ڲ,H %cԺKըXf*:H]7ɆeC!pEl\Q6(W +ʆi3#h*[bzk=,B=rvS;x[9z+6vv2R9%.N ,b0RlF{i#i:Á}qA s*`@1sl|v; @is"yQ_/Yp~ȐIb[)f`E`Q[1"]QE.}K]gX]C vN\/ڸYqltbDdUӍZ\Linpd甊R:JFx!qnD+l"v%3joit?Gb4XZƒ7~hb{.㹉8>2+˯vDp}%H@(ee\ㆫXK {1q4/]3f \TH A_ nөr}IVX|U,sbMӦ>oe ,sZ08Q@|1@,ȬSMZq JΡI~~.цJY_}=o$ KZ+)ӯ>MF́9:+ h$"@!/[gy)" C6@u7$ Ol1(L`&GgRpCU6$^3Y7Tؐw% `h=1wZ#.\& H5Ս!;y eФQVa&K"ݳbj}Vl/ίv:҆ pu#CzsK%tEXtdate:create2012-09-26T20:26:30+01:00%tEXtdate:modify2012-09-26T20:26:30+01:00'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/to.png0000664000175000017500000000503212625666430027153 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> XIDATxϋǿgvF!e:,8`!؛kn|1%Cؒl cŒJݝ鮗CWԼ%ZMOwWիyy^~Ni(\ES! M;9/ ^:7~6M{ MysUĢœw梗ljlr8l[fc zY 9Q4W^v /~eeh4 z=}1(MZ›o(+h4(cvṖ.Btp|}Zͱ,Kww/N>?̻^VZ_^9cvvin<} ~h4Z#=8@VFHC"nX,ޓ="L_&Ͳ r`kyegv\BD$2p95(ϫUZD- y%yh@sY5]##8?<,,sۅ><;(MX@G O^cn0c =PI։A_n@Iҷ㭭Ed~}}IC5_{ 7o9cG),{$cjDp?dR>~?}df4tt磏2]g,^>Ht`@ ,8`=I~qIu*)LY@ xYxGo#[5 ]@@\59 s b=x6!+Q3l "_a$@(QX] G)j#1vPzdN]fILJblq*0(?$ GiLJ%tEXtdate:create2012-09-26T20:26:19+01:00XUݲ%tEXtdate:modify2012-09-26T20:26:19+01:00)eIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tw.png0000664000175000017500000000715712625666430027175 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxil]gϹq7mV%4CR@#D(BH403 FlU !@Tj 4`4 mAHMq8N؎뻞g{zo\ѐ:gyY9pu\W;2{% iOfupyx6ݞh|osw[PܩV(""YYD|",isjXLK/M lI _ݝ6li4O<2[yiX@+ | xy3k ) |P^/ 4oN\ ymk"Cٹt}00gQo[۶7z}z[!so^NuIZ@1_ȯy- kg@) X ,-7[n>}cLW.*0=W諁>&e]i,-1fG3-`SvTF8?K\}:δǾRiwBu},[q|3PxM[89^<К2vvZƲ.J=36X]+ҪՠVg0d*OwIm; (,˒]1׌,]j/ [Jqf \μĵ'2e&+L,;&B`j)-[ y\%I&.&}}mÁ#2l(Kt@ 'V1@82b>LJn`_sNfչmuܺkkK\X\|C z^W~/,;hz: ?Qc.PH# D@&t G5Zor3|o3[G~o_}'7mpCs;sYFaQď*n\ޘ!kyS$ykJ/;E7ɳwS1fYGgg8)=}>;7tDxǞ-|ηr>zyN_L% Z1 Hjp>I>\W'/jF;6ZnnS'ٶ7esޭ|szrWNGœY ;JWYڊg NᥢkaYnYO!]%t v=>7<9Rma\ʹ;C7j*~yuܸkrr!\]dǦ~!vlXȭ D4e{ w6:?C:??)޸u cWi+Veb"GGf gq2ԄH^@w+*d`5X+mf=Bȓ{2ur "}RO@K:^یl=9b=]y>'q>R(rnc|#_`_-"u2oenI8FԄ.wmc>1AB_垟űTj -jONw23<(Ezv y+ efdF sq+ /}c @=%f]qM;]|/pyy )Y$HI싂eFŨ者 # h4Eض2W6[Ucc߿(.xȆ.Tj /0gM7Ϭ%n&U2(wb큟@Ký4##keH'N.oadla%x'% )Qm 4ȗE > #cs%<{Uȱ/q2W %lMBIlucY"2"̌^w wTU*<%,N+ ?>W[S2& S UΓonI,88LLa1&[3yްyq_舐I`o* q|3;$mZ(69BtE`h 0a$OU "S5/,QiT,}b 4[>9+}䶎ܖ8SG^3hޙבglhEe]JFG%x6ƹ?h?,#2y)EQa"'ݑ#n򘾲ͽJPT1\F1[p# ^-k1DyIE~+J "./a_uW@%E唚S吗VynF=N Դe(Í&h0gb=eOYޖm%hiLQJ4+xdyCIWF'fM;'Y$/ @&8 -?3TH6ӈ>u"]Lch;ZxCgKh3-ʴ)/-rbtUi bE^_#smYZUoq:7@엔zUUkRp3^@ǿD vZ_I^ڡ.Z(5*U[uI& "mgϷ': @S8 N׻KM%[_\i zl -Wi m>6TG PιI u`" "i@8{9p>+ڿ6kV#:f1˝sr%?oxV2L IMu\A]WJ^%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ad.png0000664000175000017500000001151212625666430027115 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx[$Yߩ\{fv]{zH0q'<PA\$/H<"$xE!A B (q6\&wfwfgZU|&;pжާŮHk*Q@FʔzsͲLlUSU4"?Q,TCR[vtȍM6_k&cG4;6ǰ aRNAiDLh]6bdWՠ' }KΣ [Y=6{6q!xCjxf֑rPB*K}z'R-GӬ{,HuUauiy+5ZAWQW P  Q RU*:Dx1Tߘo `}sF@%:ah7ͬy7sJmNJ:\fjUC $SҪ"l{w[[ ,tЋkQI4sCjb}8Puр#t'!Xv Q|OߢڵJ@4?G1{.78~{G^v[B5/=C.P9yZ=;WuwZc:^NF V+9ʋ8i*-M!pU=~πSYcT:uxcJѤY2(&t ĠLƾEa$( vd,}9u&'x7 ]16@,D ;؎Cq &dC<ڧ\l7NqYl!hgO&FZ P<%3B(9aH9@IR#Rl{oa 73h7sUSK|~TvN10o<l|ސ.3Y)sqT*BOT*^ Bg~ܦz1hI&I.!:I('J$`9#:#}d0MAYz?q{gN^G0If*: N_;.Qbr[ww᱌uLʍ+M_KWc"7`_]8G@ ̘W2l* /~or P9Gkߡ}֠!B|U4ͮrI3OֹDZƐsHF `Y8wˎYY iT*{tVze͑-{B{]~.퀼%@`ClY@l6hYȈ8:g-s:k# |1`п,?)4B, 8X>֧0_NUa4viZk3+SlV8sK2]>ÿ o_ںE!#`<ؽV{R&jʭyQ3o,^άjƕ相͈/m92Ec/O N//br9E0k{E!^tK\nvYC?6;̓sP;E@8G[M= L&b?:y)`y sxZ ^T#?="4 }U1եL@ c;Qa@:9-ˬȰ8o/*>雴3,m7;|߳Ѫ7wuP:Qй ǕCB(#Yנxi"I0t|Aoj' {, byxygVv.~Z ^ *-L=Q7ޑWYyMrJʞɄxwl\?tǝν~ 0J#w(Kd킇Srtt۱{V|KA+.pė8ׇ+'x-3Vf+˗Ng|>b+Dۜ!,sB|R96{)I`sM -qS)UxH̙;N3T 8c@ׇn C#dU M O  WPyz}K*8UzK [cK?,\hsNfa=ڍoX|͈zkIBx Ƣង= (n*~>H 4J,k!5 5ñ*p230'Cgq|. x o$gc;5=גIA+ѩnnϫ$ah"e% ̹GA+zr`Xd_q) .~)F1F~;9IeyޣY74놹I  ,y9lzna- pۖ**~ӌ :&bnfq 7&KX:z}KqdVr㎲ӵBo`- dݘJfDkkw;ǎU1^͚1B$ꑉ4 =ʾp^ҁe@(~ eq~(|[q #ZUK1?piPp |Eխڐ-`S293="lͯmcN?~Y?!>(IWiRZCA\ :ZepI?V7^ < NI90 W #ec"YσVOz`;6]ʀgv5܄3 THnLX_x<#IKOdfz͐so5o{gC^"c&'t[t5F~dZ2+&N#vq:EX676/O[1Ukty5mOoqy_u LUj!y6fj7UjqM*~Ø;:~p3v7WWu`#r @ 0Ok7o7H [mN ^}>6̌PցPn j@N?F%0);@'aJ"kX#D"? 1v~$d>rbR9{=2eIm?2cZt( AE'I%tEXtdate:create2012-09-26T20:26:26+01:00 %tEXtdate:modify2012-09-26T20:26:26+01:00QIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/me.png0000664000175000017500000001166212625666430027140 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxs\u}AF--UvEeYq)N%SR%!NRIlYV줬XMoZ(RE`0s;˝ j`۷ϯt7|L1%~HSI')}c,?w{Q*9X!<9sg))yˡ6pU"'vN40&js0cy*׉-yLC2+ior/Q_oBaOJgW:xNmx`~Iݙ-5d.D&,RWGsOXNȗ||2#F0wאh(BU3 :D>X lņ38a^AW`-e"͜-O֐t6D7r5@/̆1SQ硕*g஀LX3W +#So6|,`' 2]od8w]D`<5\ ܶ"7‡&њAŞhkp`sOڇ k-h ]=ZEs̅BpO ! pM lEs!3`*۾!xrƂ1BVJr:&40YA`m6[pO Ra rv T \G*y}B;m=8 ON9@/W\G~~<#͢O'*)+^[0@v>ױ>}HxpG#{U|aF#+cSاQd;G_DN1X' @Uwџ#[8̣ӄOϣƂf1odEK.h^JBڵN**߻pnH 0 [hP}ѝo&, <2V%]H[+2uSkXt;v$?RpvWρnlu9ZHtr|("T>2K+JRZ"PlM rY'sW6 R0x+LZ`~{sk~DbA摆o }XV; 0g >qSI>uZ|Pa`B!P" )vH͠7;aCEJ1]*LTAuf&,TwXM<8U4s[{3aiõ&5s#W=wP~> PA[8 `-)>z{Qݣz^A HPLxgE\K+m{N-g0SKxY;8\d#+<7Uo4A;X[TJK_q7ӑד!iO(ǯd,>  >85 ygpr;_X|S-CS]+غA2zF͠&!8j=uXƀbn=-Ԫ^$s}1Q jTЙs'p9߼LOdp9y&,WX&;T)&McGn ==0^ʲ&#ިhgF/q+ rt+DtyLTs|a-;øıi 9g^b"Ms\mΓ\"_ۄv5=iuW ¥X БĮ OcU.0"$ t##uϔ,vljL+$!u^vz4wz +FM bp'1LXu4xC0d m#C=l!uk?Bvp%iKv9bV ϡab=c.qyc5INo$ cC5s|##y+w$$yǃ㡏ܞaUkwpt!T'{i ":[L3OU83Ac_c3&lƆ|i96Μj[i;\z =FAOzu>7fPw![^@ Ss? +dKD,| 0ar(1!3uE;iւ,Rj&CBz3Grb_쯟ddxޭAj~l+&,p"g.lEG!|=REXnztQ6sܕb_\j5iŐM:reH)pȈu;zNMZ?ZC#8]Ja.ߴљ"_y| c͌$ejH&hT ab" nYr/<>v |{H)u>1%y^;sE}eYÄ)fo`~yj'l߉>)O`3%lS$j@#*_9}%-dt'y0=*VIw`/kd-f^Eb S3Mȱ*\I72ϯM[ǪL\M[# 9L`>9'iNpWBDƗOl ܎~W &R[\pۘ 8ZsOu†4[M | ,\:.M`Di}k[vjKu# \i5J2ŭ) 2i 9ѽN\GS4f%9ߢbbci4*|}ggwGZ6٫Mn?DWb^J ~Bo`XqJOo@6[W0I~g`&.SUӸ~jm,2| pL^{@>~j4iL=|fR +ܚLJs+vbby8 @v i-HW!L``w#%<ڰFJ7[9ٍh"QG%NA|3ǭgŦQK~׋>cZZyPfzر'g.0W6j '+~{y I|?qNKbq̾uدGi0p.AZv0m^Xu~F$<븳Mxsu& ظ!o1w7{s:y乒9%4mmpmVfdDW8DIR“3؅+ȅvw}7(io~ B;vizM2/6t—QofH C4s:鍤;fqkaG3|$'N~hn8}eu=ۜf4$1=-1QUGvg{uH)6qcl•5 76J|Wo^V" & $i!} 3/]ԺȘ́X^Z,M?ИlgS5|Zno30Bqd8qbcinz0ځ('q3tYEpdRqu@\|u>j v 뀴+j{6,xݛP#FwQhw0vq@:)Jq1"O1e%[ʻIRq9Yu!ye"Q%1}Gzn}%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ly.png0000664000175000017500000000607712625666430027167 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> }IDATxMW=3]5![EI KY/dk%%D-9ľp4|%%ms+Q@c1 aaggz^3=ݽ=`[VOUz_z?@y "_QAJ%٤L }%f<-!Ծ[k>x#bꈈG D#I~ĭ+:]XِJ> 4jXm_`=滝в[ِsVE C SrkQ.0"˜Gw`>%f@Ӣy"bJD/cPq"`uwIrV7L}}]a} l8QeHeٷхFb9ٗ<IŮHOhMW?D}HgsGw0IhYI5XA~Prc\`*%C_>p$Wg6瑾&N`mFݳgI1>KG8|>5bR8U{w}"jvBkvQڢlQ  j(s[}1<-Wm2~UOuQ<ޟJt'!o_DIDb3/g3b(lp pXb #&Becpdp ~cJ#K﹩% 0^EhF8#Կp)UM^0ͤb|Hҫru~gEDr<٭HOS`V * א26xҦBy=68_t\y Ru<-+<`U;`ļ2FСC޽NåK8wW^Z[Y_0.YU^*sI=/Lfvv>5/&m >o ?~<ϣlb^3==ŋv;6YY(eyUŔV=i^YkSiZˑ#G̙3k\x1y1cǎOt}FA(|~|7nbٱyR)V8e[nfIV+c| Nwaii)iׁ&x7B,iM1~:wfϞ=A ۜ>}z9n޼o͍7xWضm[93Lb`U)eCʕ+rQfff㏓k1Z-N8ɓ'`޽xWzwQe2{"rX6]A0??ÇygYYY~9\"B^Zᅬt:iZ0?5`p5, bޥJwhii .pA^|Ex VVVt:4MvY]]n石QئA$okieF+",,,0;;O==b޽t:X[[HP6kf3s-h;EƘ_øw#{77M&&&X__8TMZkoU"pppw"E^m5^VtR,-*Rq Z=)j|^,*"cRlAUVMHo|/3,:jue}jF&yˤe 'gzo+{\'/QZt0V$DGQT{nJ` b!`M40#5Pw Ĉ8?*:{ SfKBΊh2*]pM/ `mj=ԦFun5)#2͌X6SZQ|/7h/x:Z^:IDz129cnT͔SKU@+jcR6$&@cp:avyT(Adn =p\4{S E-VQ+^OQʥm}Zt\m!-hfę-zC 3HG+͛5Kg:^OiO_ZEF]EĢ; LV2uxb5yƛv֢uXmNQ^/6-Ҹ+ߢ~vڍ/R XkvbA"7o#ջUփ'`Ajdž}0~P ٘}'bQmw7.\ ܌@]y=AFM>wֹ hAZ[u6P e@ ]NAN;@+bB]H$R}tMu!֨2k,0>[o^I"|b G~-HW!+rUM%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cr.png0000664000175000017500000000426512625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxϏǿm%B@ $R$H8G⊸'Ʌ#M"Yi%ڝi]8]vWi'YU~{~ֲz@eWAO!8GsLa M;ty 4HmFoJǞ'"f~u=5lvtῊo@@ B"qdW߉KW(BX,.9{6Í-<35|H}>FQ:O>(( /A F{ 1}@%`4h QF֍m\v<5ώS9\L|j\+ZKͯvKEQtE OB[`pypDQA HO_!D+D1\ M53߮k7B2\7>Dmc'Q q+/G.#Zm"!3wCd22x>WF{zS7kLs ې2 fRoT kmF"ȵF5rfa %e ^5Թǜ=. ={PZ_,>Q"s#[Y'0մh|(t]Cs6α < {S6oRpJS*A/3f`}r= y[aZQ]Fg3C Y6196.} D3Ć"\2}8uv]c]@jHPQ^?kgK0x/]*x~XAa),06L6l W})ӛ^bo 7 PgZx[w8,Af.D@ `x\S q ?&*`_ 5w 0p^2HTrkDB ʟan/a' l'4ƙֺY͓lY:*?+6 y K(^Uz-B~?ݾv%tEXtdate:create2012-09-26T20:26:25+01:00z%%tEXtdate:modify2012-09-26T20:26:25+01:00`'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/tj.png0000664000175000017500000000527212625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx_$G?{]λ>LD*1AQAT1⋢!胨 |HJ b%!b</%w%r3{{;3]?z{fvwflwuկ_կpM5&"sʻD$ `43EeIAA饀V!@n]8#^/jC_ĕ%_YNMғYSx\Tu1zl< 28vC JVc "yІשTS48zf/OUs`*ZA^/G.<ΫVR+u'Z+_9H+_Z^(-*%AM\okޱx:+WZ薦ȁXP5: W*)Q*$R᝕e[byL~9ӌ@?+}kEրZ EԀJNw}H^'La5s*"e5\VxL\$ߧvs"0}Zmڳ9m_vEt.+]|d32P|[){֚غx8蛰P'/aVq~~9Nv ?*iF <(>pdge 2[%`ggGĩs˸ݦ]G_W}ޢ4/"ykW,%\Ryg(_Ԁcqx[+pn/sp읗0 $@f>;"XQ6N+$9{wX`D;I%Zgă#\mDvp[c?6LΩ%{km|$0m]c RD)Kk5vcbqc]ŗc=m0N,Vlz_*i/LrQ7 RwV&\%tEXtdate:create2012-09-26T20:26:30+01:00%tEXtdate:modify2012-09-26T20:26:30+01:00'IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/pk.png0000664000175000017500000000651712625666430027154 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATx[l=wv;H@D*QD[Tm)P!*-V'$PAD*.-IKZDJ  mB'ݙ>mr\\;"om^gv'p^y9/"sAT+'&`h"Mj?h#05k}W.[BPB^Hneߡ0 4zC[S*3[} R=W)PJIG֜3`) ؀1^(qCwu׃KoZT,a4` }4!k"HҫU*_\?}J&BIUekez#3c@ } |-}}}|>O@!c/$g"*T<@xfOuBRJl6'OMT(G)&=xv@/\prLxcac1mM3]q๚^_NjR>cȲnqEqGmWnwE\^X*( I30XԬ9#&~LTXqK7[}i:HG0"'q9׍f@ٱ M>$n"[J+<纕^c(,[E  A 00P(gpC\Jonq,/ϏWZe룪|c:ۧ[cC(zl"wK3-I)ēmG[L2n>EB`8 R yVJnr"E +. BbkBz-(SC0=7O_kh|h/n~QN:_юU+sP5܈VT_ʡ\(sݥ1pqբ>[}Ύ X;RJ^q5,Yxj q%ScD:!ȟ6使d6Ŷmò멽B)Iwrvq^6H@T>:8a'1D::[)e-<|LզxX3vd*^cs, ^TxY JƠ ѢH+o1& -B^MoYtr$:Ͼ,c'GuܼAkHF #> dN\8bH [.8}bj)e#(GO_8O 1` |j=F#B[<;aiRzJ%DLD*~wb4#}YeR6Hktˏ720*>TT(/pa$5z3%~KjX"b2= &AJE1_rG=Y9*~i01۷ˆtfz"2eg;deo9ta4?.lzmfzKqܴ1`Jw{uq5.Z}RJwirnN7n-iև?a|r o/N5~[qhT%4kI\ӳSP1J)ml4MRq2jdYrf/\O0t˲,z/-BIɭZcTKNj``SSSjP(oѲ-N%콈5T . ;:! ;vtOOOa2ˮ(Պϥl=S*ų84 tݽ~vW*CCCR >m eC炣T v;'4pjJm/m{V֭]wmooo%gʝl s :g_r?&3lsZo9|`209P,r˖/bq⥦iRM>>NWOXY6g92=9/q>b@({eEtOPǃt8|Md߀ HtT=)Citku%I9ajY,\h3efg?P+vZ/:0nh{j{^B 8t<%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/sv.png0000664000175000017500000001046212625666430027164 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>pIDATxi]y;l6B!!)!$UJm UTEU+G5FQ!D)1<6cg<;w9ݷl\J;xOޓ=+J{=,'Zt9RfjΙ@ہP]{"[ngcm!RmtyD-ƜHw5IOjΉ""w 3#gpVP 7>HbDw ZkRUpljXRo4_U}%`R:Y:&MO-F=|;e)hxhw=11xG% ZkTj Ck[yJu+sU!J=_gt頬Sejّ7a=hP"& U\n)A1^Pմ 6jWKEᨼꫀbu%B%6u4l*fiV0[[y ~OU+Uz-_"UK7.wEV6]/s6#A.i "J \eG,zKySjl}nh{w`46MAb@Č>8"GZ¨!6)w5mtq+WukhVQ%,7gp 8!LDd7bN omDW[L[Y5*IHxӋGYEh%&0zar%fX7eJkil+J_M<9YüN^'R@>Jg}duQ5NP0Y68ubd5n"` /C7UOyDWkm2% :&]%b2FZ bKN5*cںr-V7(\#jž JՒ6U7C !V7*&rБx=unI"aNҤB٢$NF8_s6332  K9JnA" t{1~4ӇZ6T33?N_:\ftl'wP"4Jqr</wMwWF)PpssA.^Sڦ^Wmvl c#Ļ0 ^~,4w8s_T / cSo˲f^$㓢e,^Oŕt6VK$}!Ķd2y?|{|X4c?d@2'(Gx~UiLj쿍[nLY p>,66rd)rc- g>ɹuL?7!~p{D?x?!❛wo&v+W^lfPb57.Nz/̺<?Gawb#c[@? =ϥ#xS{#Plzs_\ts\ [C_zGlOо8/xg<+?ӟD2]cx&zI#@)vhr#JYWe5xP%#)w&w۷{ӊc\qYk1Y&n||姛q_a|&p0u"<@.PDgADyye<}xϝǘ7v[GIO[춿-<1aIϞ@Ϻ|޻pd -wumi|7Mgkԁp'=c|X('nɌ-ڞ^o FBкL,&od} WC Nf2kUrB,.zr scܼG?9 E aAz\۶ pnvϽ1~su7RtߊW%W`9b:7>Mj4k8Cgƃ|]M)XX*b HI^_ǖ#MytgezdT3t%H o|\'=siƗL i i8*YPH2PZO6m\U(YC;rQ[ٚx)rQ1]8vRvxO5o̾#$x5^:B{l xo19ٯ7"o 02> dRζi Z(+J7L7AjY] NZ>L%3FDu>QLS\H}al|fTo^O6@d=ݏПJ<k)Μ}3[Pj a~EC6r[gaNqJ( jk`r 0O_tBe6,:c}:D4ÜS JI}űp # +++X%eRXyeomHaAm梊-&.cVwbo dgdR,#قVA"f'nΐSdsz1m-4]Rck f5G2#nN-GmaĠ,%)Q`JQ]`%ɀ6UغEVOօR4lUZ=f^&#Re[z:a lT wiew+ifkJ% RkF2\ n/(<[T/ QPR~Y"2յD,\[k]:p ⳌK -]܌Ie^}EXLb4P {-Wmh[V W*ԒeK+X1FY(2)ڢݠ1$sڥj T.\1@`|Īli"^Tj^xE)""]tlm7.BݬnAi+_t96f=eFZ-WNVBӆ!X!}y>TZV^fo50^!4:=NӨ;v>u!+ bk(ddvGha+?2PV;u& "1xGO`cV!X ,kcǚ ;"JR VkJTw0bB.=qqnzt"0QY DJ{Kn A:כ+P) T ^%>6eYPtT j;]GRy SԈ)}tM?B^b(u.Eh[7u'CVb|R.~ͯj!-%zi}O _w3b%tEXtdate:create2012-09-26T20:26:31+01:00)%tEXtdate:modify2012-09-26T20:26:31+01:00X,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cu.png0000664000175000017500000000706312625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> qIDATxkusz" =vt, 2qb[+ ) Se*!Q ?!%BJ8 AVZF ]eтS}>}ǹp^y9/E>炘f ( by>Aӱ<!ۅ)m{偋hs F D5EQkoR gBLƄI;z!ggzʀi x6ߐku[3OƕCg,*-R, JL`BT6jsqq/g +A:ڙO-'O/d Ĵ:X@q/x0o-Eikݺ 7k)bQa"1Uˌ7zLmm2F3BPqxK[m#!L pe_~!VimZ<b<6^x5?]ɾB#Iun]L=01+hDAF\%yb߰;뤸r,̊"2(BqPF)r2uC{ٹ`O,Ӗ55:~[}f cC"bO-9ˁ i  Zo>(|e3=͟~Gm)6\8$vODVW $@E}ȧIQhX\/'G G۬iD@FLMJ !"il?(\:ĮX3yn(Dj=+}]VY_x+k}Mp5?8kV/ip}U*Aʦ/s{;G{ٵ\Xİ 5 KTa s pS6DuR 6jPl$X/QXInWӛ\ Mc}DZw8vݯjFA0W?GaW}dsjPo@IӠ|c*#?@nz݆ύMلhwt4jY't<oY}li`3b|B)ѣ ?Cwމk\l{p%qwq|دRދHbx;: [`Z5L$cF.:HJZ)}YnÇ'I`I|c_r ~:d1OO6Ǐ 0bF0 rc]Ҏp| m݊ 8gX l+8,Le>=q`֪UH[}{C>K/&}7n+1HiqS1iYZЀ>R;&KFH.Zc_qj\P@-[?v9dJRA'DO~p#;: q6u-M Zk?v0nfT6iTaYP,b_{m1꫃| ZrU]Q^rN807A1?<< t5צ\M6? gLvO>g-ߏueގjoZލ3S0-;| \~jο'J%ɞb5Ec8ftAV'l8RcJwߍJ#:ܻ}min}ƪ#Zj SB]c+K8P }0!?R(ᇔ~#ܝ;8"sbu~-6]Ӗ6uVlLn$jȸ`~kWA#ex=m@ W=-{Z}X.yȼy6m"e p_yCuOd"lz^+D#\eW;v`J`[An?!Z]† ]+c~ DZciC3]}Jr-e:0kKm#nӚinS)A$̄)9kZSش ={L ~d%YoH;>O.]#(Y֗X\>֭a_sM0|F9 LZXs$#cZEq6r7S3.  kF󭰟蠰y3NԂ -0~f)e-467L~]%~9 D5iL Fbh\k8+P.«&I.d>sҐˇ'\z瞈I պ_r B\0ns JI' YNg.n eLovZ&GO0|PS oŝN[,U_Jj: h?we~?`Gtb 1Sjᅹeab[,\KE6/ptp cRi;T3Pe[(S!xJs{ JD35BQ xE6 rƘa 7•IJE!b5(Y}# `ͧ FuE;!iզ-pƞE3w@,iGF͚P} 7 +:>}{K޽+-"Ŗo͸:na+TScxwV0_F7W^qc}p 9ZBc&0گu5ꇐ'^Q>6ϑC`CyR>w:$0rD@( H0}lw&\h#T?6haQ; ?+/Сsi%tEXtdate:create2012-09-26T20:26:15+01:00%tEXtdate:modify2012-09-26T20:26:15+01:00zIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bv.png0000664000175000017500000000660212625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxM$uL{f%Vh%$A2"|YuWǽ":a-+nYVюvƳ]]?|̪ȨȪ ' ̨Ȉx_xE&O/.f@ĸ^ t? ^tzq֔,kk>$,IsjG80V89.N_G%WU- &>*O+Be4=y'~. kC5zWg/~_tZ@T Uv7DLRO:DAcp!=fkא4]T9ڽ8_ 5;[,C-g8:Q 64|~j{q]vD0J("t~OCb4LU[HLDN1Kvtn~ REE_ݫW?$0Z?SC=eLKp! ZP$e!)$R&;BCV\-@; H% 啕5h`HVwJ"Qnmq(P}Zk 5,}6QRx>lяyPVtl1y79 kyZlg@X)i8\vׯsK!r.й`=Xu`zȽяr+Hb޸T14ؙNIw{`gܼ`0>C5f5+l4EF}ޛo6m8O2FWΦD: -o&)yn3,2HrlHkHӄhh<#I~ƿ+hT+cչ*.(FsR+On_Z¥(kPWi}/Mզ`Ś<\s|tɭ3TO;sQ uUTV,Zd_o_ *%x"XOv4R UVVu2WV T=`@ VUOrA-W*Ru8Bk2*-H[m%5,}[_Xk'}[\,jq*)-:,@í ^.g+ZARjGS+i9Ԝ} 7ܖ+5aݎ7&p5۸p<+1V ~>=AwwЪrYћU]3*a=FJA}(K{Z_۴R_mFXl%~}n3 ܼy^[$ I#|׾\wff@uFŶ7^9E+g092 9zϠ CfE~ja*5GnMGWo@[֝sf݉E2~ךP$nw~?Ƙ{%ą;%q اί6tb)!ޔ  * =+v |J Eaei)?к>G1bA.z%Nˏo>Wjh\ѵ~?I~G#5G%tEXtdate:create2012-09-26T20:26:35+01:00а%tEXtdate:modify2012-09-26T20:26:35+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/hu.png0000664000175000017500000000431312625666430027146 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxϏǿv03,I*R$$P8EKP"HB9Fʟ{\"$8(H(BZe a==ݶP?\Uvv=zWUv=[Vsoph1 2ầY 4I]A2wv.""ԵzU2 y3jr߳>v2̬8/J;/>y0Pcc5!9yͻ;;o ")ln2J=S+l fƢ,t^~eY_NB 摟ώ~/&5}\q NI;cɐ,Qe6߾u+q>â/j@}_N_F\#W& 6F{k<`:^ݗȉ|.kϏy'!) / =;.Aו?KҜUfm5mj Emf㣜^1TuLF A "0X w!vW{#.A!#F BKT[Ԍ@~P$SBc]Eklw5^ן>.U'4j @\j6ޏ:\[࢕v}cl7T]p1} 16s4 o̠F KA\ \M*#!F+j(GZ(iaP:>55D)o PԷtȭF.HmշtTЭPj{5_#ַ,h8}U}pwuz+{%̲גxZ]?}QN\7%>{l6v$W~_`o`LfJlv\ߥ΁1}s"k)(KNP}8y=8<05W~;{a(`'CH4^HN~;O~ b=JE9Ҭu>KW|d}'8<09r6R }; g}κ|>+1r֞=Pzx \emϤpt"3l LBüa0UNtSbnap'ݰkU>;uL*Yx]u؏@Zpp?}qp1]eo; 1d$*0(Ӱb^sބ95ܞ)s#6=!- g~w-=( w T, ~sWv+:, WIl1&^zs!V3:uz$@R.7$ۿhN^8D\%@V$~&{ί [+{M7"U~q&v567(YLv!ࢥVy7iַI&deib.d8X?_27zF.e։H|:{tٻ_!a>(9YKL<s ? Hb~S. xO/Gs ֐&wQUPDC-p\jN$B@t u#s< ֕Ph>neZ.$Bm>#& kgI>tAmNr:h%tEXtdate:create2012-09-26T20:26:37+01:00JO%tEXtdate:modify2012-09-26T20:26:37+01:00;.IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bj.png0000664000175000017500000000560312625666430027130 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk> IDATxK9ǷGҙdxx(h$F `,kXزGf#X`1X f(J Lѹ}o,.lWBT*sS*yz7TYHSH& ̌fUuY%`p\ {gBDD@wDܱTx/ !tZ]A#9s U9 @ro?~ht9M^`җj Օfc DzXZP: 7.Rl:I IXkqi?OO~n8@`\W^Y|uYs\:r Q ,Ё@3pcWr`*R+L!2O` 6ݶ /7fo}!|A]z{ŗ͡-N&X [a$_3R)=`bu1 -6ۭ^׾^y{d\p(7K|,-6 `Dn\[d On;.;Z4e%[\( `Opx W?gnp@#Y 8(dp<U2`tm"t`5,X)T2Jru 4 K\k0K wU^/[U'7$(͑2z X < }$!Wh[96Q& ٰ/y@ȏ ]^j 0t@%hpCVZ,|rwixnt~Yz㰠_ȅ,,0݉V'q *wN m͸ 9:ͅXK3$YoίdŌ9T:ɰdUYfhsyL=B$ j⠶ oqi;8e7.DE^t$/7?80#V .t$-|A{n7@$ ZLh;`F1Q\X}2N*/c %ⴵx|b]? 005@ل`+8~s{XC>#F^Wl qHa=\j*Cf[J m tOlHWV M8Gf7: U%Rϙpg$~a6&da8 Z`aG D`z z""i,7Ry$w L0q_҄gC=Dw`m*_o_mmx<$&f];ͫ0ISE},@"w(6qlg9 #tۑL"l%NH X[O!Vȃ 6!{PDzh̃2X0Bky `,/8 u`X*8śg"y *? Ag&9 nW03(HVmW KlQn ؋)q<C RAUqiP@2{dݮCdɣ`*+/sTy,04.lt=Ⱥ*1feYW;8X{# DԒw\ fIæUEf f =E 3-7f': d М8(@tÌNZh΋]$EV]X·( Ɠ&c4ZO$qmА<_YHX^sb} a ФZDJ ae($Xvp>~Ժ9͂Ug(17owRK|P8;fġH4/Ԃ!zglBJ[JZ3qD;RG0[ 6סmEz3o"M/D *-qK]ڡo::NI-ܖ4Yv}Gn`q,{uk%%qb?ANaL:x-q[A0Hx;-~{Ū»;73x%; ۊuJ Kw-Ǧ9-5=[}}U Xy|7O ,%>,Px_ɟW1528%/bՃ#s{L4ܝ3  {'&[@j9 U]pk0x]p;dž>tsPW| %pc#a4@[>dяu[N>5KbϪ佰S4$g5<?s[%tEXtdate:create2012-09-26T20:26:41+01:00#Z%tEXtdate:modify2012-09-26T20:26:41+01:00R% IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/cc.png0000664000175000017500000001327012625666430027121 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{tTս?L I& !JIĢ(mEmZE|Ui*bouuيjQAZ"$d?}TOaaQ/V:RQ #]& |d[ ,݆}N}S&v=NI+ .9~?yFO8tHD*B!C˸lit2Qh 6NQE$"1(,qmpH|tzYg1("\=}cyd`D [؆u?y,JDRj(HT: "klig3W_yFBj+/m.okGr.z6ntl\pN9GYkm"1b㘁@@(AhJJaD`!J)>g(w`_#tc:Xm}t 8b$ $'9i'J<`SRa%eR6c,Lq:z@!J\p @Jm|PSO7/fԅg޴eލ{p~&MFIJri[ FK hU0m\.qB!)NHX~ݷW_h1uB, *Oz̪&FVm@1PZuu=z,^onw3檘@kɜL_} p GY2і~F6 8zonShp . o@KK7b̘ATU pڵ{fNdZsxޣIQY[9 7N\Ng#SZuީSNj{yqq>2*,ڵ{@{𬇬etOǃ@!bA+8~<ɾh+ټe^4lA;$"aUsrzjmێ L εk9~=BkiQcۘ.Y@M;?ΫHŋSAeٷ .m0c Ӓ*\ ^p5xxlx) /i88hZ8Xq -YKJ%9JR;LIfSڳŝo ?d_HҫV V@OJ)3M-?x)-뢴GyO֦Y:Zϧ1H>Χ֧ $(>}8tmn]}o]MA>h%eRe4CJYϥ]1u2Vя1@ P;Сvl`^ziC,ʰ^L371+yC3)ݸ_VǶ/H)WE zofkFV\(O<mTT @542ޑ/2dpX7Τlpt8O( mn7663f;v Ԗ*?ET\BttkIδކ vѶ ;7HD+>4qOhZ-=T̸2ڎRt[ĉ╥?Kƭ m`z4*Q S&YcӦ J)*+KO.aȐB(oEw)>MbB!!53bDh1cXŋi~9~p3#FLb-hO";qu=Z-q~*`%P(L$:fdz^;_ʔ)#pĠsTUѺt)F|ޣ=5vqmKA8ln^~ՌUF{{pI::h=۶:yÇQYe a]+ؽ!1Ga/>N+4ƍ Utu&OF0ÐTWfaY\yŊ-D"4!1Z P+z9>w2ͣ %a*M51YPaErpp f7=Ȝ91f 6W463oرhymfv fW?akS0ot{ SEF5MPSS?YNJ =`m ֱXԶ鍖R}ƎG7dKٿ CCC;MM]}> }K?0\'w +4!p8 ?V%.tGr@)aj %=csBQtFƵꋟvG{m\ H !erJxZ16{gJ"Xl6$qk]i$f2'eOg^:̬Yw8li`8%'bxo8y\MۿL"B*JI V8'GSG,Y!3'ҏ'b#w:aH!>(Ohee\&4߶D$sNK/C0agֺ4.s\Leu zfz)kp6&` jlQ)֭G QY`!D"%%>Zar $kFJ΀ -oK.L閥p9,\m;2kF, GD0w㳭Y_\6+hm3ҽ8 '٤k<{={ 7t.D]".Ѩ+㤪͇ *}q2V|7p&Ys[[7?6O=?&T&:(v1g2嶺+D0ILA-`\/=f;ʦK/*LNj 0SX6dh81sDz>;i]W R`sfflr(B_Y~p$ xxg rS駯#ϡ;_i:aR0fL9B <;溨#2F`( 8=tIkcs9t5jj%?(*rsH-(Fs9g3gN0Oȼy+DBdZq܀@TA$B)DnWOo|pPsĺu{Ϛ5[ :U3/sy砠D((pP\&?Agg(,F ڎ*eOG;Ng:p^,J2'm݅kedlc׮|>lrf֭tHxY>80 A)wYn.9q3Y-.XCccyy.V8gB)]w;-m&&!(*t]?0\r+@p4?q0`@.D=BRvi0:2d6@ V o \k{@ p hM۩T0e(aN20"ـЈ}t:7G} EbuLB$?$A/Y!llyҼ)1%tEXtdate:create2012-09-26T20:26:12+01:00ZRH%tEXtdate:modify2012-09-26T20:26:12+01:00+1IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/bm.png0000664000175000017500000001415412625666430027134 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATxy}?tm;f4eFҌf`Hج2+~,/`L\**R8!bM BB $$,!v4f_.䏻Ν;K%Wu޾}~wNw}n_AC0=?) @&&q~S \_uKg,(^GFmЖJ !kgɓm8!"R.;Ji+仏:!eR5bJa~;X7yyv\B8%[[5܇2d8R`-:پ'w 7yy)5`ز _b0~gM0>\b[֧9 >\MX5_.m^YWa!W]UI}Ԭw5ooZ} /Po]GAdƕlr7< Q!Ґw5B bÝY³og_ewUe=DκuqpOG ::LS5PB+mcH ^ GCS)iۉ @I8lqp5+*شa~wv2s'{#oFr֮,*gaC=|Ҋ!e$$@h/?xl xk vuᮮY>.!c׮tvttlEiPD"׍yA,Vu<=u(1DYS3Өov>.W&鈎">jijq561{7O>{LLӠ~6uu!T>؊ե, 2kA[]We%97^?iJn!EF"Kh]d Ž C-*19.,/.*t9 X-_g+RTunpȚ9Vil"*+9?c Ng^:Q'IotV> a`HIJcI={lRpa 'nw|TL[FJyiz4Mc ƫ-Cc_) :)D܅2,%HƋs+oߞ SNZI˓e؄a`P7}c`*3j\/TDئ~O_RSJhN~Kw|rRU0*Rt-_N&\+Օ'RꢣJLNq.Y_**-%Eycԕ=]L@mYc;G6:|PFGG 2O`xM~E.)'.AmmwJWydd4ItIIcT ꫑.8m? {9_ܽ n7TV;aUWMzHV6!ի{"iLN.3U_tiRҿe q-Ga!3|nj*oY# `Bg8v3߲haG)\M};#HiT%dg ϙy5̘7O rSPQ1^Ipe-!'/yj)4ϫٷ[;lO(.ޓqQ`REE+ϝZ#Th#VNFaDV=l>-ɵ,E)4:.rs8o Koݽs UEي#GڲW^"nGyWo5G鈂3bk:ex LL$U$7tGݽы,؜,l~E9' dxѷAg,̻jxS &TaSh{ԛo"\͏ҺJfU:}F6f cdbXH1u.=!!9id/9͐X"X.6c/94_]ÑJCT|J`Ҁ=+J 22Fr{m'l#,!qy1JJ=G܋>Ȉ8Q^"̥(?E4X8{hhok$N;CO_2^vV7χ/cf+y*y>rk0Qlq5  1^PK3`B 0khMi2 .lOQ2?̑ch%֭dX+SK h?Ұmr}vFmoU2'W!%!{j aciUpdhgKmuqaf:[SQDu8;q,Ѐ8Ɩ50Zcd"Oh:XZ}7,!)^,W8s:9KRe]񪥔~ jJ8y i/_I4e4[ ~tq,%( @^XJ+e#<6H4)R qɧ p30>YFq$2֐b)z^J}$v<} NfpPXϼ_rLV(3'0|Cx*(t jf>Ͳst|)_T6!7ߜ© J~+ M&ڡ@Oh$'lQӉй\kVpl6q9!233ж0 I'8@Nf$O-/[ $5BK)'&3NBR!j&rp7C_GBvB ̻\9%wçZX|So:@)[N> 1/9u>xȳ+Xa ޥ,:Ni:v*̐3ݺ:% I-F P>wO~[scJR !hU|#E554μƅy<uV=:mtbf&%7q0Fs#i_ҾHWt[ZڛOFVx_ h栬C&XVݥecI_C-xARw]z|U~F )IO.MzjF$>FA0V`CE~lR{GKbç)ه_kw;w$7)g[]Ō`!ҨO|Ԧ1"G:Pp}Ů#A>m`x 4[X1LGEPٴ}W>dͲ{ _ v5CKB jlMӆBain>dfH3hG?)ϷN2 :x!S"g xcP!fHЈ&a`/yщi![Д [s08gN%TQhGDlT)p*K HK yiA%i$"Uu_,<{f 7P`g\5|.̔93x@Z,/srD9A{4"v'5,;f'!}*r A a1ՆUh끁,s%c]xL`jzaLj}tHV%tEXtdate:create2012-09-26T20:26:31+01:00)%tEXtdate:modify2012-09-26T20:26:31+01:00X,IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ba.png0000664000175000017500000000755512625666430027127 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>IDATx{pygC$bt pL:7ƥ.iq;=ĝvigj{H&3q7&q:5q6<`02/ ${{vWB#3s=~]QnF['z(F1JQyŅf)uSB Z`%bߋH`T DTNwX¾xH ȓ.R:Hm$LD]O ?6i9Ry ).yaّnrPZ[ti=Sxȧ/AT@|Fy]^72Vy2DP׭R2o^-ݬ[ ޥ?å[ylZ~NT΂AD)8s4wF5ɻ-vPzB>=&6̅ GHiW6Nab_i" G#ٽ@.+QsO\f C `` UlܸEX5pu ;mQ@k)tt0e~#/AkFg; lrJ͟!v| u_J;n=;Ȟ=rɤI/R eȨar6WnN <&nGHDYW^iK_MaZ{V?z3R83k"* QmSLp+z} :Nᝪ:މ ~ӳ|q/IKKSVѩY<͇~ X)Ht"2_@s\K/fҵHc(Xj:r!CCYD+IWWx<¢o!:"*5oBW,A)5b Y_/6bUKfMZ[8"UK~%d; {Y3*?tPCY䭧tUs1?ӧxC;;{ ]V1fa>]p]VPߺ\ezoYjoY&Gؼtqmy u` 1gB}#>:)B}aRJ.^PY t| }„<̮]2˛xk_ւQ At'˅eQص(u:P5s2wwXlr -/Sݰ%7P! y%UAya99J~PYslQ?0@ 6o 02#3>xc0~y{6?C;?l<uԁ# !:{Xk@V/eɅy.HT6ȁ >{ xTWǹڷξ|YjW^"le 0PŇqI9:'BpjP>4I2aBf$!S&o/*+Q[9E7hx]ph~z n' I~$64H͐]-G5]pQ}nӵ J?W2<4s\m{3n?rA=~,s cõ\{Swymyv(.\W6ge6o>LT-gzyd>0-}wL S,]X*tNu 5 J]KR HѨ=gI…8qNֿ=P(m+m%,P*VKmHJ.xF 4-BM,w"մSb!0F}²׿>paAX`,o̙>><>؏ 3a "} O"*[aYAD:ƒ7 ش BA7sJVGSH\rXjo)-ϲ` έN0t4 NRlTC0.%5d񾗕H *|NW2h}:OMIy%<9K/`ֳ`͚i?eV.yG $T̆'1mx#u7qT \"ͻV(ܰΔ+Ohi{2^}$0WknX_K^ 1Ґ:f?Rq̹ RD^k납J1d,<)ʙ1N_}Ƨ8N^9xG=xrO,YpsE\ދ{Rk78[:Loo~N*+Åvpwst~,`WZDɅR`.4%cp0š|zAH*Cr^ztrrC@K._\ (ா ZN[Vȋ *ylκuُ03z[l?k1-H<@&KTt2n& y5r-\C tw'^|0Uxzt.?Ƭ\N*'lr+,P@D]*S xf+7M;g &R NŎZ/g'tkc]|,vnijkOJbtvO=IC [\l*{oM:9 _C۪\vӬ2bY>s{9sdwwȡj~Gm& uofW@#Df&MM`TIQJ#rΜ˲twwwK'MӼ]ȴ Q0$m"Pk @M t=J|lf̮m}8ϝI0p0^o*ðu],l_ņu #,fInQ; YDTHJݮRdqF#ac%tEXtdate:create2012-09-26T20:26:27+01:00 %tEXtdate:modify2012-09-26T20:26:27+01:00IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/im.png0000664000175000017500000001042312625666430027136 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>QIDATx[ytmgo}EB7@`P*TqQNS!"q PePr$vaу;.²@,EJhJ{ag{zgfQO|.t7|Z>-O">{? EUspMS'hq..hv B2+":MB >? z{PRcORJ{SA>+< `VA=DwzN]I/%ѦK)e^0V8w-:WK)=lʳmi3ȰmoarE @^4Z%'!RJ-)Ir_by%]aerniԤY@}5! !jui"s9q" QX(}I;6 V1MB|M?rz!\aUpJ_"\JyA&FD .D!ݎ R¶ @):,%:x! G+^pbYH@3]\!Jt 1xH|@R69F nhAL[<(_ *z.<΂(s)zXXͅ*_:96l5~zi2jv20tؐ?v.S:8A@ 5gpL 8BHtX HGLv:4|f'˲4暛ɬXA_k8۴yƾ}?'o2FR0S,м u,]JﹿNT8,`/>`mq ]}%g$`6S$mmX},db"P72y3?OiΩ([`m 8F/n"}}bduCCD BYz٫A&o$ E={e+}gh۶žVy'k!B,mx [o=]f4]$:a۽ߦ罣L\44.#ְ8s?Ί#G' , %MJ| \O8u m*V}7odM J)2 .\ g"6f&EVf4Mk_c F`̧` 1o>GCs3˸ث!{t7K_%+sm mᆯv|-Y-%9ӤH$B,'133C|ԏߤJ7cY4-Φ={xe~zpW">* |Vdl`>cQ;>N18uf9Ua@ $ot3kX"d&p*EkSSh`iylo/mDnV.moA]*n[X]]ݽ4}i:~V@{I;Vazg )?G"g"#6G?D6f ˢIOr@v|:.RGF.BIY:Ϟi^ #8uTʀW-p 3 'S3}c9ۂl>9.pBc&m#mJ";v`IY)"y3;v0>:ʪ+QssUx+zؿ$TrO(ŜPDlr TTgR)a,DeYD5~M=x{ׯd :V2 eaQ8 hFl^ HvVrFa&=Dzz\5ڈ^Ҷ]W_M<N!Vb <O&B/v@srxU4*~`;!HS^$g1ʊg'qu4,fz iڣQ&= SVe/ ZȺ2L[෉-b|Ez~N42d*\7t~_^bg9rirٶ,] .053كy/CWqKd=gzeY mc-d2w(Cl4'O|L{/13ʺM(:~m,Ȃ>3Af]/ cM]ږ5lw S$5199 Tdm-WDv03sýAI==.f9ae,UjF:ϪR eYٰctb(>C/i544Egg'5nReYl(C2@.%` T}(tE7 JJ!H ꫌$޾4Yi?3%0>E,//pU+-C)fAO?.U~S=XUP1HJT݇f,޽ l؈b#6?Q,/u (@J_zJovl!hf>4|k~۵ aHi4JV!ǾЀȂQE;yII~)S)ujm~({MT//?V?3(-߃xE@9e\hS9ߡD +[1[,/]`>Ye fuĿgӉRb{ީT+/r\%pT}aSw*cH/ SM:m3ԭ~Q-X^3d]R:W0*%C!0_y^ed#$ b=,SYu@J/DY@[rzA PQ' D1`qφu(ԝp@u?iy!i`u <Bԩ5ǷiA,?[}Rd설0DZTQwX%tEXtdate:create2012-09-26T20:26:13+01:00%%tEXtdate:modify2012-09-26T20:26:13+01:00x:@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/hr.png0000664000175000017500000001045212625666430027144 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>hIDATx{y38 N !`@JʉrU0U!ࢂ]N9_88P.#IU+8!+7z#N:ػݙ/Xnӿ{cX>+t y Б4~%f"i&HO,Osv*l+A)*B>(/R?O 1{ӇO99$I+ҿޙoZm5^NVTCf>$D2tAD0Pv\3S.L?Bx( cHwjy{.ՖuߊlNJhJ( VYl֬‘O +e+"ї/ฎ+Ls/ْW=n3enooڴR69r]" 3URCI&/ei<#LXJJxA5M$2R8%K%3HhʍfޚQ+\jBbWP (ttObtMGXD֮gSY~Oy=HL^/rMguVp c**#([ 9Wv[VNЌ(@+3ٝ[]향2Ja+Vioz_S&;;jn>k]+W}Y/?ybFs*6GSVy+{NWjkLywu/_ InhRV'4ub8\.|\: Qu5A:W#B؏78϶m^vظx064;|mMPmf,%O.{?|c( -7w{/mv OIHӕRAoRo…4 ĞCNjFĊ%j. mlIb+y]oRPm\]cOYx.czA>5.W OJ7fW 2a²PZj U-ȍZ[9qoa~秮湞 wL9u_Qs`A +!,NϨSY\YkGO3zW+'[þSm7FWP,HΓv~PNZt.n.:C{1"վ @Fnf)E*=eycںְorg'א?J{Z"mXת+btL'}s8y+9qDσV%8Ng0#qWF>E&Y WQl9asq/L7rޮ'LbͣQ_-Ķ1,n.lkYNZ/f7]MzϘ4l ]. ‪Ĉ;@Gۤsv|햰IrcH@2К] Y9Ӹ mdg|zOMScu$D]c*uh Z:Е92CV:}֐Ju# "{g}2%e b339?g vw7A*Aף2("qj0쾍==tS6ӴɮN*aS'}]<ֆxg(.hPmmqJO@Y*߬&I-t hx8⎎3- `r3mI:}Pq;&Vi$`^EmZb2P ?0@w۸ ⺈ֈe%Wv"t*~۫z t.uv] ذTq4_\Kk򟾑y_~Cʤ\I11 H!{Uo<#|[.6܍}3m"m?F'8R:z5:~Psǎ/.EW$ɹy~VyZ 1*$w* Ow.\#G0s=+&l#G|h",vtk޷>KSz”? ˤZ۸8Y6k0o@d/S:g\cP>yϷmV?}G`8HLXv Fe#sskKcoI bi1u]Erˡ[[ն&&qϟc T*48y7>JF۶ R KWղ9Ӻ%ʇB2>Hezu_b>TbV5_JU!9=U`#NcEMI֨e=+npj 33STRz{~ |Bj2Wl9y,3VV"fC NOWrT J$ YiUwX@Fϭ͛ =\# g_9S DŃR ~qx1O]:R٣<렼2ʶ@` ʮJ$7nj9H ݋ ru}GP"׼0r׆\a6'V{[ nwkaNWn?U.[TMjJ2|f2?6NfVRkSqثVLE@0}N7˾qU4ėK:[DǍ|r=9`Gs M8*p}X'7h_uy+hݽor>GgF^Zewd={(wCc@sŅo ᆣŬG5/&ه *΋a`k-"` 53_ʳ6XV`rpGײg HkoI}a6-ҏ~5b< (> o:T,Z5-\w\ǥ qW-VoA9t HV$툋\ t0Q$ 3?xi~| w]ud3YPb6F?ϱ}^,|S}HMYM,h^[D_{62x٭x$˫:I~ x{*JӪوdM+58a%nT%Hb0 P |JŋRkDQ\F}Mj1~%<.+1e8 0$gi95!@'8ʂ)^mh0,Ǩs8)s 6/I^󱰻$~]<IDATx{TUs 7`a>)6MKe5iQBolRAjZ%[Y9^I%LRw E}}=w>y}~'I1M"Iw^,^hJ BԀfĈ!AZV#IR$hp\_޷"@o*@8!!avLLV$I(¶mxwxǙ2e :Q),,dJZFѨE  @;@dTxv:k]tΟ?Ng%>>kֆl&33}q̙>W_}v~󟓖b˖-?SdBeH$In`D REQMg;ҥK)))AR裏2~xV\I]],YBNN?<%%%}ܹs,Z}:gF ~nAi/-EnnF( t% ΞOPmۆa "zɓ-{ȲLrr2cƌv3gxwٷoqqqpBjjjlyP${1p:\r͛7t: '';vw^,Ze:.]Gnoz~!|K$op} Z'~|23JZYVlBMM ϧuVbccY|9MMM<èjn7@Z̓>HSSgϦOٶm,3~xrss Z^{i)˗c-/ jfR!S ͛GC`׮]lڴ MDD,XrvʹiHHH@4 $ @رc1b`2 !##&̙CCCWjƺu; X;YHMMvZZZZ0瓞ǥKTciR5tHnݻfKNNf夤`Z^W_e='5@zd&Olfɒ%=+A]Agx?  F݆ ϟz#GGbb"IMMFFn* $,^)S0lذӶ}){D{O$­R#$j!Vd7HZ)7 99infG IF##FFFo"yN,i CiǥbrZHeU~>c; oElgb9qc@k# v}ǙM_s1Ir> ۖ>v(rSp:6$z? `S+I;Т1u(mmtoSzDnEM3YΗC~vPR&NP3sOzIQphtJvhZ]44xb68@Rd @1) ME`)J{}ÏKV2jb<7 6C8(CPMq4fOL(H]!ClcR] TZϿ}OFj@uFv e"SH]UIq}h'ݖc 4NAOQ@IQplv>v)4ombɑ{fF>'qSx$(Jw( F1}g%I"JbR/3ӬȨIQnTMc_q4ŋc0|{.^%F̽<1. r}lmw!peB&MKGFv744pUشil6ioo'##_||f̘AYYMAղfHH$b ֭[GII cǎ塇444jbPZZJUUdeea4҇1D"L:lV;VSQ:HEQ41am<9m43cf.\ȶm>|8$%%qaOyWؿ?EEE\x(>dYnpYWk"E,oNuu5999?OV\ITTֲsNvŀXfMP$dz|]ȅKԷbq +ѡܞŐc G8֬YCqq1nb ^'-(UTTPXX(dee1wn;VofI#͛Ǐ+555b%2d8|lB!Ν;'ő#GDff`W(Be!e(MeYlڴIL&,QZZ*f͚% nBQWW'&L .N>-r "jjjıcYfFt ܹszQJ.np\bڵB @?^TVVZ}g|6lDDDسgq%"==];$PsN#Rq -}<.ZZZDUU8r8_=Jkk+{@.S@(>ґ#Gfd4IJJoll䭷bDFF8;ӗ"IIIIT*F#qqq,XV˵k׮7 ˲jF:thEEEAjDկ9rtttI|xB\.hll IDATx훿Iǿά{^Ks I?9!9!K.EH ˾;aٝԏ~U]37;>_.uy]^[hs_"Dk?U,.``wY0%""v "ll=T=oW?{ E(d@_n-b`.?~:?Ps;37k]{w2O&dbTG$~B9>AqCJnQ$a,(*/߾uM(H@`:;E~/ft&)LG C0 4Lz,8<{ۢ׾=3)f4QR *@7a>n7\%bkXl@b4]1.3d(*+LdPKZk4>pU֗8*AK P@Z9'"`̉<2+iK 9su0B($>F9(d;?i|C`X0"d*3\T lBxVxIh!`B =Jp[Domx18ƏԢ B |$V]iw;1N/tQhT)VFvJb-lB$,Hߛo dYc~JD>WkPւvϛm(F=םۦ<~En-\m!zц}=$$l7w-@MPDw!cM#| nmb`x`ϴfE&P׬`hy@;斘Ȕ l5֐}ˁ`nk4q%@)GCH1ȡM#z֮ PN&p4Z ]eK F ݻEVmO2Q 5U@i ҙȀZ¹1<.&<߶& 3Y}i"0{ˆM5RwQ&,^ t.j|ndSC[t&LP"IqֈQ_.0h6ၣ0RN FkB=զV%84* ypXE .(h JcK bm @|R=C\Z36 '| ^Yf2$/68z%)3n̷H Egikू x0UK6؃iǢ\IZ .ӕDp~IAa(<v S0sBu.: ;s 6ﮂ|ʎ[ bSf[ O@R]n*Fcrwi^}ItE\;JJ24 kkMBy!@00|F5gŲ1v3D] m5!`[&}jOaөO"P%DF% n1]z e[ n/H3~NMu(Q)X(vtSaA A-PYT"RdHSyt8 *DZa54X 3ҧv[jV"Xq!, .SὛƏrK- *wP uft77/.EpƌgR|\C6@O=4\#G5* TTzyUU¶=Z T. <矠ca``)"|?_;Y5:a{>T߮0 c>4tbpބ+_d)>X"cʭ Jn8hF=E`>[n,)dU\v!kru] Xi9#s[n8@<[* <w5 }X}N p8sZA ?Gl; 5, JXh+4[ R&)_<1?WU>_jiiPU-ұ~](ܧ`}[o%tEXtdate:create2012-09-26T20:26:05+01:00S_X%tEXtdate:modify2012-09-26T20:26:05+01:00"IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/flags/ni.png0000664000175000017500000000741212625666430027143 0ustar fabiofabioPNG  IHDRP<'=bKGD pHYsHHFk>HIDATx[Ys3Hʖ,KۊE"¸B)''xWH P.<J$#رcbٲL_yٝ,jO4|*ʧ1ŽDvImsO:weXmBN?X8sh)jňyGE)y+IOba"5Asko LĴt(wݟ{ gL&(TdعՒ4Zzo60i""Xk 6[Wu˫A& )@6W3RʔTd2֨bUBՆ0etɜ[ Q ^GwYušVy_nΕWTXAEB\A'#=GYܱV%Vn1P/*|?3 }?&(ϕs2I'G]|^T3bNR*}qh=׭sՂh,L7SɗN*v3@ZSVJ9:#a;'n_Dz']J@n؉9yvs(El[N+ Pj}bì CUHڕyNB}*F3JLrm6iEqZ `$ fL{Ӽ7V۹(DӆKŎ:"#.ؾ{27\Z݌ѹ)}j"p"۬yr{#%DHAaɫ%B0%d?C_2AFyGh?J=MzƲ7w{W6E>".[$Z EJP IiF%tdQ!EurWeJu,F2֡aƱ킢vO1M8[]IHX]<3*B J \o>Uch(F\EppdEs VC4T8i ~]Ŋ0B]b˹=B cNͣ5' {HĠUCNGpPb @VG÷h覒Dm>\{\+H!Seβb*76FU*'6~#34h6e43G$`(s8KʹZ J8ԬKi88I@Nb%QoϨ|SHk5ǙmTyxJ|QP&oMD^xL0pp0*) pت,/:b?T=sϑP: NLؤBX_-UPW^䅭%^)Ji*)T'bCme/dJ7SiAj˗:c-blXI.0@ep(VqEn\ U5v<=pћײXmex=W&?AyxzD7ʀ1ӧя}t#,6ׯZ[)7x?8DƱ0al}֚Y%_lr$PT Woo)]E=_-0K^7TS50[gygZ]!7G >ÐMNo+!x]r2 `(f`yWh\/9qeK/ _O m}ygZvoOpH]g~2ʭX 2-jw_g4Bm) /-q7@)σH_Tn+XY؍R UxEX,E"ִ'=}oަh(nB_.Զ#l=l6DlU A)xabPVE b&D VGq:SuF›7TXk| v0jt%j bOiJt7f={MRFC9zzħ Fҙ捍L~|Bi!Jڽ2#j47Ukg^ihp;/8d _fN9Z7}jy 1~vU._g/24!w_|QY/Wž֙۲X2 $l7ƕSP|lyJƁY8ǧf(sHk.6\z +xՕ+g1b m3Lk;Դ!m܆Wg VE)Zކ%s;H">h3jm} G״.O#D۶K hA~l!IOZVNHRhb>ϋCNԀ*2|@c@V*%tEXtdate:create2012-09-26T20:26:28+01:00p%tEXtdate:modify2012-09-26T20:26:28+01:00iYIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/settings-schema.json0000664000175000017500000000020212625666430030712 0ustar fabiofabio{ "use-flags": { "type": "checkbox", "default" : true, "description" : "Use flags to display keyboard layouts" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/metadata.json0000664000175000017500000000021012625666430027373 0ustar fabiofabio{ "uuid": "keyboard@cinnamon.org", "name": "Keyboard", "description": "Keyboard layout", "icon": "keyboard", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js0000664000175000017500000001514412625666430026557 0ustar fabiofabioconst Applet = imports.ui.applet; const Gkbd = imports.gi.Gkbd; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Gtk = imports.gi.Gtk; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const Util = imports.misc.util; const Settings = imports.ui.settings; function LayoutMenuItem() { this._init.apply(this, arguments); } LayoutMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(config, id, indicator, long_name) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this); this._config = config; this._id = id; this.label = new St.Label({ text: long_name }); this.indicator = indicator; this.addActor(this.label); this.addActor(this.indicator); }, activate: function(event) { PopupMenu.PopupBaseMenuItem.prototype.activate.call(this); this._config.lock_group(this._id); } }; function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.metadata = metadata; Main.systrayManager.registerRole("keyboard", metadata.uuid); this.icon_theme = Gtk.IconTheme.get_default(); this.icon_theme.append_search_path(metadata.path + "/flags"); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.actor.add_style_class_name('panel-status-button'); this._labelActors = [ ]; this._layoutItems = [ ]; this.settings = new Settings.AppletSettings(this, metadata["uuid"], this.instance_id); this.settings.bindProperty(Settings.BindingDirection.BIDIRECTIONAL, "use-flags", "_showFlags", this._syncConfig, null); this._config = Gkbd.Configuration.get(); this._config.connect('changed', Lang.bind(this, this._syncConfig)); this._config.connect('group-changed', Lang.bind(this, this._syncGroup)); this._config.start_listen(); this._syncConfig(); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() { Main.overview.hide(); Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]); })); this.menu.addAction(_("Show Character Table"), Lang.bind(this, function() { Main.overview.hide(); Util.spawn(['gucharmap']); })); this.menu.addSettingsAction(_("Keyboard Settings"), 'keyboard'); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _adjustGroupNames: function(names) { // Disambiguate duplicate names with a subscript // This is O(N^2) to avoid sorting names // but N <= 4 so who cares? for (let i = 0; i < names.length; i++) { let name = names[i]; let cnt = 0; for (let j = i + 1; j < names.length; j++) { if (names[j] == name) { cnt++; // U+2081 SUBSCRIPT ONE names[j] = name + String.fromCharCode(0x2081 + cnt); } } if (cnt != 0) names[i] = name + '\u2081'; } return names; }, _syncConfig: function() { let groups = this._config.get_group_names(); if (groups.length > 1) { this.actor.show(); } else { this.menu.close(); this.actor.hide(); } for (let i = 0; i < this._layoutItems.length; i++) this._layoutItems[i].destroy(); for (let i = 0; i < this._labelActors.length; i++) this._labelActors[i].destroy(); let short_names = this._adjustGroupNames(this._config.get_short_group_names()); this._selectedLayout = null; this._layoutItems = [ ]; this._labelActors = [ ]; for (let i = 0; i < groups.length; i++) { let icon_name = this._config.get_group_name(i); let actor; if (this._showFlags && this.icon_theme.lookup_icon(icon_name, 20, 0)) actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.FULLCOLOR, style_class: 'popup-menu-icon' }); else actor = new St.Label({ text: short_names[i] }); let item = new LayoutMenuItem(this._config, i, actor, groups[i]); item._short_group_name = short_names[i]; item._icon_name = icon_name; item._long_name = groups[i]; this._layoutItems.push(item); this.menu.addMenuItem(item, i); let shortLabel = new St.Label({ text: short_names[i] }); this._labelActors.push(shortLabel); } this._syncGroup(); }, _syncGroup: function() { let selected = this._config.get_current_group(); if (this._selectedLayout) { this._selectedLayout.setShowDot(false); this._selectedLayout = null; } let item = this._layoutItems[selected]; item.setShowDot(true); let selectedLabel = this._labelActors[selected]; this.set_applet_tooltip(item._long_name) if (this._showFlags && this.icon_theme.lookup_icon(item._icon_name, 20, 0)) { this.set_applet_icon_name(item._icon_name); this.set_applet_label(""); } else { this.hide_applet_icon(); this.set_applet_label(selectedLabel.text); } this._selectedLayout = item; }, on_applet_removed_from_panel: function() { Main.systrayManager.unregisterRole("keyboard", this.metadata.uuid); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/systray@cinnamon.org/0000775000175000017500000000000012625666430024645 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/systray@cinnamon.org/metadata.json0000664000175000017500000000023412625666430027317 0ustar fabiofabio{ "uuid": "systray@cinnamon.org", "name": "System Tray", "description": "An applet which hosts all the system tray icons", "icon": "indicator-applet" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/systray@cinnamon.org/applet.js0000664000175000017500000003166412625666430026502 0ustar fabiofabioconst Lang = imports.lang; const St = imports.gi.St; const Clutter = imports.gi.Clutter; const Applet = imports.ui.applet; const PopupMenu = imports.ui.popupMenu; const Main = imports.ui.main; const Mainloop = imports.mainloop; const SignalManager = imports.misc.signalManager; const ICON_SCALE_FACTOR = .8; // for custom panel heights, 20 (default icon size) / 25 (default panel height) // Override the factory and create an AppletPopupMenu instead of a PopupMenu function IndicatorMenuFactory() { this._init.apply(this, arguments); } IndicatorMenuFactory.prototype = { __proto__: PopupMenu.PopupMenuFactory.prototype, _init: function() { PopupMenu.PopupMenuFactory.prototype._init.call(this); }, _createShellItem: function(factoryItem, launcher, orientation) { // Decide whether it's a submenu or not let shellItem = null; let item_type = factoryItem.getFactoryType(); if (item_type == PopupMenu.FactoryClassTypes.RootMenuClass) shellItem = new Applet.AppletPopupMenu(launcher, orientation); if (item_type == PopupMenu.FactoryClassTypes.SubMenuMenuItemClass) shellItem = new PopupMenu.PopupSubMenuMenuItem("FIXME"); else if (item_type == PopupMenu.FactoryClassTypes.MenuSectionMenuItemClass) shellItem = new PopupMenu.PopupMenuSection(); else if (item_type == PopupMenu.FactoryClassTypes.SeparatorMenuItemClass) shellItem = new PopupMenu.PopupSeparatorMenuItem(''); else if (item_type == PopupMenu.FactoryClassTypes.MenuItemClass) shellItem = new PopupMenu.PopupIndicatorMenuItem("FIXME"); return shellItem; } }; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id); this.actor.remove_style_class_name("applet-box"); this.actor.style="spacing: 5px;"; this._signalManager = new SignalManager.SignalManager(this); let manager = new Clutter.BoxLayout( { spacing: 2 * global.ui_scale, homogeneous: true, orientation: Clutter.Orientation.HORIZONTAL }); this.manager_container = new Clutter.Actor( { layout_manager: manager } ); this.actor.add_actor (this.manager_container); this.manager_container.show(); this._statusItems = []; this._shellIndicators = {}; this.menuFactory = new IndicatorMenuFactory(); this.menuManager = new PopupMenu.PopupMenuManager(this); this.signalAdded = 0; this.signalRemoved = 0; }, _addIndicatorSupport: function() { let currentIndicators = Main.indicatorManager.getIndicatorIds(); for (let pos in currentIndicators) { let appIndicator = Main.indicatorManager.getIndicatorById(currentIndicators[pos]); this._onIndicatorAdded(Main.indicatorManager, appIndicator); } if (this.signalAdded == 0) this.signalAdded = Main.indicatorManager.connect('indicator-added', Lang.bind(this, this._onIndicatorAdded)); if (this.signalRemoved == 0) this.signalRemoved = Main.indicatorManager.connect('indicator-removed', Lang.bind(this, this._onIndicatorRemoved)); }, _removeIndicatorSupport: function() { if (this.signalAdded) { Main.indicatorManager.disconnect(this.signalAdded); this.signalAdded = 0; } if (this.signalRemoved) { Main.indicatorManager.disconnect(this.signalRemoved); this.signalRemoved = 0; } this._shellIndicators.forEach(function(iconActor) { iconActor.destroy(); }); this._shellIndicators = {}; }, _onIndicatorAdded: function(manager, appIndicator) { if (!(appIndicator.id in this._shellIndicators)) { let hiddenIcons = Main.systrayManager.getRoles(); if (hiddenIcons.indexOf(appIndicator.id) != -1 ) { // We've got an applet for that global.log("Hiding indicator (role already handled): " + appIndicator.id); return; } else if (["quassel"].indexOf(appIndicator.id) != -1) { // Blacklist some of the icons // quassel: The proper icon in Quassel is "QuasselIRC", this is a fallback icon which Quassel launches when it fails to detect // our indicator support (i.e. when Cinnamon is restarted for instance) // The problem is.. Quassel doesn't kill that icon when it creates QuasselIRC again.. global.log("Hiding indicator (blacklisted): " + appIndicator.id); return; } else { global.log("Adding indicator: " + appIndicator.id); } let iconActor = appIndicator.getIconActor(this._getIndicatorSize(appIndicator)); iconActor._applet = this; this._shellIndicators[appIndicator.id] = iconActor; this.actor.add_actor(iconActor.actor); appIndicator.createMenuClientAsync(Lang.bind(this, function(client) { if (client != null) { let newMenu = client.getShellMenu(); if (!newMenu) { newMenu = this.menuFactory.buildShellMenu(client, iconActor, this._applet_context_menu._arrowSide); this.menuManager.addMenu(newMenu); } iconActor.setMenu(newMenu); } })); } }, _getIndicatorSize: function(appIndicator) { if (this._scaleMode) return this._panelHeight * ICON_SCALE_FACTOR; return 16; }, _onIndicatorRemoved: function(manager, appIndicator) { if (appIndicator.id in this._shellIndicators) { global.log("Removing indicator: " + appIndicator.id); let iconActor = this._shellIndicators[appIndicator.id]; delete this._shellIndicators[appIndicator.id]; iconActor.destroy(); } }, on_applet_clicked: function(event) { }, on_applet_removed_from_panel: function () { this._signalManager.disconnectAllSignals(); this._removeIndicatorSupport(); }, on_applet_added_to_panel: function() { Main.statusIconDispatcher.start(this.actor.get_parent().get_parent()); this._signalManager.connect(Main.statusIconDispatcher, 'status-icon-added', this._onTrayIconAdded); this._signalManager.connect(Main.statusIconDispatcher, 'status-icon-removed', this._onTrayIconRemoved); this._signalManager.connect(Main.statusIconDispatcher, 'before-redisplay', this._onBeforeRedisplay); this._signalManager.connect(Main.systrayManager, "changed", Main.statusIconDispatcher.redisplay, Main.statusIconDispatcher); this._addIndicatorSupport(); }, on_panel_height_changed: function() { Main.statusIconDispatcher.redisplay(); for (let id in this._shellIndicators) { let indicator = Main.indicatorManager.getIndicatorById(id); if (indicator) { let size = this._getIndicatorSize(indicator); this._shellIndicators[id].setSize(size); } } }, _onBeforeRedisplay: function() { // Mark all icons as obsolete // There might still be pending delayed operations to insert/resize of them // And that would crash Cinnamon for (var i = 0; i < this._statusItems.length; i++) { this._statusItems[i].obsolete = true; } this._statusItems = []; let children = this.manager_container.get_children(); for (var i = 0; i < children.length; i++) { children[i].destroy(); } }, _onTrayIconAdded: function(o, icon, role) { try { let hiddenIcons = Main.systrayManager.getRoles(); if (hiddenIcons.indexOf(role) != -1 ) { // We've got an applet for that global.log("Hiding systray: " + role); return; } global.log("Adding systray: " + role + " (" + icon.get_width() + "x" + icon.get_height() + "px)"); if (icon.get_parent()) icon.get_parent().remove_child(icon); icon.obsolete = false; this._statusItems.push(icon); if (["pidgin"].indexOf(role) != -1) { // Delay pidgin insertion by 10 seconds // Pidgin is very weird.. it starts with a small icon // Then replaces that icon with a bigger one when the connection is established // Pidgin can be fixed by inserting or resizing after a delay // The delay is big because resizing/inserting too early // makes pidgin invisible (in absence of disk cache).. even if we resize/insert again later this._insertStatusItemLater(role, icon, -1, 10000); } else if (["shutter", "filezilla", "dropbox", "thunderbird", "unknown", "blueberry-tray.py", "mintupdate.py"].indexOf(role) != -1) { // Delay insertion by 1 second // This fixes an invisible icon in the absence of disk cache for : shutter // filezilla, dropbox, thunderbird, blueberry, mintupdate are known to show up in the wrong size or position, this chould fix them as well // Note: as of Oct 2015, the dropbox systray is calling itself "unknown" this._insertStatusItemLater(role, icon, -1, 1000); } else { // Delay all other apps by 1 second... // For many of them, we don't need to do that, // It's a small delay though and that fixes most buggy apps // And we're far from having an exhaustive list of them.. this._insertStatusItemLater(role, icon, -1, 1000); } } catch (e) { global.logError(e); } }, _insertStatusItemLater: function(role, icon, position, delay) { // Inserts an icon in the systray after a delay (useful for buggy icons) // Delaying the insertion of pidgin by 10 seconds for instance is known to fix it on empty disk cache let timerId = Mainloop.timeout_add(delay, Lang.bind(this, function() { this._insertStatusItem(role, icon, position); Mainloop.source_remove(timerId); })); }, _onTrayIconRemoved: function(o, icon) { icon.obsolete = true; for (var i = 0; i < this._statusItems.length; i++) { if (this._statusItems[i] == icon) { this._statusItems.splice(i, 1); } } this.manager_container.remove_child(icon); icon.destroy(); }, _insertStatusItem: function(role, icon, position) { if (icon.obsolete == true) { return; } let children = this.manager_container.get_children(); let i; for (i = children.length - 1; i >= 0; i--) { let rolePosition = children[i]._rolePosition; if (position > rolePosition) { this.manager_container.insert_child_at_index(icon, i + 1); break; } } if (i == -1) { // If we didn't find a position, we must be first this.manager_container.insert_child_at_index(icon, 0); } icon._rolePosition = position; if (this._scaleMode) { let timerId = Mainloop.timeout_add(500, Lang.bind(this, function() { this._resizeStatusItem(role, icon); Mainloop.source_remove(timerId); })); } }, _resizeStatusItem: function(role, icon) { if (icon.obsolete == true) { return; } let size; let disp_size = this._panelHeight * ICON_SCALE_FACTOR; if (["shutter", "filezilla"].indexOf(role) != -1) { global.log("Not resizing " + role + " as it's known to be buggy (" + icon.get_width() + "x" + icon.get_height() + "px)"); } else { if (disp_size < 22) { size = 16; } else if (disp_size < 32) { size = 22; } else if (disp_size < 48) { size = 32; } else { size = 48; } icon.set_size(size, size); global.log("Resized " + role + " with normalized size (" + icon.get_width() + "x" + icon.get_height() + "px)"); //Note: dropbox doesn't scale, even though we resize it... } }, }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/0000775000175000017500000000000012625666430026704 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/icons/0000775000175000017500000000000012625666430030017 5ustar fabiofabio././@LongLink0000644000000000000000000000016600000000000011606 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/icons/windows-quick-list-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/icons/windows-quick-0000664000175000017500000000036512625666430032627 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/icon.png0000664000175000017500000000033712625666430030345 0ustar fabiofabioPNG  IHDR szzsRGBbKGD pHYs B(xtIME 0q_IDATX; Cxn%oui 쨙5t^~;@6+gX<lA;;2pc B:IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/metadata.json0000664000175000017500000000023712625666430031361 0ustar fabiofabio{ "uuid": "windows-quick-list@cinnamon.org", "name": "Windows Quick List", "description": "Quickly access windows from all workspaces", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/windows-quick-list@cinnamon.org/applet.js0000664000175000017500000001273412625666430030536 0ustar fabiofabioconst Applet = imports.ui.applet; const Cinnamon = imports.gi.Cinnamon; const GLib = imports.gi.GLib; const Lang = imports.lang; const Mainloop = imports.mainloop; const PopupMenu = imports.ui.popupMenu; const St = imports.gi.St; const Main = imports.ui.main; function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); }; MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("windows-quick-list"); this.set_applet_tooltip(_("All windows")); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menuManager.addMenu(this.menu); } catch (e) { global.logError(e); } }, updateMenu: function() { this.menu.removeAll(); let empty_menu = true; try { let tracker = Cinnamon.WindowTracker.get_default(); for ( let wks=0; wks0) { this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); } if(global.screen.n_workspaces>1) { let item = new PopupMenu.PopupMenuItem(workspace_name); item.actor.reactive = false; item.actor.can_focus = false; item.label.add_style_class_name('popup-subtitle-menu-item'); if(wks == global.screen.get_active_workspace().index()) { item.setShowDot(true); } this.menu.addMenuItem(item); empty_menu = false; } for ( let i = 0; i < windows.length; ++i ) { let metaWindow = windows[i]; let item = new PopupMenu.PopupMenuItem(windows[i].get_title()); item.connect('activate', Lang.bind(this, function() { this.activateWindow(metaWorkspace, metaWindow); } )); item._window = windows[i]; let app = tracker.get_window_app(item._window); item._icon = app.create_icon_texture(24); item.addActor(item._icon, { align: St.Align.END }); this.menu.addMenuItem(item); empty_menu = false; } } } } catch(e) { global.logError(e); } if (empty_menu) { let item = new PopupMenu.PopupMenuItem(_("No open windows")) item.actor.reactive = false; item.actor.can_focus = false; item.label.add_style_class_name('popup-subtitle-menu-item'); this.menu.addMenuItem(item); } }, activateWindow: function(metaWorkspace, metaWindow) { this.menu.toggle(); if(!metaWindow.is_on_all_workspaces()) { metaWorkspace.activate(global.get_current_time()); } metaWindow.unminimize(global.get_current_time()); metaWindow.activate(global.get_current_time()); }, on_applet_clicked: function(event) { this.updateMenu(); this.menu.toggle(); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/workspace-switcher@cinnamon.org/0000775000175000017500000000000012625666430026753 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/workspace-switcher@cinnamon.org/settings-schema.json0000664000175000017500000000042012625666430032740 0ustar fabiofabio{ "display_type": { "type": "radiogroup", "description": "Type of display", "default": "visual", "options" : { "A visual representation of the workspaces": "visual", "Simple buttons": "buttons" } } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/workspace-switcher@cinnamon.org/metadata.json0000664000175000017500000000030612625666430031425 0ustar fabiofabio{ "uuid": "workspace-switcher@cinnamon.org", "name": "Workspace switcher", "description": "Select the workspace from the panel", "icon": "gnome-panel-workspace-switcher", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/workspace-switcher@cinnamon.org/applet.js0000664000175000017500000003260712625666430030606 0ustar fabiofabioconst St = imports.gi.St; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Applet = imports.ui.applet; const Main = imports.ui.main; const Meta = imports.gi.Meta; const PopupMenu = imports.ui.popupMenu; const SignalManager = imports.misc.signalManager; const Tooltips = imports.ui.tooltips; const Settings = imports.ui.settings; const ModalDialog = imports.ui.modalDialog; function WorkspaceButton(index, applet) { this._init(index, applet); } WorkspaceButton.prototype = { _init : function(index, applet) { this.index = index; this.applet = applet; this.workspace = global.screen.get_workspace_by_index(this.index); this.workspace_name = Main.getWorkspaceName(index); this.actor = null; // defined in subclass this.ws_signals = new SignalManager.SignalManager(this); this.ws_signals.connect(this.workspace, "window-added", this.update); this.ws_signals.connect(this.workspace, "window-removed", this.update); }, show : function() { this.actor.connect('button-release-event', Lang.bind(this, this.onClicked)); this._tooltip = new Tooltips.PanelItemTooltip(this, this.workspace_name, this.applet.orientation); }, onClicked: function(actor, event) { if (event.get_button() == 1) { this.workspace.activate(global.get_current_time()); } }, update: function() { // defined in subclass }, destroy: function() { this.ws_signals.disconnectAllSignals(); this._tooltip.destroy(); this.actor.destroy(); } }; function WorkspaceGraph(index, applet) { this._init(index, applet); } WorkspaceGraph.prototype = { __proto__: WorkspaceButton.prototype, _init: function(index, applet) { WorkspaceButton.prototype._init.call(this, index, applet); this.actor = new St.Bin({ reactive: true, style_class: 'workspace', y_fill: true, important: true }); this.graphArea = new St.DrawingArea({ style_class: 'windows', important: true }); this.actor.child = this.graphArea; this.panelApplet = applet; this.workspace_size = new Meta.Rectangle(); this.workspace.get_work_area_all_monitors(this.workspace_size); this.sizeRatio = this.workspace_size.width / this.workspace_size.height; let height = applet._panelHeight - 6; this.actor.set_size(this.sizeRatio * height, height); this.graphArea.set_size(this.sizeRatio * height, height); this.graphArea.connect('repaint', Lang.bind(this, this.onRepaint)); if (index == global.screen.get_active_workspace_index()) { this.actor.add_style_pseudo_class('active'); } }, scale: function (windows_rect, workspace_rect, area_width, area_height) { let scaled_rect = new Meta.Rectangle(); let x_ratio = area_width / workspace_rect.width; let y_ratio = area_height / workspace_rect.height; scaled_rect.x = windows_rect.x * x_ratio; scaled_rect.y =windows_rect.y * y_ratio; scaled_rect.width = windows_rect.width * x_ratio; scaled_rect.height = windows_rect.height * y_ratio; return scaled_rect; }, sortWindowsByUserTime: function (win1, win2) { let t1 = win1.get_user_time(); let t2 = win2.get_user_time(); return (t2 < t1) ? 1 : -1; }, onRepaint: function(area) { try { let graphThemeNode = this.graphArea.get_theme_node(); let workspaceThemeNode = this.panelApplet.actor.get_theme_node(); let height = this.panelApplet._panelHeight - workspaceThemeNode.get_vertical_padding(); let borderWidth = workspaceThemeNode.get_border_width(St.Side.TOP) + workspaceThemeNode.get_border_width(St.Side.BOTTOM); this.graphArea.set_size(this.sizeRatio * height, height - borderWidth); let cr = area.get_context(); let [area_width, area_height] = area.get_surface_size(); let workspace_is_active = false; if (this.index == global.screen.get_active_workspace_index()) { workspace_is_active = true; } // construct a list with all windows let windows = this.workspace.list_windows(); windows = windows.filter( Main.isInteresting ); windows = windows.filter( function(w) { return !w.is_skip_taskbar() && !w.minimized; }); windows.sort(this.sortWindowsByUserTime); if (windows.length) { let windowBackgroundColor; let windowBorderColor; for (let i = 0; i < windows.length; ++i) { let metaWindow = windows[i]; let scaled_rect = this.scale(metaWindow.get_outer_rect(), this.workspace_size, area_width, area_height); cr.setLineWidth(1); if (metaWindow.has_focus()) { windowBorderColor = graphThemeNode.get_color('-active-window-border'); Clutter.cairo_set_source_color(cr, windowBorderColor); } else { windowBorderColor = graphThemeNode.get_color('-inactive-window-border'); Clutter.cairo_set_source_color(cr, windowBorderColor); } cr.rectangle(scaled_rect.x, scaled_rect.y, scaled_rect.width, scaled_rect.height); cr.strokePreserve(); if (metaWindow.has_focus()) { windowBackgroundColor = graphThemeNode.get_color('-active-window-background'); Clutter.cairo_set_source_color(cr, windowBackgroundColor); } else { windowBackgroundColor = graphThemeNode.get_color('-inactive-window-background'); Clutter.cairo_set_source_color(cr, windowBackgroundColor); } cr.fill(); } } cr.$dispose(); } catch(e) { global.logError(e); } }, update: function() { this.graphArea.queue_repaint(); } }; function SimpleButton(index, applet) { this._init(index, applet); } SimpleButton.prototype = { __proto__: WorkspaceButton.prototype, _init : function(index, applet) { WorkspaceButton.prototype._init.call(this, index, applet); this.actor = new St.Button({ name: 'workspaceButton', style_class: 'workspace-button', reactive: true }); if ( index == global.screen.get_active_workspace_index() ) { this.actor.add_style_pseudo_class('outlined'); } let label = new St.Label({ text: (index + 1).toString() }); this.actor.set_child(label); if (applet._scaleMode) { this.actor.set_height(applet._panelHeight); } }, update: function() { } }; function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.orientation = orientation; this.panel_height = panel_height; this.signals = new SignalManager.SignalManager(this); this.buttons = []; this.settings = new Settings.AppletSettings(this, metadata.uuid, instance_id); this.settings.bindProperty(Settings.BindingDirection.IN, "display_type", "display_type", Lang.bind(this, this._createButtons), null); this.actor.connect('scroll-event', this.hook.bind(this)); this._createButtons(); global.screen.connect('notify::n-workspaces', Lang.bind(this, this.onNumberOfWorkspacesChanged)); global.window_manager.connect('switch-workspace', Lang.bind(this, this._createButtons)); this.on_panel_edit_mode_changed(); global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); let expoMenuItem = new PopupMenu.PopupIconMenuItem(_("Manage workspaces (Expo)"), "view-grid-symbolic", St.IconType.SYMBOLIC); expoMenuItem.connect('activate', Lang.bind(this, function() { if (!Main.expo.animationInProgress) Main.expo.toggle(); })); this._applet_context_menu.addMenuItem(expoMenuItem); let addWorkspaceMenuItem = new PopupMenu.PopupIconMenuItem (_("Add a new workspace"), "list-add", St.IconType.SYMBOLIC); addWorkspaceMenuItem.connect('activate', Lang.bind(this, function() { Main._addWorkspace(); })); this._applet_context_menu.addMenuItem(addWorkspaceMenuItem); this.removeWorkspaceMenuItem = new PopupMenu.PopupIconMenuItem (_("Remove the current workspace"), "list-remove", St.IconType.SYMBOLIC); this.removeWorkspaceMenuItem.connect('activate', Lang.bind(this, function() { this.removeWorkspace(); })); this._applet_context_menu.addMenuItem(this.removeWorkspaceMenuItem); this.removeWorkspaceMenuItem.setSensitive(global.screen.n_workspaces > 1); } catch (e) { global.logError(e); } }, onNumberOfWorkspacesChanged: function() { this.removeWorkspaceMenuItem.setSensitive(global.screen.n_workspaces > 1); this._createButtons(); }, removeWorkspace : function (){ if (global.screen.n_workspaces <= 1) { return; } this.workspace_index = global.screen.get_active_workspace_index(); let removeAction = Lang.bind(this, function() { Main._removeWorkspace(global.screen.get_active_workspace()); }); if (!Main.hasDefaultWorkspaceName(this.workspace_index)) { let prompt = _("Are you sure you want to remove workspace \"%s\"?\n\n").format( Main.getWorkspaceName(this.workspace_index)); let confirm = new ModalDialog.ConfirmDialog(prompt, removeAction); confirm.open(); } else { removeAction(); } }, on_panel_edit_mode_changed: function() { let reactive = !global.settings.get_boolean('panel-edit-mode'); for (let i = 0; i < this.buttons.length; ++i) { this.buttons[i].reactive = reactive; } }, on_panel_height_changed: function() { this._createButtons(); }, hook: function(actor, event){ var direction = event.get_scroll_direction(); if (direction == 0) this.switch_workspace(-1); if (direction == 1) this.switch_workspace(1); }, switch_workspace: function(incremental){ var index = global.screen.get_active_workspace_index(); index += incremental; if(global.screen.get_workspace_by_index(index) != null) { global.screen.get_workspace_by_index(index).activate(global.get_current_time()); } }, _createButtons: function() { for (let i = 0; i < this.buttons.length; ++i) { this.buttons[i].destroy(); } this.buttons = []; for (let i = 0; i < global.screen.n_workspaces; ++i) { if (this.display_type == "visual") { this.actor.set_style_class_name('workspace-graph'); this.actor.set_important(true); this.buttons[i] = new WorkspaceGraph(i, this); } else { this.actor.set_style_class_name('workspace-switcher'); this.actor.set_important(true); this.buttons[i] = new SimpleButton(i, this); } this.actor.add_actor(this.buttons[i].actor); this.buttons[i].show(); } this.signals.disconnectAllSignals(); if (this.display_type == "visual") { // In visual mode, keep track of window events to represent them this.signals.connect(global.display, "notify::focus-window", this._onFocusChanged); this._onFocusChanged(); } }, _onFocusChanged: function() { if (global.display.focus_window && this._focusWindow == global.display.focus_window.get_compositor_private()) return; this.signals.disconnect("position-changed"); this.signals.disconnect("size-changed"); if (!global.display.focus_window) return; this._focusWindow = global.display.focus_window.get_compositor_private(); this.signals.connect(this._focusWindow, "position-changed", Lang.bind(this, this._onPositionChanged)); this.signals.connect(this._focusWindow, "size-changed", Lang.bind(this, this._onPositionChanged)); this._onPositionChanged(); }, _onPositionChanged: function() { let button = this.buttons[global.screen.get_active_workspace_index()]; button.update(); }, on_applet_removed_from_panel: function() { this.signals.disconnectAllSignals(); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/0000775000175000017500000000000012625666430026000 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icon.svg0000664000175000017500000006637312625666430027470 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/settings-schema.json0000664000175000017500000000070012625666430031766 0ustar fabiofabio{ "ignoreTransientNotifications": { "type": "checkbox", "default": true, "description": "Ignore transient notifications", "tooltip" : "Check this to ignore transient notifications." }, "showEmptyTray" : { "type" : "checkbox", "default" : false, "description" : "Show empty tray", "tooltip" : "Check this to show the tray even when there are no new notifications." } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/0000775000175000017500000000000012625666430027113 5ustar fabiofabio././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/empty-notif-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/empty-notif-symboli0000664000175000017500000000031312625666430032762 0ustar fabiofabio ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/normal-notif-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/normal-notif-symbol0000664000175000017500000000055412625666430032752 0ustar fabiofabio ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/low-notif-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/low-notif-symbolic.0000664000175000017500000000056312625666430032655 0ustar fabiofabio ././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/alt-critical-notif-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/alt-critical-notif-0000664000175000017500000000060312625666430032577 0ustar fabiofabio ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/critical-notif-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/icons/critical-notif-symb0000664000175000017500000000062312625666430032716 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/metadata.json0000664000175000017500000000027212625666430030454 0ustar fabiofabio{ "uuid": "notifications@cinnamon.org", "name": "Notifications", "description": "Click to display and manage system notifications", "role": "notifications", "icon": "cs-notifications" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js0000664000175000017500000003012112625666430027620 0ustar fabiofabioconst Applet = imports.ui.applet; const Lang = imports.lang; const Main = imports.ui.main; const Gtk = imports.gi.Gtk; const Gio = imports.gi.Gio; const PopupMenu = imports.ui.popupMenu; const St = imports.gi.St; const Clutter = imports.gi.Clutter; const Mainloop = imports.mainloop; const MessageTray = imports.ui.messageTray; const Urgency = imports.ui.messageTray.Urgency; const NotificationDestroyedReason = imports.ui.messageTray.NotificationDestroyedReason; const Settings = imports.ui.settings; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instanceId); // Settings this.settings = new Settings.AppletSettings(this, metadata.uuid, instanceId); this.settings.bindProperty(Settings.BindingDirection.IN, "ignoreTransientNotifications", "ignoreTransientNotifications", null, null); this.settings.bindProperty(Settings.BindingDirection.IN, "showEmptyTray", "showEmptyTray", this._show_hide_tray, null); // Layout this._orientation = orientation; this.menuManager = new PopupMenu.PopupMenuManager(this); // Lists this.notifications = []; // The list of notifications, in order from oldest to newest. // Events Main.messageTray.connect('notify-applet-update', Lang.bind(this, this._notification_added)); global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); // States this._blinking = false; this._blink_toggle = false; }, _display: function() { // Always start the applet empty, void of any notifications. this.set_applet_icon_symbolic_name("empty-notif"); this.set_applet_tooltip(_("Notifications")); // Setup the notification container. this._maincontainer = new St.BoxLayout({name: 'traycontainer', vertical: true}); this._notificationbin = new St.BoxLayout({vertical:true}); this.button_label_box = new St.BoxLayout(); // Setup the tray icon. this.menu_label = new PopupMenu.PopupMenuItem(stringify(this.notifications.length)); this.menu_label.actor.reactive = false; this.menu_label.actor.can_focus = false; this.menu_label.label.add_style_class_name('popup-subtitle-menu-item'); this.clear_separator = new PopupMenu.PopupSeparatorMenuItem(); this.clear_action = new PopupMenu.PopupMenuItem(_("Clear notifications")); this.clear_action.connect('activate', Lang.bind(this, this._clear_all)); this.clear_action.actor.hide(); if (this._orientation == St.Side.BOTTOM) { this.menu.addMenuItem(this.menu_label); this.menu.addActor(this._maincontainer); this.menu.addMenuItem(this.clear_separator); this.menu.addMenuItem(this.clear_action); } else { this.menu.addMenuItem(this.clear_action); this.menu.addMenuItem(this.clear_separator); this.menu.addMenuItem(this.menu_label); this.menu.addActor(this._maincontainer); } this.scrollview = new St.ScrollView({ x_fill: true, y_fill: true, y_align: St.Align.START, style_class: "vfade"}); this._maincontainer.add(this.scrollview); this.scrollview.add_actor(this._notificationbin); this.scrollview.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); let vscroll = this.scrollview.get_vscroll_bar(); vscroll.connect('scroll-start', Lang.bind(this, function() { this.menu.passEvents = true; })); vscroll.connect('scroll-stop', Lang.bind(this, function() { this.menu.passEvents = false; })); // Alternative tray icons. this._crit_icon = new St.Icon({icon_name: 'critical-notif', icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' }); this._alt_crit_icon = new St.Icon({icon_name: 'alt-critical-notif', icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' }); this.update_list(); }, _notification_added: function (mtray, notification) { // Notification event handler. // Ignore transient notifications? if (this.ignoreTransientNotifications && notification.isTransient) { return; } if (notification.enter_id > 0) { notification.actor.disconnect(notification.enter_id); notification.enter_id = 0; } if (notification.leave_id > 0) { notification.actor.disconnect(notification.leave_id); notification.leave_id = 0; } notification.actor.opacity = (notification._table.get_theme_node().get_length('opacity') / global.ui_scale) || 255; notification.actor.unparent(); let existing_index = this.notifications.indexOf(notification); if (existing_index != -1) { // This notification is already listed. if (notification._destroyed) { this.notifications.splice(existing_index, 1); } else { notification._inNotificationBin = true; global.reparentActor(notification.actor, this._notificationbin); notification.expand(); notification._timeLabel.show(); } this.update_list(); return; } else if (notification._destroyed) { return; } // Add notification to list. notification._inNotificationBin = true; this.notifications.push(notification); // Steal the notication panel. notification.expand(); this._notificationbin.add(notification.actor) notification.actor._parent_container = this._notificationbin; notification.actor.add_style_class_name('notification-applet-padding'); // Register for desctruction. notification.connect('clicked', Lang.bind(this, this._item_clicked, false)); notification.connect('destroy', Lang.bind(this, this._item_clicked, true)); notification._timeLabel.show(); this.update_list(); }, _item_clicked: function(notification, destroyed) { let i = this.notifications.indexOf(notification); if (i != -1) { this.notifications.splice(i, 1); if (!destroyed) { notification.destroy(NotificationDestroyedReason.DISMISSED); } } this.update_list(); }, update_list: function () { try { let count = this.notifications.length; if (count > 0) { // There are notifications. this.actor.show(); this.clear_action.actor.show(); this.set_applet_label(count.toString()); // Find max urgency and derive list icon. let max_urgency = -1; for (let i = 0; i < count; i++) { let cur_urgency = this.notifications[i].urgency; if (cur_urgency > max_urgency) max_urgency = cur_urgency; } switch (max_urgency) { case Urgency.LOW: this._blinking = false; this.set_applet_icon_symbolic_name("low-notif"); break; case Urgency.NORMAL: case Urgency.HIGH: this._blinking = false; this.set_applet_icon_symbolic_name("normal-notif"); break; case Urgency.CRITICAL: if (!this._blinking) { this._blinking = true; this.critical_blink(); } break; } } else { // There are no notifications. this._blinking = false; this.set_applet_label(''); this.set_applet_icon_symbolic_name("empty-notif"); this.clear_action.actor.hide(); if (!this.showEmptyTray) { this.actor.hide(); } } this.menu_label.label.set_text(stringify(count)); this._notificationbin.queue_relayout(); } catch (e) { global.logError(e); } }, _clear_all: function() { let count = this.notifications.length; if (count > 0) { for (let i = count-1; i >=0; i--) { this._notificationbin.remove_actor(this.notifications[i].actor); this.notifications[i].destroy(NotificationDestroyedReason.DISMISSED); } } this.notifications = []; this.update_list(); }, _show_hide_tray: function() { // Show or hide the notification tray. if (this.notifications.length || this.showEmptyTray) { this.actor.show(); } else { this.actor.hide(); } }, on_panel_edit_mode_changed: function () { }, on_applet_added_to_panel: function() { this.on_orientation_changed(this._orientation); }, on_orientation_changed: function (orientation) { this._orientation = orientation; if (this.menu) { this.menu.destroy(); } this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._display(); }, on_applet_clicked: function(event) { this._update_timestamp(); this.menu.toggle(); }, _update_timestamp: function () { let dateFormat = _("%l:%M %p"); let actors = this._notificationbin.get_children(); if (actors) { for (let i = 0; i < actors.length; i++) { let notification = actors[i]._delegate; let orig_time = notification._timestamp; notification._timeLabel.clutter_text.set_markup(timeify(orig_time)); } } }, critical_blink: function () { if (!this._blinking) return; if (this._blink_toggle) { this._applet_icon_box.child = this._crit_icon; } else { this._applet_icon_box.child = this._alt_crit_icon; } this._blink_toggle = !this._blink_toggle; Mainloop.timeout_add_seconds(1, Lang.bind(this, this.critical_blink)); } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } function stringify(count) { let str; switch (true) { case (count == 0): str = _("No notifications"); break; case (count == 1): str = count.toString() + _(" notification"); break; case (count > 1): str = count.toString() + _(" notifications"); break; default: str = ""; } return str; } function timeify(orig_time) { let settings = new Gio.Settings({schema_id: 'org.cinnamon.desktop.interface'}); let use_24h = settings.get_boolean('clock-use-24h'); let now = new Date(); let diff = Math.floor((now.getTime() - orig_time.getTime()) / 1000); // get diff in seconds let str; if (use_24h) { str = orig_time.toLocaleFormat('%T'); } else { str = orig_time.toLocaleFormat('%r'); } switch (true) { case (diff <= 15): str += _(" (Just now)"); break; case (diff > 15 && diff <= 59): str += _(" (%s seconds ago)").format(diff.toString()); break; case (diff > 59 && diff <= 119): str += _(" (%s minute ago)").format(Math.floor(diff / 60).toString()); break; case (diff > 119 && diff <= 3540): str += _(" (%s minutes ago)").format(Math.floor(diff / 60).toString()); break; } return str; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/0000775000175000017500000000000012625666430025130 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/0000775000175000017500000000000012625666430026243 5ustar fabiofabio././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-disabled-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-disabled-symb0000664000175000017500000000607112625666430032710 0ustar fabiofabio Gnome Symbolic Icon Theme image/svg+xml Gnome Symbolic Icon Theme ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-pause-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-pause-symboli0000664000175000017500000000616312625666430032764 0ustar fabiofabio Gnome Symbolic Icon Theme image/svg+xml Gnome Symbolic Icon Theme ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-play-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/icons/slideshow-play-symbolic0000664000175000017500000000601012625666430032746 0ustar fabiofabio Gnome Symbolic Icon Theme image/svg+xml Gnome Symbolic Icon Theme Cinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/metadata.json0000664000175000017500000000024412625666430027603 0ustar fabiofabio{ "uuid":"slideshow@cinnamon.org", "name":"Slideshow Controls", "description":"Basic controls for the Cinnamon slideshow", "icon":"preferences-desktop-wallpaper" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/slideshow@cinnamon.org/applet.js0000664000175000017500000001340612625666430026757 0ustar fabiofabioconst Lang = imports.lang; const Gio = imports.gi.Gio; const St = imports.gi.St; const Main = imports.ui.main; const Applet = imports.ui.applet; const PopupMenu = imports.ui.popupMenu; const Util = imports.misc.util; const Gettext = imports.gettext.domain("cinnamon-applets"); const _ = Gettext.gettext; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instanceId); this._slideshowSettings = new Gio.Settings({ schema_id: 'org.cinnamon.desktop.background.slideshow' }); try { if (this._slideshowSettings.get_boolean("slideshow-enabled")) { if (!this._slideshowSettings.get_boolean("slideshow-paused")) { this.set_applet_icon_symbolic_name('slideshow-play-symbolic.svg'); this.set_applet_tooltip(_("Click to pause the slideshow")); } else { this.set_applet_icon_symbolic_name('slideshow-pause-symbolic.svg'); this.set_applet_tooltip(_("Click to resume the slideshow")); } } else { this.set_applet_icon_symbolic_name('slideshow-disabled-symbolic.svg'); this.set_applet_tooltip(_("The slideshow is disabled")); } this._slideshowSettings.connect("changed::slideshow-enabled", Lang.bind(this, this._on_slideshow_enabled_changed)); this._slideshowSettings.connect("changed::slideshow-paused", Lang.bind(this, this._on_slideshow_paused_changed)); this.enable_slideshow_switch = new PopupMenu.PopupSwitchMenuItem(_("Slideshow"), this._slideshowSettings.get_boolean("slideshow-enabled")); this._applet_context_menu.addMenuItem(this.enable_slideshow_switch); this.enable_slideshow_switch.connect("toggled", Lang.bind(this, this._on_slideshow_enabled_toggled)); this._applet_context_menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.next_image_context_menu_item = new PopupMenu.PopupIconMenuItem(_("Next Background"), "media-seek-forward", St.IconType.SYMBOLIC); this.next_image_context_menu_item.connect('activate', Lang.bind(this, this.get_next_image)); this._applet_context_menu.addMenuItem(this.next_image_context_menu_item); this._applet_context_menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.open_settings_context_menu_item = new PopupMenu.PopupIconMenuItem(_("Background Settings"), "preferences-desktop-wallpaper", St.IconType.SYMBOLIC); this.open_settings_context_menu_item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings backgrounds") })); this._applet_context_menu.addMenuItem(this.open_settings_context_menu_item); } catch(e) { global.logError(e); } }, on_applet_clicked: function(event) { if (this._slideshowSettings.get_boolean("slideshow-enabled")) { if (!this._slideshowSettings.get_boolean("slideshow-paused")) { this._slideshowSettings.set_boolean("slideshow-paused", true); this.set_applet_icon_symbolic_name('slideshow-pause-symbolic.svg'); this.set_applet_tooltip(_("Click to resume the slideshow")); } else { this._slideshowSettings.set_boolean("slideshow-paused", false); this.set_applet_icon_symbolic_name('slideshow-play-symbolic.svg'); this.set_applet_tooltip(_("Click to pause the slideshow")); } } }, _on_slideshow_enabled_toggled: function() { if (this._slideshowSettings.get_boolean("slideshow-enabled")) { this._slideshowSettings.set_boolean("slideshow-enabled", false); this.set_applet_icon_symbolic_name('slideshow-disabled-symbolic.svg'); this.set_applet_tooltip(_("The slideshow is disabled")); } else { this._slideshowSettings.set_boolean("slideshow-enabled", true); this.set_applet_icon_symbolic_name('slideshow-play-symbolic.svg'); this.set_applet_tooltip(_("Click to pause the slideshow")); } }, _on_slideshow_enabled_changed: function() { if (this._slideshowSettings.get_boolean("slideshow-enabled")) { this.enable_slideshow_switch.setToggleState(true); this.set_applet_icon_symbolic_name('slideshow-play-symbolic.svg'); this.set_applet_tooltip(_("Click to pause the slideshow")); } else { this.enable_slideshow_switch.setToggleState(false); this.set_applet_icon_symbolic_name('slideshow-disabled-symbolic.svg'); this.set_applet_tooltip(_("The slideshow is disabled")); } }, _on_slideshow_paused_changed: function() { if (this._slideshowSettings.get_boolean("slideshow-paused")) { this.set_applet_icon_symbolic_name('slideshow-pause-symbolic.svg'); this.set_applet_tooltip(_("Click to resume the slideshow")); } else { this.set_applet_icon_symbolic_name('slideshow-play-symbolic.svg'); this.set_applet_tooltip(_("Click to pause the slideshow")); } }, get_next_image: function() { Main.slideshowManager.getNextImage(); } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/expo@cinnamon.org/0000775000175000017500000000000012625666430024102 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/expo@cinnamon.org/settings-schema.json0000664000175000017500000000017412625666430030075 0ustar fabiofabio{ "activate-on-hover": { "type": "checkbox", "default" : false, "description" : "Activate expo on hover" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/expo@cinnamon.org/icons/0000775000175000017500000000000012625666430025215 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/expo@cinnamon.org/icons/cinnamon-expo-symbolic.svg0000664000175000017500000000045012625666430032327 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/expo@cinnamon.org/icon.png0000664000175000017500000000256512625666430025550 0ustar fabiofabioPNG  IHDR szzsRGBbKGD pHYs B(xtIME 6;BIDATXŗkU?f^4DD!B@.JKvEŅF7܈ RJBRh5!֘V$}w=.yFցaos==g>L#@>#{յP^~gϾ[v;oŋ]SXQUǞ"x~~~fc^M[iv⥅8}ιq`Xse#wWW= Z^yyǝ_xRYb%`\n Hǎ|+&&961u}} ',""Gyxቧ!ɹG0}֑n]㇍o <>Å pwWBه!G$Dk˃͇1թ_g !kd '#VWW+NJpniUlQjԚFfjsXk630`( NOIiIAT0FAJ`dX@q-\kmł v|vvw^_W&G֫KQ 1^!8gi5X qii~k|TQ *K`Yu.C ?)Vqf/ cYu8J\$b%9^__ÇU˲qKyFɶAb& ell4<ȩS=a {`hW:@*EĘ*H1*@h &0!BH%0tA8[ȰI$Բ!N(D@TTPpJA hrɹ9.-G#(kbßѨTʊ dQT[kƺ"qnmBTTӰNrtM7BP|P#Fdk1n!J[x'ke image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme ././@LongLink0000644000000000000000000000017700000000000011610 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/applets/on-screen-keyboard@cinnamon.org/icons/on-screen-keyboard-disabled-symbolic.svgCinnamon-2.8.6/files/usr/share/cinnamon/applets/on-screen-keyboard@cinnamon.org/icons/on-screen-keyb0000664000175000017500000000526112625666430032501 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme Cinnamon-2.8.6/files/usr/share/cinnamon/applets/on-screen-keyboard@cinnamon.org/icon.png0000664000175000017500000000040212625666430030250 0ustar fabiofabioPNG  IHDR szzbKGD pHYs B(xtIME .IDATX 0 F_JאWA<8iG F< "wkB |)4#EU50#_@<RJ_#Yo>Sxk܄>} ڷ݄>hl?i@|Z9+`bFmIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/on-screen-keyboard@cinnamon.org/metadata.json0000664000175000017500000000022412625666430031267 0ustar fabiofabio{ "uuid": "on-screen-keyboard@cinnamon.org", "name": "On-Screen keyboard", "description": "A Cinnamon applet to launch the on-screen keyboard" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/on-screen-keyboard@cinnamon.org/applet.js0000664000175000017500000000357012625666430030446 0ustar fabiofabioconst Applet = imports.ui.applet; const Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const Interfaces = imports.misc.interfaces; const Util = imports.misc.util; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; const GLib = imports.gi.GLib; const Gvc = imports.gi.Gvc; const Pango = imports.gi.Pango; const Tooltips = imports.ui.tooltips; const Main = imports.ui.main; const Settings = imports.ui.settings; function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instanceId); this.settings = new Gio.Settings({ schema_id: 'org.cinnamon.desktop.a11y.applications' }); this.settings.connect('changed::screen-keyboard-enabled', Lang.bind(this, this.update_status)); this.update_status(); }, on_applet_clicked: function(event) { Main.layoutManager.showKeyboard(); }, update_status: function() { if (this.settings.get_boolean('screen-keyboard-enabled')) { this.set_applet_icon_symbolic_name('on-screen-keyboard'); this.set_applet_tooltip(_("Click to show the on-screen keyboard")); } else { this.set_applet_icon_symbolic_name('on-screen-keyboard-disabled'); this.set_applet_tooltip(_("The on-screen keyboard is disabled. It can be enabled in the accessibility settings.")); } } }; function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/network@cinnamon.org/0000775000175000017500000000000012625666430024620 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/network@cinnamon.org/metadata.json0000664000175000017500000000020312625666430027266 0ustar fabiofabio{ "uuid": "network@cinnamon.org", "name": "Network Manager", "description": "Network manager applet", "icon": "network-idle" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js0000664000175000017500000025036512625666430026456 0ustar fabiofabioconst Applet = imports.ui.applet; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Lang = imports.lang; const NetworkManager = imports.gi.NetworkManager; const NMClient = imports.gi.NMClient; const Signals = imports.signals; const St = imports.gi.St; const Mainloop = imports.mainloop; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const MessageTray = imports.ui.messageTray; const ModemManager = imports.misc.modemManager; const Util = imports.misc.util; const DEFAULT_PERIODIC_UPDATE_FREQUENCY_SECONDS = 10; const FAST_PERIODIC_UPDATE_FREQUENCY_SECONDS = 2; const NMConnectionCategory = { INVALID: 'invalid', WIRED: 'wired', WIRELESS: 'wireless', WWAN: 'wwan', VPN: 'vpn' }; const NMAccessPointSecurity = { UNKNOWN: 0, NONE: 1, WEP: 2, WPA_PSK: 3, WPA2_PSK: 4, WPA_ENT: 5, WPA2_ENT: 6 }; // small optimization, to avoid using [] all the time const NM80211Mode = NetworkManager['80211Mode']; const NM80211ApFlags = NetworkManager['80211ApFlags']; const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags']; // number of wireless networks that should be visible // (the remaining are placed into More...) const NUM_VISIBLE_NETWORKS = 5; function macToArray(string) { return string.split(':').map(function(el) { return parseInt(el, 16); }); } function macCompare(one, two) { for (let i = 0; i < 6; i++) { if (one[i] != two[i]) return false; } return true; } function ssidCompare(one, two) { if (!one || !two) return false; if (one.length != two.length) return false; for (let i = 0; i < one.length; i++) { if (one[i] != two[i]) return false; } return true; } // shared between NMNetworkMenuItem and NMDeviceWWAN function signalToIcon(value) { if (value > 80) return 'excellent'; if (value > 55) return 'good'; if (value > 30) return 'ok'; if (value > 5) return 'weak'; return 'none'; } // shared between NMNetworkMenuItem and NMDeviceWireless function sortAccessPoints(accessPoints) { return accessPoints.sort(function (one, two) { return two.strength - one.strength; }); } function ssidToLabel(ssid) { let label = NetworkManager.utils_ssid_to_utf8(ssid); if (!label) label = _(""); return label; } function NMNetworkMenuItem() { this._init.apply(this, arguments); } NMNetworkMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(accessPoints, title, params) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params); accessPoints = sortAccessPoints(accessPoints); this.bestAP = accessPoints[0]; if (!title) { let ssid = this.bestAP.get_ssid(); title = ssidToLabel(ssid); } this._label = new St.Label({ text: title }); this.addActor(this._label); let strStrengh = new String(this.bestAP.strength); strStrengh = strStrengh + '%'; this._labelStrength = new St.Label({ text: strStrengh }); this.addActor(this._labelStrength, { align: St.Align.END }); this._icons = new St.BoxLayout({ style_class: 'nm-menu-item-icons' }); this.addActor(this._icons, { align: St.Align.END }); this._signalIcon = new St.Icon({ icon_name: this._getIcon(), style_class: 'popup-menu-icon' }); this._icons.add_actor(this._signalIcon); this._secureIcon = new St.Icon({ style_class: 'popup-menu-icon' }); if (this.bestAP._secType != NMAccessPointSecurity.UNKNOWN && this.bestAP._secType != NMAccessPointSecurity.NONE) this._secureIcon.icon_name = 'network-wireless-encrypted'; this._icons.add_actor(this._secureIcon); this._accessPoints = [ ]; for (let i = 0; i < accessPoints.length; i++) { let ap = accessPoints[i]; // need a wrapper object here, because the access points can be shared // between many NMNetworkMenuItems let apObj = { ap: ap, updateId: ap.connect('notify::strength', Lang.bind(this, this._updated)) }; this._accessPoints.push(apObj); } }, _updated: function(ap) { if (ap.strength > this.bestAP.strength) this.bestAP = ap; this._signalIcon.icon_name = this._getIcon(); let strStrengh = new String(this.bestAP.strength); strStrengh = strStrengh + '%'; this._labelStrength.set_text(strStrengh); }, _getIcon: function() { if (this.bestAP.mode == NM80211Mode.ADHOC) return 'network-workgroup'; else return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength); }, updateAccessPoints: function(accessPoints) { for (let i = 0; i < this._accessPoints.length; i++) { let apObj = this._accessPoints[i]; apObj.ap.disconnect(apObj.updateId); apObj.updateId = 0; } accessPoints = sortAccessPoints(accessPoints); this.bestAP = accessPoints[0]; this._accessPoints = [ ]; for (let i = 0; i < accessPoints; i++) { let ap = accessPoints[i]; let apObj = { ap: ap, updateId: ap.connect('notify::strength', Lang.bind(this, this._updated)) }; this._accessPoints.push(apObj); } }, destroy: function() { for (let i = 0; i < this._accessPoints.length; i++) { let apObj = this._accessPoints[i]; apObj.ap.disconnect(apObj.updateId); apObj.updateId = 0; } PopupMenu.PopupBaseMenuItem.prototype.destroy.call(this); } }; function NMWiredSectionTitleMenuItem() { this._init.apply(this, arguments); } NMWiredSectionTitleMenuItem.prototype = { __proto__: PopupMenu.PopupSwitchMenuItem.prototype, _init: function(label, params) { params = params || { }; params.style_class = 'popup-subtitle-menu-item'; PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, label, false, params); }, updateForDevice: function(device) { if (device) { this._device = device; this.setStatus(device.statusLabel); this.setToggleState(device.connected); // if this device is not controllable, hide the switch this._switch.actor.visible = device.controllable; } else this.setStatus(''); }, activate: function(event) { PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event); if (!this._device) { log('Section title activated when there is more than one device, should be non reactive'); return; } let newState = this._switch.state; // Immediately reset the switch to false, it will be updated appropriately // by state-changed signals in devices (but fixes the VPN not being in sync // if the ActiveConnection object is never seen by libnm-glib) this._switch.setToggleState(false); if (newState) this._device.activate(); else this._device.deactivate(); } }; function NMWirelessSectionTitleMenuItem() { this._init.apply(this, arguments); } NMWirelessSectionTitleMenuItem.prototype = { __proto__: PopupMenu.PopupSwitchMenuItem.prototype, _init: function(client, property, title, params) { params = params || { }; params.style_class = 'popup-subtitle-menu-item'; PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, title, false, params); this._client = client; this._property = property + '_enabled'; this._propertyHardware = property + '_hardware_enabled'; this._setEnabledFunc = property + '_set_enabled'; this._client.connect('notify::' + property + '-enabled', Lang.bind(this, this._propertyChanged)); this._client.connect('notify::' + property + '-hardware-enabled', Lang.bind(this, this._propertyChanged)); this._propertyChanged(); }, updateForDevice: function(device) { this._device = device; // we show the switch // - if there not just one device // - if the switch is off // - if the device is activated or disconnected if (device && this._softwareEnabled && this._hardwareEnabled) { this.setStatus(device.statusLabel); this._switch.actor.visible = device.controllable; } else { this.setStatus(null); this._switch.actor.show(); } }, activate: function(event) { PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event); log(this._setEnabledFunc); this._client[this._setEnabledFunc](this._switch.state); if (!this._device) { log('Section title activated when there is more than one device, should be non reactive'); return; } let newState = this._switch.state; if (newState) this._device.activate(); else this._device.deactivate(); this.emit('enabled-changed', this._switch.state); }, _propertyChanged: function() { this._softwareEnabled = this._client[this._property]; this._hardwareEnabled = this._client[this._propertyHardware]; let enabled = this._softwareEnabled && this._hardwareEnabled; this.setToggleState(enabled); if (!this._hardwareEnabled) /* Translators: this indicates that wireless or wwan is disabled by hardware killswitch */ this.setStatus(_("disabled")); this.emit('enabled-changed', enabled); } }; function NMDevice() { throw new TypeError('Instantanting abstract class NMDevice'); } NMDevice.prototype = { _init: function(client, device, connections) { this.device = device; if (device) { this.device._delegate = this; this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged)); } else this._stateChangedId = 0; // protected this._client = client; this._connections = [ ]; for (let i = 0; i < connections.length; i++) { if (!connections[i]._uuid) continue; if (!this.connectionValid(connections[i])) continue; // record the connection let obj = { connection: connections[i], name: connections[i]._name, uuid: connections[i]._uuid, timestamp: connections[i]._timestamp, }; this._connections.push(obj); } this._connections.sort(this._connectionSortFunction); this._activeConnection = null; this._activeConnectionItem = null; this._autoConnectionItem = null; this._overflowItem = null; if (this.device) { this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' }); this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) { if (state) this.activate(); else this.deactivate(); this.emit('enabled-changed'); })); this._updateStatusItem(); } this.section = new PopupMenu.PopupMenuSection(); this._createSection(); }, destroy: function() { if (this.device) this.device._delegate = null; if (this._stateChangedId) { // Need to go through GObject.Object.prototype because // nm_device_disconnect conflicts with g_signal_disconnect GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId); this._stateChangedId = 0; } if (this._carrierChangedId) { // see above for why this is needed GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId); this._carrierChangedId = 0; } if (this._firmwareChangedId) { GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId); this._firmwareChangedId = 0; } this._clearSection(); if (this.statusItem) this.statusItem.destroy(); this.section.destroy(); }, deactivate: function() { this.device.disconnect(function() {}); }, activate: function() { if (this._activeConnection) // nothing to do return; // pick the most recently used connection and connect to that // or if no connections ever set, create an automatic one if (this._connections.length > 0) { this._client.activate_connection(this._connections[0].connection, this.device, null, null); } else if (this._autoConnectionName) { let connection = this._createAutomaticConnection(); if (connection) this._client.add_and_activate_connection(connection, this.device, null, null); } }, get connected() { return this.device.state == NetworkManager.DeviceState.ACTIVATED; }, setActiveConnection: function(activeConnection) { if (activeConnection == this._activeConnection) // nothing to do return; // remove any UI if (this._activeConnectionItem) { this._activeConnectionItem.destroy(); this._activeConnectionItem = null; } this._activeConnection = activeConnection; this._clearSection(); this._createSection(); }, checkConnection: function(connection) { let exists = this._findConnection(connection._uuid) != -1; let valid = this.connectionValid(connection); if (exists && !valid) this.removeConnection(connection); else if (!exists && valid) this.addConnection(connection); }, addConnection: function(connection) { // record the connection let obj = { connection: connection, name: connection._name, uuid: connection._uuid, timestamp: connection._timestamp, }; this._connections.push(obj); this._connections.sort(this._connectionSortFunction); this._clearSection(); this._createSection(); }, removeConnection: function(connection) { if (!connection._uuid) { log('Cannot remove a connection without an UUID'); return; } let pos = this._findConnection(connection._uuid); if (pos == -1) { // this connection was never added, nothing to do here return; } let obj = this._connections[pos]; if (obj.item) obj.item.destroy(); this._connections.splice(pos, 1); if (this._connections.length <= 1) { // We need to show the automatic connection again // (or in the case of NMDeviceWired, we want to hide // the only explicit connection) this._clearSection(); this._createSection(); } }, connectionValid: function(connection) { return this.device.connection_valid(connection); }, _connectionSortFunction: function(one, two) { if (one.timestamp == two.timestamp) return GLib.utf8_collate(one.name, two.name); return two.timestamp - one.timestamp; }, setEnabled: function(enabled) { // do nothing by default, we want to keep the conneciton list visible // in the majority of cases (wired, wwan, vpn) }, get statusLabel(){ switch(this.device.state) { case NetworkManager.DeviceState.DISCONNECTED: case NetworkManager.DeviceState.ACTIVATED: return null; case NetworkManager.DeviceState.UNMANAGED: /* Translators: this is for network devices that are physically present but are not under NetworkManager's control (and thus cannot be used in the menu) */ return _("unmanaged"); case NetworkManager.DeviceState.DEACTIVATING: return _("disconnecting..."); case NetworkManager.DeviceState.PREPARE: case NetworkManager.DeviceState.CONFIG: case NetworkManager.DeviceState.IP_CONFIG: case NetworkManager.DeviceState.IP_CHECK: case NetworkManager.DeviceState.SECONDARIES: return _("connecting..."); case NetworkManager.DeviceState.NEED_AUTH: /* Translators: this is for network connections that require some kind of key or password */ return _("authentication required"); case NetworkManager.DeviceState.UNAVAILABLE: // This state is actually a compound of various states (generically unavailable, // firmware missing, carrier not available), that are exposed by different properties // (whose state may or may not updated when we receive state-changed). if (!this._firmwareMissingId) this._firmwareMissingId = this.device.connect('notify::firmware-missing', Lang.bind(this, this._substateChanged)); if (this.device.firmware_missing) { /* Translators: this is for devices that require some kind of firmware or kernel module, which is missing */ return _("firmware missing"); } if (this.device.capabilities & NetworkManager.DeviceCapabilities.CARRIER_DETECT) { if (!this._carrierChangedId) this._carrierChangedId = this.device.connect('notify::carrier', Lang.bind(this, this._substateChanged)); if (!this.carrier) { /* Translators: this is for wired network devices that are physically disconnected */ return _("cable unplugged"); } } /* Translators: this is for a network device that cannot be activated (for example it is disabled by rfkill, or it has no coverage */ return _("unavailable"); case NetworkManager.DeviceState.FAILED: return _("connection failed"); default: log('Device state invalid, is %d'.format(this.device.state)); return 'invalid'; } }, get controllable(){ // controllable for every state except unavailable or unmanaged if(this.device.state === NetworkManager.DeviceState.UNAVAILABLE || this.device.state === NetworkManager.DeviceState.UNMANAGED) return false; return true; }, // protected _createAutomaticConnection: function() { throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection'); }, _findConnection: function(uuid) { for (let i = 0; i < this._connections.length; i++) { let obj = this._connections[i]; if (obj.uuid == uuid) return i; } return -1; }, _clearSection: function() { // Clear everything this.section.removeAll(); this._autoConnectionItem = null; this._activeConnectionItem = null; this._overflowItem = null; for (let i = 0; i < this._connections.length; i++) { this._connections[i].item = null; } }, _shouldShowConnectionList: function() { return (this.device.state >= NetworkManager.DeviceState.DISCONNECTED); }, _createSection: function() { if (!this._shouldShowConnectionList()) return; if (this._activeConnection) { this._createActiveConnectionItem(); this.section.addMenuItem(this._activeConnectionItem); } if (this._connections.length > 0) { let activeOffset = this._activeConnectionItem ? 1 : 0; for(let j = 0; j < this._connections.length; ++j) { let obj = this._connections[j]; if (this._activeConnection && obj.connection == this._activeConnection._connection) continue; obj.item = this._createConnectionItem(obj); if (j + activeOffset >= NUM_VISIBLE_NETWORKS) { if (!this._overflowItem) { this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More")); this.section.addMenuItem(this._overflowItem); } this._overflowItem.menu.addMenuItem(obj.item); } else this.section.addMenuItem(obj.item); } } else if (this._autoConnectionName) { this._autoConnectionItem = new PopupMenu.PopupMenuItem(this._autoConnectionName); this._autoConnectionItem.connect('activate', Lang.bind(this, function() { let connection = this._createAutomaticConnection(); if (connection) this._client.add_and_activate_connection(connection, this.device, null, null); })); this.section.addMenuItem(this._autoConnectionItem); } }, _createConnectionItem: function(obj) { let connection = obj.connection; let item = new PopupMenu.PopupMenuItem(obj.name); item.connect('activate', Lang.bind(this, function() { this._client.activate_connection(connection, this.device, null, null); })); return item; }, _createActiveConnectionItem: function() { let title; let active = this._activeConnection._connection; if (active) { title = active._name; } else { /* TRANSLATORS: this is the indication that a connection for another logged in user is active, and we cannot access its settings (including the name) */ title = _("Connected (private)"); } this._activeConnectionItem = new PopupMenu.PopupMenuItem(title, { reactive: false }); this._activeConnectionItem.setShowDot(true); }, _deviceStateChanged: function(device, newstate, oldstate, reason) { if (newstate == oldstate) { log('device emitted state-changed without actually changing state'); return; } if (oldstate == NetworkManager.DeviceState.ACTIVATED) { this.emit('network-lost'); } if (newstate == NetworkManager.DeviceState.FAILED) { this.emit('activation-failed', reason); } this._updateStatusItem(); this._clearSection(); this._createSection(); this.emit('state-changed'); }, _updateStatusItem: function() { if (this._carrierChangedId) { // see above for why this is needed GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId); this._carrierChangedId = 0; } if (this._firmwareChangedId) { GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId); this._firmwareChangedId = 0; } this.statusItem.setStatus(this.statusLabel); this.statusItem.setToggleState(this.connected); // if this device is not controllable, hide the switch this.statusItem._switch.actor.visible = this.controllable; }, _substateChanged: function() { this.statusItem.setStatus(this.statusLabel); this.statusItem._switch.actor.visible = this.controllable; this.emit('state-changed'); }, _getDescription: function() { let dev_product = this.device.get_product(); let dev_vendor = this.device.get_vendor(); if (!dev_product || !dev_vendor) { if (this.device.name) { return this.device.name; } else { switch (this.device.get_device_type()) { case NetworkManager.DeviceType.ETHERNET: return _("Ethernet"); case NetworkManager.DeviceType.WIFI: return _("Wifi"); case NetworkManager.DeviceType.MODEM: return _("Modem"); case NetworkManager.DeviceType.BT: return _("Bluetooth"); default: return ""; } } } let product = Util.fixupPCIDescription(dev_product); let vendor = Util.fixupPCIDescription(dev_vendor); let out = ''; // Another quick hack; if all of the fixed up vendor string // is found in product, ignore the vendor. if (product.indexOf(vendor) == -1) out += vendor + ' '; out += product; return out; } }; Signals.addSignalMethods(NMDevice.prototype); function NMDeviceWired() { this._init.apply(this, arguments); } NMDeviceWired.prototype = { __proto__: NMDevice.prototype, _init: function(client, device, connections) { this._autoConnectionName = _("Auto Ethernet"); this.category = NMConnectionCategory.WIRED; NMDevice.prototype._init.call(this, client, device, connections); }, _createSection: function() { NMDevice.prototype._createSection.call(this); // if we have only one connection (normal or automatic) // we hide the connection list, and use the switch to control // the device // we can do it here because addConnection and removeConnection // both call _createSection at some point if (this._connections.length <= 1) this.section.actor.hide(); else this.section.actor.show(); }, _createAutomaticConnection: function() { let connection = new NetworkManager.Connection(); connection._uuid = NetworkManager.utils_uuid_generate(); connection.add_setting(new NetworkManager.SettingWired()); connection.add_setting(new NetworkManager.SettingConnection({ uuid: connection._uuid, id: this._autoConnectionName, type: NetworkManager.SETTING_WIRED_SETTING_NAME, autoconnect: true })); return connection; } }; function NMDeviceModem() { this._init.apply(this, arguments); } NMDeviceModem.prototype = { __proto__: NMDevice.prototype, _init: function(client, device, connections) { let is_wwan = false; this._enabled = true; this.mobileDevice = null; this._connectionType = 'ppp'; this._capabilities = device.current_capabilities; // Support new ModemManager1 devices if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) { try { is_wwan = true; this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities); if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) { this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME; } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) { this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME; } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) { this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME; } } catch (e){ global.logError(e); } } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) { is_wwan = true; this.mobileDevice = new ModemManager.ModemGsm(device.udi); this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME; } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) { is_wwan = true; this.mobileDevice = new ModemManager.ModemCdma(device.udi); this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME; } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) { is_wwan = true; // FIXME: support signal quality } if (is_wwan) { this.category = NMConnectionCategory.WWAN; this._autoConnectionName = _("Auto broadband"); } else { this.category = NMConnectionCategory.WIRED; this._autoConnectionName = _("Auto dial-up"); } if (this.mobileDevice) { this._operatorNameId = this.mobileDevice.connect('notify::operator-name', Lang.bind(this, function() { if (this._operatorItem) { let name = this.mobileDevice.operator_name; if (name) { this._operatorItem.label.text = name; this._operatorItem.actor.show(); } else this._operatorItem.actor.hide(); } })); this._signalQualityId = this.mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() { if (this._operatorItem) { this._operatorItem.setIcon(this._getSignalIcon()); } })); } NMDevice.prototype._init.call(this, client, device, connections); }, setEnabled: function(enabled) { this._enabled = enabled; if (this.category == NMConnectionCategory.WWAN) { if (enabled) { // prevent "network unavailable" statuses this.statusItem.setStatus(null); } else this.statusItem.setStatus(this.statusLabel); } NMDevice.prototype.setEnabled.call(this, enabled); }, get connected() { return this._enabled && this.device.state == NetworkManager.DeviceState.ACTIVATED; }, destroy: function() { if (this._operatorNameId) { this.mobileDevice.disconnect(this._operatorNameId); this._operatorNameId = 0; } if (this._signalQualityId) { this.mobileDevice.disconnect(this._signalQualityId); this._signalQualityId = 0; } NMDevice.prototype.destroy.call(this); }, _getSignalIcon: function() { return 'network-cellular-signal-' + signalToIcon(this.mobileDevice.signal_quality); }, _createSection: function() { if (!this._shouldShowConnectionList()) return; if (this.mobileDevice) { // If operator_name is null, just pass the empty string, as the item is hidden anyway this._operatorItem = new PopupMenu.PopupImageMenuItem(this.mobileDevice.operator_name || '', this._getSignalIcon(), { reactive: false }); if (!this.mobileDevice.operator_name) this._operatorItem.actor.hide(); this.section.addMenuItem(this._operatorItem); } NMDevice.prototype._createSection.call(this); }, _clearSection: function() { this._operatorItem = null; NMDevice.prototype._clearSection.call(this); }, _createAutomaticConnection: function() { // Mobile wizard is too complex for Cinnamon UI and // is handled by the network panel Util.spawn(['cinnamon-settings', 'network', 'connect-3g', this.device.get_path()]); return null; } }; function NMDeviceBluetooth() { this._init.apply(this, arguments); } NMDeviceBluetooth.prototype = { __proto__: NMDevice.prototype, _init: function(client, device, connections) { this._autoConnectionName = this._makeConnectionName(device); device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName)); this.category = NMConnectionCategory.WWAN; NMDevice.prototype._init.call(this, client, device, connections); }, _createAutomaticConnection: function() { let connection = new NetworkManager.Connection; connection._uuid = NetworkManager.utils_uuid_generate(); connection.add_setting(new NetworkManager.SettingBluetooth); connection.add_setting(new NetworkManager.SettingConnection({ uuid: connection._uuid, id: this._autoConnectionName, type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME, autoconnect: false })); return connection; }, _makeConnectionName: function(device) { let name = device.name; if (name) return _("Auto %s").format(name); else return _("Auto bluetooth"); }, _updateAutoConnectionName: function() { this._autoConnectionName = this._makeConnectionName(this.device); this._clearSection(); this._createSection(); } }; // Not a real device, but I save a lot code this way function NMDeviceVPN() { this._init.apply(this, arguments); } NMDeviceVPN.prototype = { __proto__: NMDevice.prototype, _init: function(client) { // Disable autoconnections this._autoConnectionName = null; this.category = NMConnectionCategory.VPN; NMDevice.prototype._init.call(this, client, null, [ ]); }, connectionValid: function(connection) { return connection._type == NetworkManager.SETTING_VPN_SETTING_NAME; }, get empty() { return this._connections.length == 0; }, get connected() { return !!this._activeConnection; }, setActiveConnection: function(activeConnection) { NMDevice.prototype.setActiveConnection.call(this, activeConnection); this.emit('active-connection-changed'); }, _shouldShowConnectionList: function() { return true; }, deactivate: function() { if (this._activeConnection) this._client.deactivate_connection(this._activeConnection); }, statusLabel: null, controllable: true }; function NMDeviceWireless() { this._init.apply(this, arguments); } NMDeviceWireless.prototype = { __proto__: NMDevice.prototype, _init: function(client, device, connections) { this.category = NMConnectionCategory.WIRELESS; this._overflowItem = null; this._networks = [ ]; // breaking the layers with this, but cannot call // this.connectionValid until I have a device this.device = device; let validConnections = connections.filter(Lang.bind(this, function(connection) { return this.connectionValid(connection); })); let accessPoints = device.get_access_points() || [ ]; for (let i = 0; i < accessPoints.length; i++) { // Access points are grouped by network let ap = accessPoints[i]; if (ap.get_ssid() == null) { // hidden access point cannot be added, we need to know // the SSID and security details to connect // nevertheless, the access point can acquire a SSID when // NetworkManager connects to it (via nmcli or cinnamon-settings) ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb)); continue; } let pos = this._findNetwork(ap); let obj; if (pos != -1) { obj = this._networks[pos]; obj.accessPoints.push(ap); } else { obj = { ssid: ap.get_ssid(), mode: ap.mode, security: this._getApSecurityType(ap), connections: [ ], item: null, accessPoints: [ ap ] }; obj.ssidText = ssidToLabel(obj.ssid); this._networks.push(obj); } // Check if some connection is valid for this AP for (let j = 0; j < validConnections.length; j++) { let connection = validConnections[j]; if (ap.connection_valid(connection) && obj.connections.indexOf(connection) == -1) { obj.connections.push(connection); } } } if (this.device.active_access_point) { let networkPos = this._findNetwork(this.device.active_access_point); if (networkPos == -1) // the connected access point is invisible this._activeNetwork = null; else this._activeNetwork = this._networks[networkPos]; } else { this._activeNetwork = null; } this._networks.sort(this._networkSortFunction); this._apChangedId = device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged)); this._apAddedId = device.connect('access-point-added', Lang.bind(this, this._accessPointAdded)); this._apRemovedId = device.connect('access-point-removed', Lang.bind(this, this._accessPointRemoved)); NMDevice.prototype._init.call(this, client, device, validConnections); }, destroy: function() { if (this._apChangedId) { // see above for this HACK GObject.Object.prototype.disconnect.call(this.device, this._apChangedId); this._apChangedId = 0; } if (this._apAddedId) { GObject.Object.prototype.disconnect.call(this.device, this._apAddedId); this._apAddedId = 0; } if (this._apRemovedId) { GObject.Object.prototype.disconnect.call(this.device, this._apRemovedId); this._apRemovedId = 0; } NMDevice.prototype.destroy.call(this); }, setEnabled: function(enabled) { if (enabled) { this.statusItem.actor.show(); this.section.actor.show(); } else { this.statusItem.actor.hide(); this.section.actor.hide(); } }, activate: function() { if (this._activeConnection) // nothing to do return; // among all visible networks, pick the last recently used connection let best = null; let bestApObj = null; let bestTime = 0; for (let i = 0; i < this._networks.length; i++) { let apObj = this._networks[i]; for (let j = 0; j < apObj.connections.length; j++) { let connection = apObj.connections[j]; if (connection._timestamp > bestTime) { best = connection; bestTime = connection._timestamp; bestApObj = apObj; } } } if (best) { for (let i = 0; i < bestApObj.accessPoints.length; i++) { let ap = bestApObj.accessPoints[i]; if (ap.connection_valid(best)) { this._client.activate_connection(best, this.device, ap.dbus_path, null); break; } } return; } // XXX: what else to do? // for now, just pick a random network // (this function is called in a corner case anyway, that is, only when // the user toggles the switch and has more than one wireless device) if (this._networks.length > 0) { let connection = this._createAutomaticConnection(this._networks[0]); let accessPoints = sortAccessPoints(this._networks[0].accessPoints); this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null); } }, _notifySsidCb: function(accessPoint) { if (accessPoint.get_ssid() != null) { accessPoint.disconnect(accessPoint._notifySsidId); accessPoint._notifySsidId = 0; this._accessPointAdded(this.device, accessPoint); } }, _activeApChanged: function() { this._activeNetwork = null; let activeAp = this.device.active_access_point; if (activeAp) { let res = this._findExistingNetwork(activeAp); if (res != null) this._activeNetwork = this._networks[res.network]; } // we don't refresh the view here, setActiveConnection will }, _getApSecurityType: function(accessPoint) { if (accessPoint._secType) return accessPoint._secType; let flags = accessPoint.flags; let wpa_flags = accessPoint.wpa_flags; let rsn_flags = accessPoint.rsn_flags; let type; if (rsn_flags != NM80211ApSecurityFlags.NONE) { /* RSN check first so that WPA+WPA2 APs are treated as RSN/WPA2 */ if (rsn_flags & NM80211ApSecurityFlags.KEY_MGMT_802_1X) type = NMAccessPointSecurity.WPA2_ENT; else if (rsn_flags & NM80211ApSecurityFlags.KEY_MGMT_PSK) type = NMAccessPointSecurity.WPA2_PSK; } else if (wpa_flags != NM80211ApSecurityFlags.NONE) { if (wpa_flags & NM80211ApSecurityFlags.KEY_MGMT_802_1X) type = NMAccessPointSecurity.WPA_ENT; else if (wpa_flags & NM80211ApSecurityFlags.KEY_MGMT_PSK) type = NMAccessPointSecurity.WPA_PSK; } else { if (flags & NM80211ApFlags.PRIVACY) type = NMAccessPointSecurity.WEP; else type = NMAccessPointSecurity.NONE; } // cache the found value to avoid checking flags all the time accessPoint._secType = type; return type; }, _networkSortFunction: function(one, two) { let oneHasConnection = one.connections.length != 0; let twoHasConnection = two.connections.length != 0; // place known connections first // (-1 = good order, 1 = wrong order) if (oneHasConnection && !twoHasConnection) return -1; else if (!oneHasConnection && twoHasConnection) return 1; return two.accessPoints[0].strength - one.accessPoints[0].strength; /** let oneHasSecurity = one.security != NMAccessPointSecurity.NONE; let twoHasSecurity = two.security != NMAccessPointSecurity.NONE; // place secure connections first // (we treat WEP/WPA/WPA2 the same as there is no way to // take them apart from the UI) if (oneHasSecurity && !twoHasSecurity) return -1; else if (!oneHasSecurity && twoHasSecurity) return 1; // sort alphabetically return GLib.utf8_collate(one.ssidText, two.ssidText); **/ }, _networkCompare: function(network, accessPoint) { if (!ssidCompare(network.ssid, accessPoint.get_ssid())) return false; if (network.mode != accessPoint.mode) return false; if (network.security != this._getApSecurityType(accessPoint)) return false; return true; }, _findExistingNetwork: function(accessPoint) { for (let i = 0; i < this._networks.length; i++) { let apObj = this._networks[i]; for (let j = 0; j < apObj.accessPoints.length; j++) { if (apObj.accessPoints[j] == accessPoint) return { network: i, ap: j }; } } return null; }, _findNetwork: function(accessPoint) { if (accessPoint.get_ssid() == null) return -1; for (let i = 0; i < this._networks.length; i++) { if (this._networkCompare(this._networks[i], accessPoint)) return i; } return -1; }, _accessPointAdded: function(device, accessPoint) { if (accessPoint.get_ssid() == null) { // This access point is not visible yet // Wait for it to get a ssid accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb)); return; } let pos = this._findNetwork(accessPoint); let apObj; let needsupdate = false; if (pos != -1) { apObj = this._networks[pos]; if (apObj.accessPoints.indexOf(accessPoint) != -1) { log('Access point was already seen, not adding again'); return; } apObj.accessPoints.push(accessPoint); if (apObj.item) apObj.item.updateAccessPoints(apObj.accessPoints); } else { apObj = { ssid: accessPoint.get_ssid(), mode: accessPoint.mode, security: this._getApSecurityType(accessPoint), connections: [ ], item: null, accessPoints: [ accessPoint ] }; apObj.ssidText = ssidToLabel(apObj.ssid); needsupdate = true; } // check if this enables new connections for this group for (let i = 0; i < this._connections.length; i++) { let connection = this._connections[i].connection; if (accessPoint.connection_valid(connection) && apObj.connections.indexOf(connection) == -1) { apObj.connections.push(connection); // this potentially changes the order needsupdate = true; } } if (needsupdate) { if (apObj.item) apObj.item.destroy(); if (pos != -1) this._networks.splice(pos, 1); if (this._networks.length == 0) { // only network in the list this._networks.push(apObj); this._clearSection(); this._createSection(); return; } // skip networks that should appear earlier let menuPos = 0; for (pos = 0; pos < this._networks.length && this._networkSortFunction(this._networks[pos], apObj) < 0; ++pos) { if (this._networks[pos] != this._activeNetwork) menuPos++; } // (re-)add the network this._networks.splice(pos, 0, apObj); if (this._shouldShowConnectionList()) { menuPos += (this._activeConnectionItem ? 1 : 0); this._createNetworkItem(apObj, menuPos); } } }, _accessPointRemoved: function(device, accessPoint) { let res = this._findExistingNetwork(accessPoint); if (res == null) { log('Removing an access point that was never added'); return; } let apObj = this._networks[res.network]; apObj.accessPoints.splice(res.ap, 1); if (apObj.accessPoints.length == 0) { if (this._activeNetwork == apObj) this._activeNetwork = null; if (apObj.item) apObj.item.destroy(); if (this._overflowItem) { if (!apObj.isMore) { // we removed an item in the main menu, and we have a more submenu // we need to extract the first item in more and move it to the submenu let item = this._overflowItem.menu.firstMenuItem; if (item && item._apObj) { item.destroy(); // clear the cycle, and allow the construction of the new item item._apObj.item = null; this._createNetworkItem(item._apObj, NUM_VISIBLE_NETWORKS-1); } else { log('The more... menu was existing and empty! This should not happen'); } } // This can happen if the removed connection is from the overflow // menu, or if we just moved the last connection out from the menu if (this._overflowItem.menu.numMenuItems == 0) { this._overflowItem.destroy(); this._overflowItem = null; } } this._networks.splice(res.network, 1); } else if (apObj.item) apObj.item.updateAccessPoints(apObj.accessPoints); }, _createAPItem: function(connection, accessPointObj, useConnectionName) { let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined); item._connection = connection; item.connect('activate', Lang.bind(this, function() { let accessPoints = sortAccessPoints(accessPointObj.accessPoints); for (let i = 0; i < accessPoints.length; i++) { if (accessPoints[i].connection_valid(connection)) { this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null); break; } } })); return item; }, _clearSection: function() { NMDevice.prototype._clearSection.call(this); for (let i = 0; i < this._networks.length; i++) this._networks[i].item = null; this._overflowItem = null; }, removeConnection: function(connection) { if (!connection._uuid) return; let pos = this._findConnection(connection._uuid); if (pos == -1) { // removing connection that was never added return; } let obj = this._connections[pos]; this._connections.splice(pos, 1); let anyauto = false, forceupdate = false; for (let i = 0; i < this._networks.length; i++) { let apObj = this._networks[i]; let connections = apObj.connections; for (let k = 0; k < connections.length; k++) { if (connections[k]._uuid == connection._uuid) { // remove the connection from the access point group connections.splice(k); anyauto = connections.length == 0; if (anyauto) { // this potentially changes the sorting order forceupdate = true; break; } if (apObj.item) { if (apObj.item instanceof PopupMenu.PopupSubMenuMenuItem) { let items = apObj.item.menu.getMenuItems(); if (items.length == 2) { // we need to update the connection list to convert this to a normal item forceupdate = true; } else { for (let j = 0; j < items.length; j++) { if (items[j]._connection._uuid == connection._uuid) { items[j].destroy(); break; } } } } else { apObj.item.destroy(); apObj.item = null; } } break; } } } if (forceupdate || anyauto) { this._networks.sort(this._networkSortFunction); this._clearSection(); this._createSection(); } }, addConnection: function(connection) { // record the connection let obj = { connection: connection, name: connection._name, uuid: connection._uuid, }; this._connections.push(obj); // find an appropriate access point let forceupdate = false; for (let i = 0; i < this._networks.length; i++) { let apObj = this._networks[i]; // Check if connection is valid for any of these access points for (let k = 0; k < apObj.accessPoints.length; k++) { let ap = apObj.accessPoints[k]; if (ap.connection_valid(connection)) { apObj.connections.push(connection); // this potentially changes the sorting order forceupdate = true; break; } } } if (forceupdate) { this._networks.sort(this._networkSortFunction); this._clearSection(); this._createSection(); } }, _createActiveConnectionItem: function() { let icon, title; if (this._activeConnection._connection) { let connection = this._activeConnection._connection; if (this._activeNetwork) this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined, { reactive: false }); else this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name, 'network-wireless-connected', { reactive: false }); } else { // We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it let menuItem; if (this._activeNetwork) this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined, { reactive: false }); else this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"), 'network-wireless-connected', { reactive: false }); } this._activeConnectionItem.setShowDot(true); }, _createAutomaticConnection: function(apObj) { let name; let ssid = NetworkManager.utils_ssid_to_utf8(apObj.ssid); if (ssid) { /* TRANSLATORS: this the automatic wireless connection name (including the network name) */ name = _("Auto %s").format(ssid); } else name = _("Auto wireless"); let connection = new NetworkManager.Connection(); connection.add_setting(new NetworkManager.SettingWireless()); connection.add_setting(new NetworkManager.SettingConnection({ id: name, autoconnect: true, // NetworkManager will know to ignore this if appropriate uuid: NetworkManager.utils_uuid_generate(), type: NetworkManager.SETTING_WIRELESS_SETTING_NAME })); return connection; }, _createNetworkItem: function(apObj, position) { if(!apObj.accessPoints || apObj.accessPoints.length == 0) { // this should not happen, but I have no idea why it happens return; } if(apObj.connections.length > 0) { if (apObj.connections.length == 1) { apObj.item = this._createAPItem(apObj.connections[0], apObj, false); } else { let title = apObj.ssidText; apObj.item = new PopupMenu.PopupSubMenuMenuItem(title); for (let i = 0; i < apObj.connections.length; i++) apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true)); } } else { apObj.item = new NMNetworkMenuItem(apObj.accessPoints); apObj.item.connect('activate', Lang.bind(this, function() { let accessPoints = sortAccessPoints(apObj.accessPoints); if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) { // 802.1x-enabled APs require further configuration, so they're // handled in cinnamon-settings Util.spawn(['cinnamon-settings', 'network', 'connect-8021x-wifi', this.device.get_path(), accessPoints[0].dbus_path]); } else { let connection = this._createAutomaticConnection(apObj); this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null) } })); } apObj.item._apObj = apObj; if (position < NUM_VISIBLE_NETWORKS) { apObj.isMore = false; this.section.addMenuItem(apObj.item, position); } else { if (!this._overflowItem) { this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More")); this.section.addMenuItem(this._overflowItem); } this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS); apObj.isMore = true; } }, _createSection: function() { if (!this._shouldShowConnectionList()) return; if(this._activeConnection) { this._createActiveConnectionItem(); this.section.addMenuItem(this._activeConnectionItem); } let activeOffset = this._activeConnectionItem ? 1 : 0; for(let j = 0; j < this._networks.length; j++) { let apObj = this._networks[j]; if (apObj == this._activeNetwork) continue; this._createNetworkItem(apObj, j + activeOffset); } }, }; function NMMessageTraySource() { this._init(); } NMMessageTraySource.prototype = { __proto__: MessageTray.Source.prototype, _init: function() { MessageTray.Source.prototype._init.call(this, _("Network Manager")); let icon = new St.Icon({ icon_name: 'network-transmit-receive', icon_type: St.IconType.SYMBOLIC, icon_size: this.ICON_SIZE }); this._setSummaryIcon(icon); } }; function MyApplet(metadata, orientation, panel_height, instance_id) { this._init(metadata, orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(metadata, orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.metadata = metadata; Main.systrayManager.registerRole("network", metadata.uuid); Main.systrayManager.registerRole("nm-applet", metadata.uuid); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._currentIconName = undefined; this._setIcon('network-offline'); this._client = NMClient.Client.new(); this._statusSection = new PopupMenu.PopupMenuSection(); this._statusItem = new PopupMenu.PopupMenuItem('', { style_class: 'popup-inactive-menu-item', reactive: false }); this._statusSection.addMenuItem(this._statusItem); this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() { this._client.networking_enabled = true; })); this._statusSection.actor.hide(); this.menu.addMenuItem(this._statusSection); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._devices = { }; this._devices.wired = { section: new PopupMenu.PopupMenuSection(), devices: [ ], item: new NMWiredSectionTitleMenuItem(_("Wired")) }; this._devices.wired.section.addMenuItem(this._devices.wired.item); this._devices.wired.section.actor.hide(); this.menu.addMenuItem(this._devices.wired.section); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._devices.wireless = { section: new PopupMenu.PopupMenuSection(), devices: [ ], item: this._makeToggleItem('wireless', _("Wireless")) }; this._devices.wireless.section.addMenuItem(this._devices.wireless.item); this._devices.wireless.section.actor.hide(); this.menu.addMenuItem(this._devices.wireless.section); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._devices.wwan = { section: new PopupMenu.PopupMenuSection(), devices: [ ], item: this._makeToggleItem('wwan', _("Mobile broadband")) }; this._devices.wwan.section.addMenuItem(this._devices.wwan.item); this._devices.wwan.section.actor.hide(); this.menu.addMenuItem(this._devices.wwan.section); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._devices.vpn = { section: new PopupMenu.PopupMenuSection(), device: new NMDeviceVPN(this._client), item: new NMWiredSectionTitleMenuItem(_("VPN Connections")) }; this._devices.vpn.device.connect('active-connection-changed', Lang.bind(this, function() { this._devices.vpn.item.updateForDevice(this._devices.vpn.device); })); this._devices.vpn.item.updateForDevice(this._devices.vpn.device); this._devices.vpn.section.addMenuItem(this._devices.vpn.item); this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section); this._devices.vpn.section.actor.hide(); this.menu.addMenuItem(this._devices.vpn.section); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addSettingsAction(_("Network Settings"), 'network'); this.menu.addAction(_("Network Connections"), Lang.bind(this, function() { Util.spawnCommandLine("nm-connection-editor"); })); this._activeConnections = [ ]; this._connections = [ ]; this._mainConnection = null; // Device types this._dtypes = { }; this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired; this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless; this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem; this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth; // TODO: WiMax support // Connection types this._ctypes = { }; this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS; this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED; this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED; this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED; this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN; this._settings = NMClient.RemoteSettings.new(null); this._connectionsReadId = this._settings.connect('connections-read', Lang.bind(this, function() { this._readConnections(); this._readDevices(); this._syncNMState(); // Connect to signals late so that early signals don't find in inconsistent state // and connect only once (this signal handler can be called again if NetworkManager goes up and down) if (!this._inited) { this._inited = true; this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState)); this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState)); this._client.connect('notify::state', Lang.bind(this, this._syncNMState)); this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon)); this._client.connect('device-added', Lang.bind(this, this._deviceAdded)); this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved)); this._settings.connect('new-connection', Lang.bind(this, this._newConnection)); } })); this._periodicUpdateIcon(); } catch (e) { global.logError(e); } }, _setIcon: function(name) { if (this._currentIconName !== name) { this.set_applet_icon_symbolic_name(name); this._currentIconName = name; } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _ensureSource: function() { if (!this._source) { this._source = new NMMessageTraySource(); this._source.connect('destroy', Lang.bind(this, function() { this._source = null; })); if (Main.messageTray) Main.messageTray.add(this._source); } }, _makeToggleItem: function(type, title) { let item = new NMWirelessSectionTitleMenuItem(this._client, type, title); item.connect('enabled-changed', Lang.bind(this, function(item, enabled) { let devices = this._devices[type].devices; devices.forEach(function(dev) { dev.setEnabled(enabled); }); this._syncSectionTitle(type); })); return item; }, _syncSectionTitle: function(category) { let devices = this._devices[category].devices; let item = this._devices[category].item; let section = this._devices[category].section; if (devices.length == 0) section.actor.hide(); else { section.actor.show(); if (devices.length == 1) { let dev = devices[0]; dev.statusItem.actor.hide(); item.updateForDevice(dev); } else { devices.forEach(function(dev) { dev.statusItem.actor.show(); }); // remove status text from the section title item item.updateForDevice(null); } } }, _readDevices: function() { let devices = this._client.get_devices() || [ ]; for (let i = 0; i < devices.length; ++i) { this._deviceAdded(this._client, devices[i]); } }, _notifyForDevice: function(device, iconName, title, text, urgency) { if (device._notification) device._notification.destroy(); /* must call after destroying previous notification, or this._source will be cleared */ this._ensureSource(); let icon = new St.Icon({ icon_name: iconName, icon_type: St.IconType.SYMBOLIC, icon_size: this._source.ICON_SIZE }); device._notification = new MessageTray.Notification(this._source, title, text, { icon: icon }); device._notification.setUrgency(urgency); device._notification.setTransient(true); device._notification.connect('destroy', function() { device._notification = null; }); this._source.notify(device._notification); }, _deviceAdded: function(client, device) { if (device._delegate) { // already seen, not adding again return; } let wrapperClass = this._dtypes[device.get_device_type()]; if (wrapperClass) { let wrapper = new wrapperClass(this._client, device, this._connections); wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) { // XXX: nm-applet has no special text depending on reason // but I'm not sure of this generic message this._notifyForDevice(device, 'network-error', _("Connection failed"), _("Activation of network connection failed"), MessageTray.Urgency.HIGH); })); wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) { this._syncSectionTitle(dev.category); })); wrapper._destroyId = wrapper.connect('destroy', function(wrapper) { wrapper.disconnect(wrapper._activationFailedId); wrapper.disconnect(wrapper._deviceStateChangedId); wrapper.disconnect(wrapper._destroyId); }); let section = this._devices[wrapper.category].section; let devices = this._devices[wrapper.category].devices; section.addMenuItem(wrapper.section, 1); section.addMenuItem(wrapper.statusItem, 1); devices.push(wrapper); this._syncSectionTitle(wrapper.category); } else log('Invalid network device type, is ' + device.get_device_type()); }, _deviceRemoved: function(client, device) { if (!device._delegate) { log('Removing a network device that was not added'); return; } let wrapper = device._delegate; wrapper.destroy(); let devices = this._devices[wrapper.category].devices; let pos = devices.indexOf(wrapper); devices.splice(pos, 1); this._syncSectionTitle(wrapper.category) }, _syncActiveConnections: function() { let closedConnections = [ ]; let newActiveConnections = this._client.get_active_connections() || [ ]; for (let i = 0; i < this._activeConnections.length; i++) { let a = this._activeConnections[i]; if (newActiveConnections.indexOf(a) == -1) // connection is removed closedConnections.push(a); } for (let i = 0; i < closedConnections.length; i++) { let active = closedConnections[i]; if (active._primaryDevice) { active._primaryDevice.setActiveConnection(null); active._primaryDevice = null; } if (active._inited) { active.disconnect(active._notifyStateId); //active.disconnect(active._notifyDefaultId); //active.disconnect(active._notifyDefault6Id); active._inited = false; } } this._activeConnections = newActiveConnections; this._mainConnection = null; let activating = null; let activated = null; let default_ip4 = null; let default_ip6 = null; for (let i = 0; i < this._activeConnections.length; i++) { let a = this._activeConnections[i]; if (!a._inited) { //a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon)); //a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon)); a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActivated)); a._inited = true; } if (!a._connection) { a._connection = this._settings.get_connection_by_path(a.connection); if (a._connection) { a._type = a._connection._type; a._section = this._ctypes[a._type]; if (a._errorLogged) { log('network applet: Found connection for active'); a._errorLogged = false; } } else { a._connection = null; a._type = null; a._section = null; if (!a._errorLogged) { a._errorLogged = true; log('network applet: Cannot find connection for active (or connection cannot be read)'); } } } if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED) { if (!default_ip4) { // We didn't find the default IPV4 device yet.. if (!default_ip6) { // We didn't find the default IPV6 device either so consider this one. activated = a; } else { // We already found the default IPV6 device, so only use this one if it's the default IPV4 device if (a['default']) { activated = a; } } } } if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) { activating = a; } if (a['default']) { default_ip4 = a; } if (a.default6) { default_ip6 = a; } if (!a._primaryDevice) { if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) { // find a good device to be considered primary a._primaryDevice = null; let devices = a.get_devices(); for (let j = 0; j < devices.length; j++) { let d = devices[j]; if (d._delegate) { a._primaryDevice = d._delegate; break; } } } else a._primaryDevice = this._devices.vpn.device if (a._primaryDevice) a._primaryDevice.setActiveConnection(a); if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED && a._primaryDevice && a._primaryDevice._notification) { a._primaryDevice._notification.destroy(); a._primaryDevice._notification = null; } } } this._mainConnection = activated || activating || default_ip4 || default_ip6 || null; }, _notifyActivated: function(activeConnection) { if (activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED && activeConnection._primaryDevice && activeConnection._primaryDevice._notification) { activeConnection._primaryDevice._notification.destroy(); activeConnection._primaryDevice._notification = null; } this._updateIcon(); }, _readConnections: function() { let connections = this._settings.list_connections(); for (let i = 0; i < connections.length; i++) { let connection = connections[i]; if (connection._uuid) { // connection was already seen (for example because NetworkManager was restarted) continue; } connection._removedId = connection.connect('removed', Lang.bind(this, this._connectionRemoved)); connection._updatedId = connection.connect('updated', Lang.bind(this, this._updateConnection)); this._updateConnection(connection); this._connections.push(connection); } }, _newConnection: function(settings, connection) { if (connection._uuid) { // connection was already seen return; } connection._removedId = connection.connect('removed', Lang.bind(this, this._connectionRemoved)); connection._updatedId = connection.connect('updated', Lang.bind(this, this._updateConnection)); this._updateConnection(connection); this._connections.push(connection); this._updateIcon(); }, _connectionRemoved: function(connection) { let pos = this._connections.indexOf(connection); if (pos != -1) this._connections.splice(connection); let section = connection._section; if (section == NMConnectionCategory.VPN) { this._devices.vpn.device.removeConnection(connection); if (this._devices.vpn.device.empty) this._devices.vpn.section.actor.hide(); } else if (section != NMConnectionCategory.INVALID) { let devices = this._devices[section].devices; for (let i = 0; i < devices.length; i++) devices[i].removeConnection(connection); } connection._uuid = null; connection.disconnect(connection._removedId); connection.disconnect(connection._updatedId); }, _updateConnection: function(connection) { let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME); connection._type = connectionSettings.type; connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID; connection._name = connectionSettings.id; connection._uuid = connectionSettings.uuid; connection._timestamp = connectionSettings.timestamp; let section = connection._section; if (connection._section == NMConnectionCategory.INVALID) return; if (section == NMConnectionCategory.VPN) { this._devices.vpn.device.checkConnection(connection); this._devices.vpn.section.actor.show(); } else { let devices = this._devices[section].devices; for (let i = 0; i < devices.length; i++) { devices[i].checkConnection(connection); } } }, _hideDevices: function() { this._devicesHidden = true; for (let category in this._devices) this._devices[category].section.actor.hide(); }, _showNormal: function() { if (!this._devicesHidden) // nothing to do return; this._devicesHidden = false; this._statusSection.actor.hide(); this._syncSectionTitle('wired'); this._syncSectionTitle('wireless'); this._syncSectionTitle('wwan'); if (!this._devices.vpn.device.empty) this._devices.vpn.section.actor.show(); }, _syncNMState: function() { if (!this._client.manager_running) { log('NetworkManager is not running, hiding...'); this.menu.close(); this.actor.hide(); return; } else this.actor.show(); if (!this._client.networking_enabled) { this._setIcon('network-offline'); this._hideDevices(); this._statusItem.label.text = _("Networking is disabled"); this._statusSection.actor.show(); return; } this._showNormal(); this._updateIcon(); }, _updateIcon: function() { try { this._updateFrequencySeconds = DEFAULT_PERIODIC_UPDATE_FREQUENCY_SECONDS; this._syncActiveConnections(); let mc = this._mainConnection; let hasApIcon = false; let hasMobileIcon = false; if (!mc) { this._setIcon('network-offline'); this.set_applet_tooltip(_("No connection")); } else if (mc.state == NetworkManager.ActiveConnectionState.ACTIVATING) { this._updateFrequencySeconds = FAST_PERIODIC_UPDATE_FREQUENCY_SECONDS; switch (mc._section) { case NMConnectionCategory.WWAN: this._setIcon('network-cellular-acquiring'); this.set_applet_tooltip(_("Connecting to the cellular network...")); break; case NMConnectionCategory.WIRELESS: this._setIcon('network-wireless-acquiring'); this.set_applet_tooltip(_("Connecting to the wireless network...")); break; case NMConnectionCategory.WIRED: this._setIcon('network-wired-acquiring'); this.set_applet_tooltip(_("Connecting to the wired network...")); break; case NMConnectionCategory.VPN: this._setIcon('network-vpn-acquiring'); this.set_applet_tooltip(_("Connecting to the VPN...")); break; default: // fallback to a generic connected icon // (it could be a private connection of some other user) this._setIcon('network-wired-acquiring'); this.set_applet_tooltip(_("Connecting to the network...")); } } else { let dev; switch (mc._section) { case NMConnectionCategory.WIRELESS: dev = mc._primaryDevice; if (dev) { let ap = dev.device.active_access_point; let mode = dev.device.mode; if (!ap) { if (mode != NM80211Mode.ADHOC) { log('An active wireless connection, in infrastructure mode, involves no access point?'); break; } this._setIcon('network-wireless-connected'); this.set_applet_tooltip(_("Connected to the wireless network")); } else { this._setIcon('network-wireless-signal-' + signalToIcon(ap.strength)); this.set_applet_tooltip(_("Wireless connection") + ": " + ap.get_ssid() + " ("+ ap.strength +"%)"); hasApIcon = true; } break; } else { log('Active connection with no primary device?'); break; } case NMConnectionCategory.WIRED: this._setIcon('network-wired'); this.set_applet_tooltip(_("Connected to the wired network")); break; case NMConnectionCategory.WWAN: dev = mc._primaryDevice; if (!dev) { log('Active connection with no primary device?'); break; } if (!dev.mobileDevice) { // this can happen for bluetooth in PAN mode this._setIcon('network-cellular-connected'); this.set_applet_tooltip(_("Connected to the cellular network")); break; } this._setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality)); this.set_applet_tooltip(_("Connected to the cellular network")); hasMobileIcon = true; break; case NMConnectionCategory.VPN: this._setIcon('network-vpn'); this.set_applet_tooltip(_("Connected to the VPN")); break; default: // fallback to a generic connected icon // (it could be a private connection of some other user) this._setIcon('network-wired'); this.set_applet_tooltip(_("Connected to the network")); break; } } } catch (e) { global.logError(e); } }, _periodicUpdateIcon: function() { this._updateIcon(); this._updateFrequencySeconds = Math.max(2, this._updateFrequencySeconds); this._periodicTimeoutId = Mainloop.timeout_add_seconds(this._updateFrequencySeconds, Lang.bind(this, this._periodicUpdateIcon)); }, on_applet_removed_from_panel: function() { Main.systrayManager.unregisterRole("network", this.metadata.uuid); Main.systrayManager.unregisterRole("nm-applet", this.metadata.uuid); if (this._periodicTimeoutId){ Mainloop.source_remove(this._periodicTimeoutId); } }, }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(metadata, orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/menu@cinnamon.org/0000775000175000017500000000000012625666430024073 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json0000664000175000017500000000377612625666430030101 0ustar fabiofabio{ "overlay-key" : { "type" : "keybinding", "description" : "Keyboard shortcut to open and close the menu", "default" : "Super_L::Super_R" }, "menu-icon-custom" : { "type" : "checkbox", "default" : false, "description" : "Use a custom icon", "tooltip" : "Unchecking this allows the theme to set the icon" }, "menu-icon" : { "type" : "iconfilechooser", "default" : "/usr/share/cinnamon/theme/menu-symbolic.svg", "description" : "Icon", "tooltip" : "Select an icon to show in the panel.", "dependency" : "menu-icon-custom", "indent" : "true" }, "menu-label" : { "type" : "entry", "default" : "Menu", "description" : "Text", "tooltip" : "Enter custom text to show in the panel." }, "show-places" : { "type" : "checkbox", "default" : true, "description": "Show bookmarks and places", "tooltip": "Choose whether or not to show bookmarks and places in the menu." }, "enable-autoscroll" : { "type" : "checkbox", "default" : true, "description": "Enable autoscrolling in application list", "tooltip": "Choose whether or not to enable smooth autoscrolling in the application list." }, "search-filesystem" : { "type" : "checkbox", "default" : false, "description": "Enable filesystem path entry in search box", "tooltip": "Allows path entry in the menu search box." }, "activate-on-hover" : { "type" : "checkbox", "default" : false, "description": "Open the menu when I move my mouse over it", "tooltip": "Enable opening the menu when the mouse enters the applet" }, "hover-delay" : { "type": "spinbutton", "default" : 0, "min" : 0, "max" : 1000, "step" : 50, "units" : "milliseconds", "description" : "Menu hover delay:", "tooltip" : "Delay between switching categories" }, "menu-editor-button" : { "type" : "button", "description" : "Open the menu editor", "callback" : "_launch_editor", "tooltip" : "Press this button to customize your menu entries." } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/menu@cinnamon.org/metadata.json0000664000175000017500000000021512625666430026544 0ustar fabiofabio{ "uuid": "menu@cinnamon.org", "name": "Menu", "description": "Main Cinnamon menu", "icon": "applications-other", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js0000775000175000017500000032632012625666430025727 0ustar fabiofabioconst Applet = imports.ui.applet; const Mainloop = imports.mainloop; const CMenu = imports.gi.CMenu; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Clutter = imports.gi.Clutter; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const AppFavorites = imports.ui.appFavorites; const Gtk = imports.gi.Gtk; const Atk = imports.gi.Atk; const Gio = imports.gi.Gio; const Signals = imports.signals; const GnomeSession = imports.misc.gnomeSession; const ScreenSaver = imports.misc.screenSaver; const FileUtils = imports.misc.fileUtils; const Util = imports.misc.util; const Tweener = imports.ui.tweener; const DND = imports.ui.dnd; const Meta = imports.gi.Meta; const DocInfo = imports.misc.docInfo; const GLib = imports.gi.GLib; const Settings = imports.ui.settings; const Pango = imports.gi.Pango; const SearchProviderManager = imports.ui.searchProviderManager; const ICON_SIZE = 16; const MAX_FAV_ICON_SIZE = 32; const CATEGORY_ICON_SIZE = 22; const APPLICATION_ICON_SIZE = 22; const MAX_RECENT_FILES = 20; const INITIAL_BUTTON_LOAD = 30; const MAX_BUTTON_WIDTH = "max-width: 20em;"; const USER_DESKTOP_PATH = FileUtils.getUserDesktopDir(); const PRIVACY_SCHEMA = "org.cinnamon.desktop.privacy"; const REMEMBER_RECENT_KEY = "remember-recent-files"; let appsys = Cinnamon.AppSystem.get_default(); /* VisibleChildIterator takes a container (boxlayout, etc.) * and creates an array of its visible children and their index * positions. We can then work thru that list without * mucking about with positions and math, just give a * child, and it'll give you the next or previous, or first or * last child in the list. * * We could have this object regenerate off a signal * every time the visibles have changed in our applicationBox, * but we really only need it when we start keyboard * navigating, so increase speed, we reload only when we * want to use it. */ function VisibleChildIterator(container) { this._init(container); } VisibleChildIterator.prototype = { _init: function(container) { this.container = container; this.reloadVisible(); }, reloadVisible: function() { this.array = this.container.get_focus_chain() .filter(x => !(x._delegate instanceof PopupMenu.PopupSeparatorMenuItem)); }, getNextVisible: function(curChild) { return this.getVisibleItem(this.array.indexOf(curChild) + 1); }, getPrevVisible: function(curChild) { return this.getVisibleItem(this.array.indexOf(curChild) - 1); }, getFirstVisible: function() { return this.array[0]; }, getLastVisible: function() { return this.array[this.array.length - 1]; }, getVisibleIndex: function(curChild) { return this.array.indexOf(curChild); }, getVisibleItem: function(index) { let len = this.array.length; index = ((index % len) + len) % len; return this.array[index]; }, getNumVisibleChildren: function() { return this.array.length; }, getAbsoluteIndexOfChild: function(child) { return this.container.get_children().indexOf(child); } }; function ApplicationContextMenuItem(appButton, label, action) { this._init(appButton, label, action); } ApplicationContextMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function (appButton, label, action) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {focusOnHover: false}); this._appButton = appButton; this._action = action; this.label = new St.Label({ text: label }); this.addActor(this.label); }, activate: function (event) { switch (this._action){ case "add_to_panel": if (!Main.AppletManager.get_role_provider_exists(Main.AppletManager.Roles.PANEL_LAUNCHER)) { let new_applet_id = global.settings.get_int("next-applet-id"); global.settings.set_int("next-applet-id", (new_applet_id + 1)); let enabled_applets = global.settings.get_strv("enabled-applets"); enabled_applets.push("panel1:right:0:panel-launchers@cinnamon.org:" + new_applet_id); global.settings.set_strv("enabled-applets", enabled_applets); } let launcherApplet = Main.AppletManager.get_role_provider(Main.AppletManager.Roles.PANEL_LAUNCHER); launcherApplet.acceptNewLauncher(this._appButton.app.get_id()); this._appButton.toggleMenu(); break; case "add_to_desktop": let file = Gio.file_new_for_path(this._appButton.app.get_app_info().get_filename()); let destFile = Gio.file_new_for_path(USER_DESKTOP_PATH+"/"+this._appButton.app.get_id()); try{ file.copy(destFile, 0, null, function(){}); // Need to find a way to do that using the Gio library, but modifying the access::can-execute attribute on the file object seems unsupported Util.spawnCommandLine("chmod +x \""+USER_DESKTOP_PATH+"/"+this._appButton.app.get_id()+"\""); }catch(e){ global.log(e); } this._appButton.toggleMenu(); break; case "add_to_favorites": AppFavorites.getAppFavorites().addFavorite(this._appButton.app.get_id()); this._appButton.toggleMenu(); break; case "remove_from_favorites": AppFavorites.getAppFavorites().removeFavorite(this._appButton.app.get_id()); this._appButton.toggleMenu(); break; case "uninstall": Util.spawnCommandLine("gksu -m '" + _("Please provide your password to uninstall this application") + "' /usr/bin/cinnamon-remove-application '" + this._appButton.app.get_app_info().get_filename() + "'"); this._appButton.appsMenuButton.menu.close(); break; } return false; } }; function GenericApplicationButton(appsMenuButton, app) { this._init(appsMenuButton, app); } GenericApplicationButton.prototype = { __proto__: PopupMenu.PopupSubMenuMenuItem.prototype, _init: function(appsMenuButton, app, withMenu) { this.app = app; this.appsMenuButton = appsMenuButton; PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.withMenu = withMenu; if (this.withMenu){ this.menu = new PopupMenu.PopupSubMenu(this.actor); this.menu.actor.set_style_class_name('menu-context-menu'); this.menu.connect('open-state-changed', Lang.bind(this, this._subMenuOpenStateChanged)); } }, highlight: function() { this.actor.add_style_pseudo_class('highlighted'); }, unhighlight: function() { var app_key = this.app.get_id(); if (app_key == null) { app_key = this.app.get_name() + ":" + this.app.get_description(); } this.appsMenuButton._knownApps.push(app_key); this.actor.remove_style_pseudo_class('highlighted'); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button()==1){ this.activate(event); } if (event.get_button()==3){ if (this.withMenu && !this.menu.isOpen) this.appsMenuButton.closeContextMenus(this.app, true); this.toggleMenu(); } return true; }, activate: function(event) { this.unhighlight(); this.app.open_new_window(-1); this.appsMenuButton.menu.close(); }, closeMenu: function() { if (this.withMenu) this.menu.close(); }, toggleMenu: function() { if (!this.withMenu) return; if (!this.menu.isOpen){ let children = this.menu.box.get_children(); for (var i in children) { this.menu.box.remove_actor(children[i]); } let menuItem; menuItem = new ApplicationContextMenuItem(this, _("Add to panel"), "add_to_panel"); this.menu.addMenuItem(menuItem); if (USER_DESKTOP_PATH){ menuItem = new ApplicationContextMenuItem(this, _("Add to desktop"), "add_to_desktop"); this.menu.addMenuItem(menuItem); } if (AppFavorites.getAppFavorites().isFavorite(this.app.get_id())){ menuItem = new ApplicationContextMenuItem(this, _("Remove from favorites"), "remove_from_favorites"); this.menu.addMenuItem(menuItem); }else{ menuItem = new ApplicationContextMenuItem(this, _("Add to favorites"), "add_to_favorites"); this.menu.addMenuItem(menuItem); } if (this.appsMenuButton._canUninstallApps) { menuItem = new ApplicationContextMenuItem(this, _("Uninstall"), "uninstall"); this.menu.addMenuItem(menuItem); } } this.menu.toggle(); }, _subMenuOpenStateChanged: function() { if (this.menu.isOpen) this.appsMenuButton._scrollToButton(this.menu); } } function TransientButton(appsMenuButton, pathOrCommand) { this._init(appsMenuButton, pathOrCommand); } TransientButton.prototype = { __proto__: PopupMenu.PopupSubMenuMenuItem.prototype, _init: function(appsMenuButton, pathOrCommand) { let displayPath = pathOrCommand; if (pathOrCommand.charAt(0) == '~') { pathOrCommand = pathOrCommand.slice(1); pathOrCommand = GLib.get_home_dir() + pathOrCommand; } this.isPath = pathOrCommand.substr(pathOrCommand.length - 1) == '/'; if (this.isPath) { this.path = pathOrCommand; } else { let n = pathOrCommand.lastIndexOf('/'); if (n != 1) { this.path = pathOrCommand.substr(0, n); } } this.pathOrCommand = pathOrCommand; this.appsMenuButton = appsMenuButton; PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); // We need this fake app to help appEnterEvent/appLeaveEvent // work with our search result. this.app = { get_app_info: { get_filename: function() { return pathOrCommand; } }, get_id: function() { return -1; }, get_description: function() { return this.pathOrCommand; }, get_name: function() { return ''; } }; let iconBox = new St.Bin(); this.file = Gio.file_new_for_path(this.pathOrCommand); try { this.handler = this.file.query_default_handler(null); let icon_uri = this.file.get_uri(); let fileInfo = this.file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null); let contentType = Gio.content_type_guess(this.pathOrCommand, null); let themedIcon = Gio.content_type_get_icon(contentType[0]); this.icon = new St.Icon({gicon: themedIcon, icon_size: APPLICATION_ICON_SIZE, icon_type: St.IconType.FULLCOLOR }); this.actor.set_style_class_name('menu-application-button'); } catch (e) { this.handler = null; let iconName = this.isPath ? 'folder' : 'unknown'; this.icon = new St.Icon({icon_name: iconName, icon_size: APPLICATION_ICON_SIZE, icon_type: St.IconType.FULLCOLOR,}); // @todo Would be nice to indicate we don't have a handler for this file. this.actor.set_style_class_name('menu-application-button'); } this.addActor(this.icon); this.label = new St.Label({ text: displayPath, style_class: 'menu-application-button-label' }); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.label.set_style(MAX_BUTTON_WIDTH); this.addActor(this.label); this.isDraggableApp = false; }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button()==1){ this.activate(event); } return true; }, activate: function(event) { if (this.handler != null) { this.handler.launch([this.file], null) } else { // Try anyway, even though we probably shouldn't. try { Util.spawn(['gvfs-open', this.file.get_uri()]) } catch (e) { global.logError("No handler available to open " + this.file.get_uri()); } } this.appsMenuButton.menu.close(); } } function ApplicationButton(appsMenuButton, app) { this._init(appsMenuButton, app); } ApplicationButton.prototype = { __proto__: GenericApplicationButton.prototype, _init: function(appsMenuButton, app) { GenericApplicationButton.prototype._init.call(this, appsMenuButton, app, true); this.category = new Array(); this.actor.set_style_class_name('menu-application-button'); this.icon = this.app.create_icon_texture(APPLICATION_ICON_SIZE) this.addActor(this.icon); this.name = this.app.get_name(); this.label = new St.Label({ text: this.name, style_class: 'menu-application-button-label' }); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.label.set_style(MAX_BUTTON_WIDTH); this.addActor(this.label); this._draggable = DND.makeDraggable(this.actor); this.isDraggableApp = true; this.actor.label_actor = this.label; this.icon.realize(); this.label.realize(); }, get_app_id: function() { return this.app.get_id(); }, getDragActor: function() { let favorites = AppFavorites.getAppFavorites().getFavorites(); let nbFavorites = favorites.length; let monitorHeight = Main.layoutManager.primaryMonitor.height; let real_size = (0.7 * monitorHeight) / nbFavorites; let icon_size = 0.6 * real_size / global.ui_scale; if (icon_size > MAX_FAV_ICON_SIZE) icon_size = MAX_FAV_ICON_SIZE; return this.app.create_icon_texture(icon_size); }, // Returns the original actor that should align with the actor // we show as the item is being dragged. getDragActorSource: function() { return this.actor; } }; function SearchProviderResultButton(appsMenuButton, provider, result) { this._init(appsMenuButton, provider, result); } SearchProviderResultButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(appsMenuButton, provider, result) { this.provider = provider; this.result = result; this.appsMenuButton = appsMenuButton; PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.actor.set_style_class_name('menu-application-button'); // We need this fake app to help appEnterEvent/appLeaveEvent // work with our search result. this.app = { get_app_info: { get_filename: function() { return result.id; } }, get_id: function() { return -1; }, get_description: function() { return result.description; }, get_name: function() { return result.label; } }; this.icon = null; if (result.icon){ this.icon = result.icon; }else if (result.icon_app){ this.icon = result.icon_app.create_icon_texture(APPLICATION_ICON_SIZE); }else if (result.icon_filename){ this.icon = new St.Icon({gicon: new Gio.FileIcon({file: Gio.file_new_for_path(result.icon_filename)}), icon_size: APPLICATION_ICON_SIZE}); } if (this.icon){ this.addActor(this.icon); } this.label = new St.Label({ text: result.label, style_class: 'menu-application-button-label' }); this.addActor(this.label); this.isDraggableApp = false; if (this.icon) { this.icon.realize(); } this.label.realize(); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button() == 1){ this.activate(event); } return true; }, activate: function(event) { try{ this.provider.on_result_selected(this.result); this.appsMenuButton.menu.close(); } catch(e) { global.logError(e); } } } function PlaceButton(appsMenuButton, place, button_name) { this._init(appsMenuButton, place, button_name); } PlaceButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(appsMenuButton, place, button_name) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.appsMenuButton = appsMenuButton; this.place = place; this.button_name = button_name; this.actor.set_style_class_name('menu-application-button'); this.actor._delegate = this; this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.label.set_style(MAX_BUTTON_WIDTH); this.icon = place.iconFactory(APPLICATION_ICON_SIZE); if (!this.icon) this.icon = new St.Icon({icon_name: "folder", icon_size: APPLICATION_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); if (this.icon) this.addActor(this.icon); this.addActor(this.label); this.icon.realize(); this.label.realize(); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button()==1){ this.place.launch(); this.appsMenuButton.menu.close(); } }, activate: function(event) { this.place.launch(); this.appsMenuButton.menu.close(); } }; function RecentContextMenuItem(recentButton, label, is_default, callback) { this._init(recentButton, label, is_default, callback); } RecentContextMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function (recentButton, label, is_default, callback) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {focusOnHover: false}); this._recentButton = recentButton; this._callback = callback; this.label = new St.Label({ text: label }); this.addActor(this.label); if (is_default) this.label.style = "font-weight: bold;"; }, activate: function (event) { this._callback() return false; } }; function RecentButton(appsMenuButton, file) { this._init(appsMenuButton, file); } RecentButton.prototype = { __proto__: PopupMenu.PopupSubMenuMenuItem.prototype, _init: function(appsMenuButton, file) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.file = file; this.appsMenuButton = appsMenuButton; this.button_name = this.file.name; this.actor.set_style_class_name('menu-application-button'); this.actor._delegate = this; this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.label.set_style(MAX_BUTTON_WIDTH); this.icon = file.createIcon(APPLICATION_ICON_SIZE); this.addActor(this.icon); this.addActor(this.label); this.icon.realize(); this.label.realize(); this.menu = new PopupMenu.PopupSubMenu(this.actor); this.menu.actor.set_style_class_name('menu-context-menu'); this.menu.connect('open-state-changed', Lang.bind(this, this._subMenuOpenStateChanged)); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button()==1){ this.file.launch(); this.appsMenuButton.menu.close(); } if (event.get_button()==3){ if (!this.menu.isOpen) this.appsMenuButton.closeContextMenus(this, true); this.toggleMenu(); } return true; }, activate: function(event) { this.file.launch(); this.appsMenuButton.menu.close(); }, closeMenu: function() { this.menu.close(); }, hasLocalPath: function(file) { return file.is_native() || file.get_path() != null; }, toggleMenu: function() { if (!this.menu.isOpen){ let children = this.menu.box.get_children(); for (var i in children) { this.menu.box.remove_actor(children[i]); } let menuItem; menuItem = new PopupMenu.PopupMenuItem(_("Open with"), { reactive: false }); menuItem.actor.style = "font-weight: bold"; this.menu.addMenuItem(menuItem); let file = Gio.File.new_for_uri(this.file.uri); let default_info = Gio.AppInfo.get_default_for_type(this.file.mimeType, !this.hasLocalPath(file)); if (default_info) { menuItem = new RecentContextMenuItem(this, default_info.get_display_name(), false, Lang.bind(this, function() { default_info.launch([file], null, null); this.toggleMenu(); this.appsMenuButton.menu.close(); })); this.menu.addMenuItem(menuItem); } let infos = Gio.AppInfo.get_all_for_type(this.file.mimeType) for (let i = 0; i < infos.length; i++) { let info = infos[i]; file = Gio.File.new_for_uri(this.file.uri); if (!this.hasLocalPath(file) && !info.supports_uris()) continue; if (info.equal(default_info)) continue; menuItem = new RecentContextMenuItem(this, info.get_display_name(), false, Lang.bind(this, function() { info.launch([file], null, null); this.toggleMenu(); this.appsMenuButton.menu.close(); })); this.menu.addMenuItem(menuItem); } if (GLib.find_program_in_path ("nemo-open-with") != null) { menuItem = new RecentContextMenuItem(this, _("Other application..."), false, Lang.bind(this, function() { Util.spawnCommandLine("nemo-open-with " + this.file.uri); this.toggleMenu(); this.appsMenuButton.menu.close(); })); this.menu.addMenuItem(menuItem); } } this.menu.toggle(); }, _subMenuOpenStateChanged: function() { if (this.menu.isOpen) this.appsMenuButton._scrollToButton(this.menu); } }; function GenericButton(label, icon, reactive, callback) { this._init(label, icon, reactive, callback); } GenericButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(label, icon, reactive, callback) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.actor.set_style_class_name('menu-application-button'); this.actor._delegate = this; this.button_name = ""; this.label = new St.Label({ text: label, style_class: 'menu-application-button-label' }); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.label.set_style(MAX_BUTTON_WIDTH); if (icon != null) { let icon_actor = new St.Icon({ icon_name: icon, icon_type: St.IconType.FULLCOLOR, icon_size: APPLICATION_ICON_SIZE}); this.addActor(icon_actor); } this.addActor(this.label); this.label.realize(); this.actor.reactive = reactive; this.callback = callback; this.menu = new PopupMenu.PopupSubMenu(this.actor); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button() == 1) { this.callback(); } } } function RecentClearButton(appsMenuButton) { this._init(appsMenuButton); } RecentClearButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(appsMenuButton) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.appsMenuButton = appsMenuButton; this.actor.set_style_class_name('menu-application-button'); this.button_name = _("Clear list"); this.actor._delegate = this; this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); this.icon = new St.Icon({ icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC, icon_size: APPLICATION_ICON_SIZE }); this.addActor(this.icon); this.addActor(this.label); this.menu = new PopupMenu.PopupSubMenu(this.actor); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button()==1){ this.appsMenuButton.menu.close(); let GtkRecent = new Gtk.RecentManager(); GtkRecent.purge_items(); } } }; function CategoryButton(app) { this._init(app); } CategoryButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(category) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.actor.set_style_class_name('menu-category-button'); var label; let icon = null; if (category) { icon = category.get_icon(); if (icon && icon.get_names) this.icon_name = icon.get_names().toString(); else this.icon_name = ""; label = category.get_name(); } else label = _("All Applications"); this.actor._delegate = this; this.label = new St.Label({ text: label, style_class: 'menu-category-button-label' }); if (category && this.icon_name) { this.icon = St.TextureCache.get_default().load_gicon(null, icon, CATEGORY_ICON_SIZE); if (this.icon) { this.addActor(this.icon); this.icon.realize(); } } this.actor.accessible_role = Atk.Role.LIST_ITEM; this.addActor(this.label); this.label.realize(); } }; function PlaceCategoryButton(app) { this._init(app); } PlaceCategoryButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(category) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.actor.set_style_class_name('menu-category-button'); this.actor._delegate = this; this.label = new St.Label({ text: _("Places"), style_class: 'menu-category-button-label' }); this.icon = new St.Icon({icon_name: "folder", icon_size: CATEGORY_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); this.addActor(this.icon); this.icon.realize(); this.addActor(this.label); this.label.realize(); } }; function RecentCategoryButton(app) { this._init(app); } RecentCategoryButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(category) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.actor.set_style_class_name('menu-category-button'); this.actor._delegate = this; this.label = new St.Label({ text: _("Recent Files"), style_class: 'menu-category-button-label' }); this.icon = new St.Icon({icon_name: "folder-recent", icon_size: CATEGORY_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); this.addActor(this.icon); this.icon.realize() this.addActor(this.label); this.label.realize(); } }; function FavoritesButton(appsMenuButton, app, nbFavorites) { this._init(appsMenuButton, app, nbFavorites); } FavoritesButton.prototype = { __proto__: GenericApplicationButton.prototype, _init: function(appsMenuButton, app, nbFavorites) { GenericApplicationButton.prototype._init.call(this, appsMenuButton, app); let monitorHeight = Main.layoutManager.primaryMonitor.height; let real_size = (0.7 * monitorHeight) / nbFavorites; let icon_size = 0.6 * real_size / global.ui_scale; if (icon_size > MAX_FAV_ICON_SIZE) icon_size = MAX_FAV_ICON_SIZE; this.actor.style = "padding-top: "+(icon_size / 3)+"px;padding-bottom: "+(icon_size / 3)+"px; margin:auto;" this.actor.add_style_class_name('menu-favorites-button'); let icon = app.create_icon_texture(icon_size); this.addActor(icon); icon.realize() this._draggable = DND.makeDraggable(this.actor); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); this.isDraggableApp = true; }, _onDragEnd: function() { this.actor.get_parent()._delegate._clearDragPlaceholder() }, get_app_id: function() { return this.app.get_id(); }, getDragActor: function() { return new Clutter.Clone({ source: this.actor }); }, // Returns the original actor that should align with the actor // we show as the item is being dragged. getDragActorSource: function() { return this.actor; } }; function SystemButton(appsMenuButton, icon, nbFavorites, name, desc) { this._init(appsMenuButton, icon, nbFavorites, name, desc); } SystemButton.prototype = { __proto__: PopupMenu.PopupSubMenuMenuItem.prototype, _init: function(appsMenuButton, icon, nbFavorites, name, desc) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.name = name; this.desc = desc; let monitorHeight = Main.layoutManager.primaryMonitor.height; let real_size = (0.7 * monitorHeight) / nbFavorites; let icon_size = 0.6 * real_size / global.ui_scale; if (icon_size > MAX_FAV_ICON_SIZE) icon_size = MAX_FAV_ICON_SIZE; this.actor.style = "padding-top: "+(icon_size / 3)+"px;padding-bottom: "+(icon_size / 3)+"px; margin:auto;" this.actor.add_style_class_name('menu-favorites-button'); let iconObj = new St.Icon({icon_name: icon, icon_size: icon_size, icon_type: St.IconType.FULLCOLOR}); this.addActor(iconObj); iconObj.realize() }, _onButtonReleaseEvent: function(actor, event) { if (event.get_button() == 1) { this.activate(); } } }; function CategoriesApplicationsBox() { this._init(); } CategoriesApplicationsBox.prototype = { _init: function() { this.actor = new St.BoxLayout(); this.actor._delegate = this; }, acceptDrop : function(source, actor, x, y, time) { if (source instanceof FavoritesButton){ source.actor.destroy(); actor.destroy(); AppFavorites.getAppFavorites().removeFavorite(source.app.get_id()); return true; } return false; } } function FavoritesBox() { this._init(); } FavoritesBox.prototype = { _init: function() { this.actor = new St.BoxLayout({ vertical: true }); this.actor._delegate = this; this._dragPlaceholder = null; this._dragPlaceholderPos = -1; this._animatingPlaceholdersCount = 0; }, _clearDragPlaceholder: function() { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._dragPlaceholder = null; this._dragPlaceholderPos = -1; } }, handleDragOver : function(source, actor, x, y, time) { let app = source.app; let favorites = AppFavorites.getAppFavorites().getFavorites(); let numFavorites = favorites.length; let favPos = favorites.indexOf(app); let children = this.actor.get_children(); let numChildren = children.length; let boxHeight = this.actor.height; // Keep the placeholder out of the index calculation; assuming that // the remove target has the same size as "normal" items, we don't // need to do the same adjustment there. if (this._dragPlaceholder) { boxHeight -= this._dragPlaceholder.actor.height; numChildren--; } let pos = Math.round(y * numChildren / boxHeight); if (pos != this._dragPlaceholderPos && pos <= numFavorites) { if (this._animatingPlaceholdersCount > 0) { let appChildren = children.filter(function(actor) { return (actor._delegate instanceof FavoritesButton); }); this._dragPlaceholderPos = children.indexOf(appChildren[pos]); } else { this._dragPlaceholderPos = pos; } // Don't allow positioning before or after self if (favPos != -1 && (pos == favPos || pos == favPos + 1)) { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._animatingPlaceholdersCount++; this._dragPlaceholder.actor.connect('destroy', Lang.bind(this, function() { this._animatingPlaceholdersCount--; })); } this._dragPlaceholder = null; return DND.DragMotionResult.CONTINUE; } // If the placeholder already exists, we just move // it, but if we are adding it, expand its size in // an animation let fadeIn; if (this._dragPlaceholder) { this._dragPlaceholder.actor.destroy(); fadeIn = false; } else { fadeIn = true; } this._dragPlaceholder = new DND.GenericDragPlaceholderItem(); this._dragPlaceholder.child.set_width (source.actor.height); this._dragPlaceholder.child.set_height (source.actor.height); this.actor.insert_actor(this._dragPlaceholder.actor, this._dragPlaceholderPos); if (fadeIn) this._dragPlaceholder.animateIn(); } return DND.DragMotionResult.MOVE_DROP; }, // Draggable target interface acceptDrop : function(source, actor, x, y, time) { let app = source.app; let id = app.get_id(); let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); let srcIsFavorite = (id in favorites); let favPos = 0; let children = this.actor.get_children(); for (let i = 0; i < this._dragPlaceholderPos; i++) { if (this._dragPlaceholder && children[i] == this._dragPlaceholder.actor) continue; if (!(children[i]._delegate instanceof FavoritesButton)) continue; let childId = children[i]._delegate.app.get_id(); if (childId == id) continue; if (childId in favorites) favPos++; } Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function () { let appFavorites = AppFavorites.getAppFavorites(); if (srcIsFavorite) appFavorites.moveFavoriteToPos(id, favPos); else appFavorites.addFavoriteAtPos(id, favPos); return false; })); return true; } } function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this.initial_load_done = false; this.set_applet_tooltip(_("Menu")); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.settings = new Settings.AppletSettings(this, "menu@cinnamon.org", instance_id); this.settings.bindProperty(Settings.BindingDirection.IN, "show-places", "showPlaces", this._refreshBelowApps, null); this.settings.bindProperty(Settings.BindingDirection.IN, "activate-on-hover", "activateOnHover", this._updateActivateOnHover, null); this._updateActivateOnHover(); this.menu.actor.add_style_class_name('menu-background'); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); this.settings.bindProperty(Settings.BindingDirection.IN, "menu-icon-custom", "menuIconCustom", this._updateIconAndLabel, null); this.settings.bindProperty(Settings.BindingDirection.IN, "menu-icon", "menuIcon", this._updateIconAndLabel, null); this.settings.bindProperty(Settings.BindingDirection.IN, "menu-label", "menuLabel", this._updateIconAndLabel, null); this.settings.bindProperty(Settings.BindingDirection.IN, "overlay-key", "overlayKey", this._updateKeybinding, null); this._updateKeybinding(); Main.themeManager.connect("theme-set", Lang.bind(this, this._updateIconAndLabel)); this._updateIconAndLabel(); this._searchInactiveIcon = new St.Icon({ style_class: 'menu-search-entry-icon', icon_name: 'edit-find', icon_type: St.IconType.SYMBOLIC }); this._searchActiveIcon = new St.Icon({ style_class: 'menu-search-entry-icon', icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC }); this._searchIconClickedId = 0; this._applicationsButtons = new Array(); this._applicationsButtonFromApp = new Object(); this._favoritesButtons = new Array(); this._placesButtons = new Array(); this._transientButtons = new Array(); this._recentButtons = new Array(); this._categoryButtons = new Array(); this._searchProviderButtons = new Array(); this._selectedItemIndex = null; this._previousSelectedActor = null; this._previousVisibleIndex = null; this._previousTreeSelectedActor = null; this._activeContainer = null; this._activeActor = null; this._applicationsBoxWidth = 0; this.menuIsOpening = false; this._knownApps = new Array(); // Used to keep track of apps that are already installed, so we can highlight newly installed ones this._appsWereRefreshed = false; this._canUninstallApps = GLib.file_test("/usr/bin/cinnamon-remove-application", GLib.FileTest.EXISTS); this.RecentManager = new DocInfo.DocManager(); this.privacy_settings = new Gio.Settings( {schema_id: PRIVACY_SCHEMA} ); this._display(); appsys.connect('installed-changed', Lang.bind(this, this._refreshAll)); AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._refreshFavs)); this.settings.bindProperty(Settings.BindingDirection.IN, "hover-delay", "hover_delay_ms", this._update_hover_delay, null); this._update_hover_delay(); Main.placesManager.connect('places-updated', Lang.bind(this, this._refreshBelowApps)); this.RecentManager.connect('changed', Lang.bind(this, this._refreshRecent)); this.privacy_settings.connect("changed::" + REMEMBER_RECENT_KEY, Lang.bind(this, this._refreshRecent)); this._fileFolderAccessActive = false; this._pathCompleter = new Gio.FilenameCompleter(); this._pathCompleter.set_dirs_only(false); this.lastAcResults = new Array(); this.settings.bindProperty(Settings.BindingDirection.IN, "search-filesystem", "searchFilesystem", null, null); // We shouldn't need to call refreshAll() here... since we get a "icon-theme-changed" signal when CSD starts. // The reason we do is in case the Cinnamon icon theme is the same as the one specificed in GTK itself (in .config) // In that particular case we get no signal at all. this._refreshAll(); St.TextureCache.get_default().connect("icon-theme-changed", Lang.bind(this, this.onIconThemeChanged)); this._recalc_height(); }, _updateKeybinding: function() { Main.keybindingManager.addHotKey("overlay-key", this.overlayKey, Lang.bind(this, function() { if (!Main.overview.visible && !Main.expo.visible) this.menu.toggle_with_options(false); })); }, onIconThemeChanged: function() { this._refreshAll(); }, _refreshAll: function() { this._refreshApps(); this._refreshFavs(); this._refreshPlaces(); this._refreshRecent(); }, _refreshBelowApps: function() { this._refreshPlaces(); this._refreshRecent(); }, openMenu: function() { if (!this._applet_context_menu.isOpen) { this.menu.open(false); } }, _updateActivateOnHover: function() { if (this._openMenuId) { this.actor.disconnect(this._openMenuId); this._openMenuId = 0; } if (this.activateOnHover) { this._openMenuId = this.actor.connect('enter-event', Lang.bind(this, this.openMenu)); } }, _update_hover_delay: function() { this.hover_delay = this.hover_delay_ms / 1000 }, _recalc_height: function() { let scrollBoxHeight = (this.leftBox.get_allocation_box().y2-this.leftBox.get_allocation_box().y1) - (this.searchBox.get_allocation_box().y2-this.searchBox.get_allocation_box().y1); this.applicationsScrollBox.style = "height: "+scrollBoxHeight / global.ui_scale +"px;"; }, on_orientation_changed: function (orientation) { this.menu.destroy(); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this.menu.actor.add_style_class_name('menu-background'); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); this._display(); if (this.initial_load_done) this._refreshAll(); }, on_applet_added_to_panel: function () { this.initial_load_done = true; }, _launch_editor: function() { Util.spawnCommandLine("cinnamon-menu-editor"); }, on_applet_clicked: function(event) { this.menu.toggle_with_options(false); }, _onSourceKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { this.menu.toggle(); return true; } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { this.menu.close(); return true; } else if (symbol == Clutter.KEY_Down) { if (!this.menu.isOpen) this.menu.toggle(); this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); return true; } else return false; }, _onOpenStateChanged: function(menu, open) { if (open) { this.menuIsOpening = true; this.actor.add_style_pseudo_class('active'); global.stage.set_key_focus(this.searchEntry); this._selectedItemIndex = null; this._activeContainer = null; this._activeActor = null; let n = Math.min(this._applicationsButtons.length, INITIAL_BUTTON_LOAD); for (let i = 0; i < n; i++) { this._applicationsButtons[i].actor.show(); } this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; Mainloop.idle_add(Lang.bind(this, this._initial_cat_selection, n)); } else { this.actor.remove_style_pseudo_class('active'); if (this.searchActive) { this.resetSearch(); } this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(""); this._previousTreeSelectedActor = null; this._previousSelectedActor = null; this.closeContextMenus(null, false); this._clearAllSelections(false); this.destroyVectorBox(); } }, _initial_cat_selection: function (start_index) { let n = this._applicationsButtons.length; for (let i = start_index; i < n; i++) { this._applicationsButtons[i].actor.show(); } }, destroy: function() { this.actor._delegate = null; this.menu.destroy(); this.actor.destroy(); this.emit('destroy'); }, _set_default_menu_icon: function() { let path = global.datadir + "/theme/menu.svg"; if (GLib.file_test(path, GLib.FileTest.EXISTS)) { this.set_applet_icon_path(path); return; } path = global.datadir + "/theme/menu-symbolic.svg"; if (GLib.file_test(path, GLib.FileTest.EXISTS)) { this.set_applet_icon_symbolic_path(path); return; } /* If all else fails, this will yield no icon */ this.set_applet_icon_path(""); }, _updateIconAndLabel: function(){ try { if (this.menuIconCustom) { if (this.menuIcon == "") { this.set_applet_icon_name(""); } else if (GLib.path_is_absolute(this.menuIcon) && GLib.file_test(this.menuIcon, GLib.FileTest.EXISTS)) { if (this.menuIcon.search("-symbolic") != -1) this.set_applet_icon_symbolic_path(this.menuIcon); else this.set_applet_icon_path(this.menuIcon); } else if (Gtk.IconTheme.get_default().has_icon(this.menuIcon)) { if (this.menuIcon.search("-symbolic") != -1) this.set_applet_icon_symbolic_name(this.menuIcon); else this.set_applet_icon_name(this.menuIcon); } } else { this._set_default_menu_icon(); } } catch(e) { global.logWarning("Could not load icon file \""+this.menuIcon+"\" for menu button"); } if (this.menuIconCustom && this.menuIcon == "") { this._applet_icon_box.hide(); } else { this._applet_icon_box.show(); } if (this.menuLabel != "") this.set_applet_label(_(this.menuLabel)); else this.set_applet_label(""); }, _onMenuKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); let item_actor; let index = 0; this.appBoxIter.reloadVisible(); this.catBoxIter.reloadVisible(); this.favBoxIter.reloadVisible(); let keyCode = event.get_key_code(); let modifierState = Cinnamon.get_event_state(event); /* check for a keybinding and quit early, otherwise we get a double hit of the keybinding callback */ let action = global.display.get_keybinding_action(keyCode, modifierState); if (action == Meta.KeyBindingAction.CUSTOM) { return true; } index = this._selectedItemIndex; if (this._activeContainer === null && symbol == Clutter.KEY_Up) { this._activeContainer = this.applicationsBox; item_actor = this.appBoxIter.getLastVisible(); index = this.appBoxIter.getAbsoluteIndexOfChild(item_actor); this._scrollToButton(item_actor._delegate); } else if (this._activeContainer === null && symbol == Clutter.KEY_Down) { this._activeContainer = this.applicationsBox; item_actor = this.appBoxIter.getFirstVisible(); index = this.appBoxIter.getAbsoluteIndexOfChild(item_actor); this._scrollToButton(item_actor._delegate); } else if (this._activeContainer === null && symbol == Clutter.KEY_Left) { this._activeContainer = this.favoritesBox; item_actor = this.favBoxIter.getFirstVisible(); index = this.favBoxIter.getAbsoluteIndexOfChild(item_actor); } else if (symbol == Clutter.KEY_Up) { if (this._activeContainer != this.categoriesBox) { this._previousSelectedActor = this._activeContainer.get_child_at_index(index); item_actor = this._activeContainer._vis_iter.getPrevVisible(this._previousSelectedActor); this._previousVisibleIndex = this._activeContainer._vis_iter.getVisibleIndex(item_actor); index = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(item_actor); this._scrollToButton(item_actor._delegate); } else { this._previousTreeSelectedActor = this.categoriesBox.get_child_at_index(index); this._previousTreeSelectedActor._delegate.isHovered = false; item_actor = this.catBoxIter.getPrevVisible(this._activeActor) index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); } } else if (symbol == Clutter.KEY_Down) { if (this._activeContainer != this.categoriesBox) { this._previousSelectedActor = this._activeContainer.get_child_at_index(index); item_actor = this._activeContainer._vis_iter.getNextVisible(this._previousSelectedActor); this._previousVisibleIndex = this._activeContainer._vis_iter.getVisibleIndex(item_actor); index = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(item_actor); this._scrollToButton(item_actor._delegate); } else { this._previousTreeSelectedActor = this.categoriesBox.get_child_at_index(index); this._previousTreeSelectedActor._delegate.isHovered = false; item_actor = this.catBoxIter.getNextVisible(this._activeActor) index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); this._previousTreeSelectedActor._delegate.emit('leave-event'); } } else if (symbol == Clutter.KEY_Right && (this._activeContainer !== this.applicationsBox)) { if (this._activeContainer == this.categoriesBox) { if (this._previousVisibleIndex !== null) { item_actor = this.appBoxIter.getVisibleItem(this._previousVisibleIndex); } else { item_actor = this.appBoxIter.getFirstVisible(); } } else { item_actor = (this._previousTreeSelectedActor != null) ? this._previousTreeSelectedActor : this.catBoxIter.getFirstVisible(); index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); this._previousTreeSelectedActor = item_actor; } index = item_actor.get_parent()._vis_iter.getAbsoluteIndexOfChild(item_actor); } else if (symbol == Clutter.KEY_Left && this._activeContainer === this.applicationsBox && !this.searchActive) { this._previousSelectedActor = this.applicationsBox.get_child_at_index(index); item_actor = (this._previousTreeSelectedActor != null) ? this._previousTreeSelectedActor : this.catBoxIter.getFirstVisible(); index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); this._previousTreeSelectedActor = item_actor; } else if (symbol == Clutter.KEY_Left && this._activeContainer === this.categoriesBox && !this.searchActive) { this._previousSelectedActor = this.categoriesBox.get_child_at_index(index); item_actor = this.favBoxIter.getFirstVisible(); index = this.favBoxIter.getAbsoluteIndexOfChild(item_actor); } else if (this._activeContainer !== this.categoriesBox && (symbol == Clutter.KEY_Return || symbol == Clutter.KP_Enter)) { item_actor = this._activeContainer.get_child_at_index(this._selectedItemIndex); item_actor._delegate.activate(); return true; } else if (this.searchFilesystem && (this._fileFolderAccessActive || symbol == Clutter.slash)) { if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { if (this._run(this.searchEntry.get_text())) { this.menu.close(); } return true; } if (symbol == Clutter.Escape) { this.searchEntry.set_text(''); this._fileFolderAccessActive = false; } if (symbol == Clutter.slash) { // Need preload data before get completion. GFilenameCompleter load content of parent directory. // Parent directory for /usr/include/ is /usr/. So need to add fake name('a'). let text = this.searchEntry.get_text().concat('/a'); let prefix; if (text.lastIndexOf(' ') == -1) prefix = text; else prefix = text.substr(text.lastIndexOf(' ') + 1); this._getCompletion(prefix); return false; } if (symbol == Clutter.Tab) { let text = actor.get_text(); let prefix; if (text.lastIndexOf(' ') == -1) prefix = text; else prefix = text.substr(text.lastIndexOf(' ') + 1); let postfix = this._getCompletion(prefix); if (postfix != null && postfix.length > 0) { actor.insert_text(postfix, -1); actor.set_cursor_position(text.length + postfix.length); if (postfix[postfix.length - 1] == '/') this._getCompletion(text + postfix + 'a'); } return true; } return false; } else { return false; } this._selectedItemIndex = index; if (!item_actor || item_actor === this.searchEntry) { return false; } item_actor._delegate.emit('enter-event'); return true; }, _addEnterEvent: function(button, callback) { let _callback = Lang.bind(this, function() { let parent = button.actor.get_parent(); if (this._activeContainer === this.categoriesBox && parent !== this._activeContainer) { this._previousTreeSelectedActor = this._activeActor; this._previousSelectedActor = null; } if (this._previousTreeSelectedActor && this._activeContainer !== this.categoriesBox && parent !== this._activeContainer && button !== this._previousTreeSelectedActor && !this.searchActive) { this._previousTreeSelectedActor.style_class = "menu-category-button"; } if (parent != this._activeContainer) { parent._vis_iter.reloadVisible(); } let _maybePreviousActor = this._activeActor; if (_maybePreviousActor && this._activeContainer !== this.categoriesBox) { this._previousSelectedActor = _maybePreviousActor; this._clearPrevSelection(); } if (parent === this.categoriesBox && !this.searchActive) { this._previousSelectedActor = _maybePreviousActor; this._clearPrevCatSelection(); } this._activeContainer = parent; this._activeActor = button.actor; this._selectedItemIndex = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(this._activeActor); callback(); }); button.connect('enter-event', _callback); button.actor.connect('enter-event', _callback); }, _clearPrevSelection: function(actor) { if (this._previousSelectedActor && this._previousSelectedActor != actor) { if (this._previousSelectedActor._delegate instanceof ApplicationButton || this._previousSelectedActor._delegate instanceof RecentButton || this._previousSelectedActor._delegate instanceof SearchProviderResultButton || this._previousSelectedActor._delegate instanceof PlaceButton || this._previousSelectedActor._delegate instanceof RecentClearButton) this._previousSelectedActor.style_class = "menu-application-button"; else if (this._previousSelectedActor._delegate instanceof FavoritesButton || this._previousSelectedActor._delegate instanceof SystemButton) this._previousSelectedActor.remove_style_pseudo_class("hover"); } }, _clearPrevCatSelection: function(actor) { if (this._previousTreeSelectedActor && this._previousTreeSelectedActor != actor) { this._previousTreeSelectedActor.style_class = "menu-category-button"; if (this._previousTreeSelectedActor._delegate) { this._previousTreeSelectedActor._delegate.emit('leave-event'); } if (actor !== undefined) { this._previousVisibleIndex = null; this._previousTreeSelectedActor = actor; } } else { this.categoriesBox.get_children().forEach(Lang.bind(this, function (child) { child.style_class = "menu-category-button"; })); } }, makeVectorBox: function(actor) { this.destroyVectorBox(actor); let [mx, my, mask] = global.get_pointer(); let [bx, by] = this.categoriesApplicationsBox.actor.get_transformed_position(); let [bw, bh] = this.categoriesApplicationsBox.actor.get_transformed_size(); let [aw, ah] = actor.get_transformed_size(); let [ax, ay] = actor.get_transformed_position(); let [appbox_x, appbox_y] = this.applicationsBox.get_transformed_position(); let right_x = appbox_x - bx; let xformed_mouse_x = mx-bx; let xformed_mouse_y = my-by; let w = Math.max(right_x-xformed_mouse_x, 0); let ulc_y = xformed_mouse_y + 0; let llc_y = xformed_mouse_y + 0; this.vectorBox = new St.Polygon({ debug: false, width: w, height: bh, ulc_x: 0, ulc_y: ulc_y, llc_x: 0, llc_y: llc_y, urc_x: w, urc_y: 0, lrc_x: w, lrc_y: bh }); this.categoriesApplicationsBox.actor.add_actor(this.vectorBox); this.vectorBox.set_position(xformed_mouse_x, 0); this.vectorBox.show(); this.vectorBox.set_reactive(true); this.vectorBox.raise_top(); this.vectorBox.connect("leave-event", Lang.bind(this, this.destroyVectorBox)); this.vectorBox.connect("motion-event", Lang.bind(this, this.maybeUpdateVectorBox)); this.actor_motion_id = actor.connect("motion-event", Lang.bind(this, this.maybeUpdateVectorBox)); this.current_motion_actor = actor; }, maybeUpdateVectorBox: function() { if (this.vector_update_loop) { Mainloop.source_remove(this.vector_update_loop); this.vector_update_loop = 0; } this.vector_update_loop = Mainloop.timeout_add(35, Lang.bind(this, this.updateVectorBox)); }, updateVectorBox: function(actor) { if (this.vectorBox) { let [mx, my, mask] = global.get_pointer(); let [bx, by] = this.categoriesApplicationsBox.actor.get_transformed_position(); let xformed_mouse_x = mx-bx; let [appbox_x, appbox_y] = this.applicationsBox.get_transformed_position(); let right_x = appbox_x - bx; if ((right_x-xformed_mouse_x) > 0) { this.vectorBox.width = Math.max(right_x-xformed_mouse_x, 0); this.vectorBox.set_position(xformed_mouse_x, 0); this.vectorBox.urc_x = this.vectorBox.width; this.vectorBox.lrc_x = this.vectorBox.width; this.vectorBox.queue_repaint(); } else { this.destroyVectorBox(actor); } } this.vector_update_loop = 0; return false; }, destroyVectorBox: function(actor) { if (this.vectorBox != null) { this.vectorBox.destroy(); this.vectorBox = null; } if (this.actor_motion_id > 0 && this.current_motion_actor != null) { this.current_motion_actor.disconnect(this.actor_motion_id); this.actor_motion_id = 0; this.current_motion_actor = null; } }, _refreshPlaces : function() { for (let i = 0; i < this._placesButtons.length; i ++) { this._placesButtons[i].actor.destroy(); } for (let i = 0; i < this._categoryButtons.length; i++) { if (this._categoryButtons[i] instanceof PlaceCategoryButton) { this._categoryButtons[i].actor.destroy(); } } this._placesButtons = new Array(); // Now generate Places category and places buttons and add to the list if (this.showPlaces) { this.placesButton = new PlaceCategoryButton(); this._addEnterEvent(this.placesButton, Lang.bind(this, function() { if (!this.searchActive) { this.placesButton.isHovered = true; if (this.hover_delay > 0) { Tweener.addTween(this, { time: this.hover_delay, onComplete: function () { if (this.placesButton.isHovered) { this._clearPrevCatSelection(this.placesButton); this.placesButton.actor.style_class = "menu-category-button-selected"; this.closeContextMenus(null, false); this._displayButtons(null, -1); } else { this.placesButton.actor.style_class = "menu-category-button"; } } }); } else { this._clearPrevCatSelection(this.placesButton); this.placesButton.actor.style_class = "menu-category-button-selected"; this.closeContextMenus(null, false); this._displayButtons(null, -1); } this.makeVectorBox(this.placesButton.actor); } })); this.placesButton.actor.connect('leave-event', Lang.bind(this, function () { if (this._previousTreeSelectedActor === null) { this._previousTreeSelectedActor = this.placesButton.actor; } else { let prevIdx = this.catBoxIter.getVisibleIndex(this._previousTreeSelectedActor); let nextIdx = this.catBoxIter.getVisibleIndex(this.placesButton.actor); let idxDiff = Math.abs(prevIdx - nextIdx); if (idxDiff <= 1 || Math.min(prevIdx, nextIdx) < 0) { this._previousTreeSelectedActor = this.placesButton.actor; } } this.placesButton.isHovered = false; })); this._categoryButtons.push(this.placesButton); this.categoriesBox.add_actor(this.placesButton.actor); let bookmarks = this._listBookmarks(); let devices = this._listDevices(); let places = bookmarks.concat(devices); for (let i = 0; i < places.length; i++) { let place = places[i]; let button = new PlaceButton(this, place, place.name); this._addEnterEvent(button, Lang.bind(this, function() { this._clearPrevSelection(button.actor); button.actor.style_class = "menu-application-button-selected"; this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(button.place.id.slice(16).replace(/%20/g, ' ')); })); button.actor.connect('leave-event', Lang.bind(this, function() { this._previousSelectedActor = button.actor; this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(""); })); this._placesButtons.push(button); this.applicationsBox.add_actor(button.actor); } } this._setCategoriesButtonActive(!this.searchActive); this._recalc_height(); this._resizeApplicationsBox(); }, _refreshRecent : function() { for (let i = 0; i < this._recentButtons.length; i ++) { this._recentButtons[i].actor.destroy(); } for (let i = 0; i < this._categoryButtons.length; i++) { if (this._categoryButtons[i] instanceof RecentCategoryButton) { this._categoryButtons[i].actor.destroy(); } } this._recentButtons = new Array(); // Now generate recent category and recent files buttons and add to the list if (this.privacy_settings.get_boolean(REMEMBER_RECENT_KEY)) { this.recentButton = new RecentCategoryButton(); this._addEnterEvent(this.recentButton, Lang.bind(this, function() { if (!this.searchActive) { this.recentButton.isHovered = true; if (this.hover_delay > 0) { Tweener.addTween(this, { time: this.hover_delay, onComplete: function () { if (this.recentButton.isHovered) { this._clearPrevCatSelection(this.recentButton.actor); this.recentButton.actor.style_class = "menu-category-button-selected"; this.closeContextMenus(null, false); this._displayButtons(null, null, -1); } else { this.recentButton.actor.style_class = "menu-category-button"; } } }); } else { this._clearPrevCatSelection(this.recentButton.actor); this.recentButton.actor.style_class = "menu-category-button-selected"; this.closeContextMenus(null, false); this._displayButtons(null, null, -1); } this.makeVectorBox(this.recentButton.actor); } })); this.recentButton.actor.connect('leave-event', Lang.bind(this, function () { if (this._previousTreeSelectedActor === null) { this._previousTreeSelectedActor = this.recentButton.actor; } else { let prevIdx = this.catBoxIter.getVisibleIndex(this._previousTreeSelectedActor); let nextIdx = this.catBoxIter.getVisibleIndex(this.recentButton.actor); if (Math.abs(prevIdx - nextIdx) <= 1) { this._previousTreeSelectedActor = this.recentButton.actor; } } this.recentButton.isHovered = false; })); this.categoriesBox.add_actor(this.recentButton.actor); this._categoryButtons.push(this.recentButton); if (this.RecentManager._infosByTimestamp.length > 0) { for (let id = 0; id < MAX_RECENT_FILES && id < this.RecentManager._infosByTimestamp.length; id++) { let button = new RecentButton(this, this.RecentManager._infosByTimestamp[id]); this._addEnterEvent(button, Lang.bind(this, function() { this._clearPrevSelection(button.actor); button.actor.style_class = "menu-application-button-selected"; this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(button.file.uri.slice(7).replace(/%20/g, ' ')); })); button.actor.connect('leave-event', Lang.bind(this, function() { button.actor.style_class = "menu-application-button"; this._previousSelectedActor = button.actor; this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(""); })); this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); this.applicationsBox.add_actor(button.menu.actor); } let button = new RecentClearButton(this); this._addEnterEvent(button, Lang.bind(this, function() { this._clearPrevSelection(button.actor); button.actor.style_class = "menu-application-button-selected"; })); button.actor.connect('leave-event', Lang.bind(this, function() { button.actor.style_class = "menu-application-button"; this._previousSelectedActor = button.actor; })); this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); } else { let button = new GenericButton(_("No recent documents"), null, false, null); this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); } } this._setCategoriesButtonActive(!this.searchActive); this._recalc_height(); this._resizeApplicationsBox(); }, _refreshApps : function() { this.applicationsBox.destroy_all_children(); this._applicationsButtons = new Array(); this._transientButtons = new Array(); this._applicationsButtonFromApp = new Object(); this._applicationsBoxWidth = 0; //Remove all categories this.categoriesBox.destroy_all_children(); this._allAppsCategoryButton = new CategoryButton(null); this._addEnterEvent(this._allAppsCategoryButton, Lang.bind(this, function() { if (!this.searchActive) { this._allAppsCategoryButton.isHovered = true; if (this.hover_delay > 0) { Tweener.addTween(this, { time: this.hover_delay, onComplete: function () { if (this._allAppsCategoryButton.isHovered) { this._clearPrevCatSelection(this._allAppsCategoryButton.actor); this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; this._select_category(null, this._allAppsCategoryButton); } else { this._allAppsCategoryButton.actor.style_class = "menu-category-button"; } } }); } else { this._clearPrevCatSelection(this._allAppsCategoryButton.actor); this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; this._select_category(null, this._allAppsCategoryButton); } this.makeVectorBox(this._allAppsCategoryButton.actor); } })); this._allAppsCategoryButton.actor.connect('leave-event', Lang.bind(this, function () { this._previousSelectedActor = this._allAppsCategoryButton.actor; this._allAppsCategoryButton.isHovered = false; })); this.categoriesBox.add_actor(this._allAppsCategoryButton.actor); let trees = [appsys.get_tree()]; for (var i in trees) { let tree = trees[i]; let root = tree.get_root_directory(); let dirs = []; let iter = root.iter(); let nextType; while ((nextType = iter.next()) != CMenu.TreeItemType.INVALID) { if (nextType == CMenu.TreeItemType.DIRECTORY) { dirs.push(iter.get_directory()); } } let prefCats = ["administration", "preferences"]; dirs = dirs.sort(function(a, b) { let menuIdA = a.get_menu_id().toLowerCase(); let menuIdB = b.get_menu_id().toLowerCase(); let prefIdA = prefCats.indexOf(menuIdA); let prefIdB = prefCats.indexOf(menuIdB); if (prefIdA < 0 && prefIdB >= 0) { return -1; } if (prefIdA >= 0 && prefIdB < 0) { return 1; } let nameA = a.get_name().toLowerCase(); let nameB = b.get_name().toLowerCase(); if (nameA > nameB) { return 1; } if (nameA < nameB) { return -1; } return 0; }); for (let i = 0; i < dirs.length; i++) { let dir = dirs[i]; if (dir.get_is_nodisplay()) continue; if (this._loadCategory(dir)) { let categoryButton = new CategoryButton(dir); this._addEnterEvent(categoryButton, Lang.bind(this, function() { if (!this.searchActive) { categoryButton.isHovered = true; if (this.hover_delay > 0) { Tweener.addTween(this, { time: this.hover_delay, onComplete: function () { if (categoryButton.isHovered) { this._clearPrevCatSelection(categoryButton.actor); categoryButton.actor.style_class = "menu-category-button-selected"; this._select_category(dir, categoryButton); } else { categoryButton.actor.style_class = "menu-category-button"; } } }); } else { this._clearPrevCatSelection(categoryButton.actor); categoryButton.actor.style_class = "menu-category-button-selected"; this._select_category(dir, categoryButton); } this.makeVectorBox(categoryButton.actor); } })); categoryButton.actor.connect('leave-event', Lang.bind(this, function () { if (this._previousTreeSelectedActor === null) { this._previousTreeSelectedActor = categoryButton.actor; } else { let prevIdx = this.catBoxIter.getVisibleIndex(this._previousTreeSelectedActor); let nextIdx = this.catBoxIter.getVisibleIndex(categoryButton.actor); if (Math.abs(prevIdx - nextIdx) <= 1) { this._previousTreeSelectedActor = categoryButton.actor; } } categoryButton.isHovered = false; })); this.categoriesBox.add_actor(categoryButton.actor); } } } // Sort apps and add to applicationsBox this._applicationsButtons.sort(function(a, b) { a = Util.latinise(a.app.get_name().toLowerCase()); b = Util.latinise(b.app.get_name().toLowerCase()); return a > b; }); for (let i = 0; i < this._applicationsButtons.length; i++) { this.applicationsBox.add_actor(this._applicationsButtons[i].actor); this.applicationsBox.add_actor(this._applicationsButtons[i].menu.actor); } this._appsWereRefreshed = true; }, _favEnterEvent : function(button) { button.actor.add_style_pseudo_class("hover"); if (button instanceof FavoritesButton) { this.selectedAppTitle.set_text(button.app.get_name()); if (button.app.get_description()) this.selectedAppDescription.set_text(button.app.get_description().split("\n")[0]); else this.selectedAppDescription.set_text(""); } else { this.selectedAppTitle.set_text(button.name); this.selectedAppDescription.set_text(button.desc); } }, _favLeaveEvent : function(widget, event, button) { this._previousSelectedActor = button.actor; button.actor.remove_style_pseudo_class("hover"); this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(""); }, _refreshFavs : function() { //Remove all favorites this.favoritesBox.destroy_all_children(); //Load favorites again this._favoritesButtons = new Array(); let launchers = global.settings.get_strv('favorite-apps'); let appSys = Cinnamon.AppSystem.get_default(); let j = 0; for ( let i = 0; i < launchers.length; ++i ) { let app = appSys.lookup_app(launchers[i]); if (app) { let button = new FavoritesButton(this, app, launchers.length + 3); // + 3 because we're adding 3 system buttons at the bottom this._favoritesButtons[app] = button; this.favoritesBox.add_actor(button.actor, { y_align: St.Align.END, y_fill: false }); this._addEnterEvent(button, Lang.bind(this, this._favEnterEvent, button)); button.actor.connect('leave-event', Lang.bind(this, this._favLeaveEvent, button)); ++j; } } //Separator if (launchers.length != 0) { let separator = new PopupMenu.PopupSeparatorMenuItem(); this.favoritesBox.add_actor(separator.actor, { y_align: St.Align.END, y_fill: false }); } //Lock screen let button = new SystemButton(this, "system-lock-screen", launchers.length + 3, _("Lock screen"), _("Lock the screen")); this._addEnterEvent(button, Lang.bind(this, this._favEnterEvent, button)); button.actor.connect('leave-event', Lang.bind(this, this._favLeaveEvent, button)); button.activate = Lang.bind(this, function() { this.menu.close(); let screensaver_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.screensaver" }); let screensaver_dialog = Gio.file_new_for_path("/usr/bin/cinnamon-screensaver-command"); if (screensaver_dialog.query_exists(null)) { if (screensaver_settings.get_boolean("ask-for-away-message")) { Util.spawnCommandLine("cinnamon-screensaver-lock-dialog"); } else { Util.spawnCommandLine("cinnamon-screensaver-command --lock"); } } else { this._screenSaverProxy.LockRemote(); } }); this.favoritesBox.add_actor(button.actor, { y_align: St.Align.END, y_fill: false }); //Logout button let button = new SystemButton(this, "system-log-out", launchers.length + 3, _("Logout"), _("Leave the session")); this._addEnterEvent(button, Lang.bind(this, this._favEnterEvent, button)); button.actor.connect('leave-event', Lang.bind(this, this._favLeaveEvent, button)); button.activate = Lang.bind(this, function() { this.menu.close(); this._session.LogoutRemote(0); }); this.favoritesBox.add_actor(button.actor, { y_align: St.Align.END, y_fill: false }); //Shutdown button let button = new SystemButton(this, "system-shutdown", launchers.length + 3, _("Quit"), _("Shutdown the computer")); this._addEnterEvent(button, Lang.bind(this, this._favEnterEvent, button)); button.actor.connect('leave-event', Lang.bind(this, this._favLeaveEvent, button)); button.activate = Lang.bind(this, function() { this.menu.close(); this._session.ShutdownRemote(); }); this.favoritesBox.add_actor(button.actor, { y_align: St.Align.END, y_fill: false }); this._recalc_height(); }, _loadCategory: function(dir, top_dir) { var iter = dir.iter(); var has_entries = false; var nextType; if (!top_dir) top_dir = dir; while ((nextType = iter.next()) != CMenu.TreeItemType.INVALID) { if (nextType == CMenu.TreeItemType.ENTRY) { var entry = iter.get_entry(); if (!entry.get_app_info().get_nodisplay()) { has_entries = true; var app = appsys.lookup_app_by_tree_entry(entry); if (!app) app = appsys.lookup_settings_app_by_tree_entry(entry); var app_key = app.get_id() if (app_key == null) { app_key = app.get_name() + ":" + app.get_description(); } if (!(app_key in this._applicationsButtonFromApp)) { let applicationButton = new ApplicationButton(this, app); var app_is_known = false; for (var i = 0; i < this._knownApps.length; i++) { if (this._knownApps[i] == app_key) { app_is_known = true; } } if (!app_is_known) { if (this._appsWereRefreshed) { applicationButton.highlight(); } else { this._knownApps.push(app_key); } } applicationButton.actor.connect('leave-event', Lang.bind(this, this._appLeaveEvent, applicationButton)); this._addEnterEvent(applicationButton, Lang.bind(this, this._appEnterEvent, applicationButton)); this._applicationsButtons.push(applicationButton); applicationButton.category.push(top_dir.get_menu_id()); this._applicationsButtonFromApp[app_key] = applicationButton; } else { this._applicationsButtonFromApp[app_key].category.push(dir.get_menu_id()); } } } else if (nextType == CMenu.TreeItemType.DIRECTORY) { let subdir = iter.get_directory(); if (this._loadCategory(subdir, top_dir)) { has_entries = true; } } } return has_entries; }, _appLeaveEvent: function(a, b, applicationButton) { this._previousSelectedActor = applicationButton.actor; applicationButton.actor.style_class = "menu-application-button"; this.selectedAppTitle.set_text(""); this.selectedAppDescription.set_text(""); }, _appEnterEvent: function(applicationButton) { this.selectedAppTitle.set_text(applicationButton.app.get_name()); if (applicationButton.app.get_description()) this.selectedAppDescription.set_text(applicationButton.app.get_description()); else this.selectedAppDescription.set_text(""); this._previousVisibleIndex = this.appBoxIter.getVisibleIndex(applicationButton.actor); this._clearPrevSelection(applicationButton.actor); applicationButton.actor.style_class = "menu-application-button-selected"; }, _scrollToButton: function(button) { var current_scroll_value = this.applicationsScrollBox.get_vscroll_bar().get_adjustment().get_value(); var box_height = this.applicationsScrollBox.get_allocation_box().y2-this.applicationsScrollBox.get_allocation_box().y1; var new_scroll_value = current_scroll_value; if (current_scroll_value > button.actor.get_allocation_box().y1-10) new_scroll_value = button.actor.get_allocation_box().y1-10; if (box_height+current_scroll_value < button.actor.get_allocation_box().y2+10) new_scroll_value = button.actor.get_allocation_box().y2-box_height+10; if (new_scroll_value!=current_scroll_value) this.applicationsScrollBox.get_vscroll_bar().get_adjustment().set_value(new_scroll_value); }, _display : function() { this._activeContainer = null; this._activeActor = null; this.vectorBox = null; this.actor_motion_id = 0; this.vector_update_loop = null; this.current_motion_actor = null; let section = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(section); let leftPane = new St.BoxLayout({ vertical: true }); this.leftBox = new St.BoxLayout({ style_class: 'menu-favorites-box', vertical: true }); this._session = new GnomeSession.SessionManager(); this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); leftPane.add_actor(this.leftBox, { y_align: St.Align.END, y_fill: false }); let rightPane = new St.BoxLayout({ vertical: true }); this.searchBox = new St.BoxLayout({ style_class: 'menu-search-box' }); rightPane.add_actor(this.searchBox); this.searchEntry = new St.Entry({ name: 'menu-search-entry', hint_text: _("Type to search..."), track_hover: true, can_focus: true }); this.searchEntry.set_secondary_icon(this._searchInactiveIcon); this.searchBox.add_actor(this.searchEntry); this.searchActive = false; this.searchEntryText = this.searchEntry.clutter_text; this.searchEntryText.connect('text-changed', Lang.bind(this, this._onSearchTextChanged)); this.searchEntryText.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress)); this._previousSearchPattern = ""; this.categoriesApplicationsBox = new CategoriesApplicationsBox(); rightPane.add_actor(this.categoriesApplicationsBox.actor); this.categoriesBox = new St.BoxLayout({ style_class: 'menu-categories-box', vertical: true, accessible_role: Atk.Role.LIST }); this.applicationsScrollBox = new St.ScrollView({ x_fill: true, y_fill: false, y_align: St.Align.START, style_class: 'vfade menu-applications-scrollbox' }); this.a11y_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.a11y.applications" }); this.a11y_settings.connect("changed::screen-magnifier-enabled", Lang.bind(this, this._updateVFade)); this._updateVFade(); this.settings.bindProperty(Settings.BindingDirection.IN, "enable-autoscroll", "autoscroll_enabled", this._update_autoscroll, null); this._update_autoscroll(); let vscroll = this.applicationsScrollBox.get_vscroll_bar(); vscroll.connect('scroll-start', Lang.bind(this, function() { this.menu.passEvents = true; })); vscroll.connect('scroll-stop', Lang.bind(this, function() { this.menu.passEvents = false; })); this.applicationsBox = new St.BoxLayout({ style_class: 'menu-applications-inner-box', vertical:true }); this.applicationsBox.add_style_class_name('menu-applications-box'); //this is to support old themes this.applicationsScrollBox.add_actor(this.applicationsBox); this.applicationsScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); this.categoriesApplicationsBox.actor.add_actor(this.categoriesBox); this.categoriesApplicationsBox.actor.add_actor(this.applicationsScrollBox); let fav_obj = new FavoritesBox(); this.favoritesBox = fav_obj.actor; this.leftBox.add_actor(this.favoritesBox, { y_align: St.Align.END, y_fill: false }); this.mainBox = new St.BoxLayout({ style_class: 'menu-applications-outer-box', vertical:false }); this.mainBox.add_style_class_name('menu-applications-box'); //this is to support old themes this.mainBox.add_actor(leftPane, { span: 1 }); this.mainBox.add_actor(rightPane, { span: 1 }); section.actor.add_actor(this.mainBox); this.selectedAppBox = new St.BoxLayout({ style_class: 'menu-selected-app-box', vertical: true }); if (this.selectedAppBox.peek_theme_node() == null || this.selectedAppBox.get_theme_node().get_length('height') == 0) this.selectedAppBox.set_height(30 * global.ui_scale); this.selectedAppTitle = new St.Label({ style_class: 'menu-selected-app-title', text: "" }); this.selectedAppBox.add_actor(this.selectedAppTitle); this.selectedAppDescription = new St.Label({ style_class: 'menu-selected-app-description', text: "" }); this.selectedAppBox.add_actor(this.selectedAppDescription); section.actor.add_actor(this.selectedAppBox); this.appBoxIter = new VisibleChildIterator(this.applicationsBox); this.applicationsBox._vis_iter = this.appBoxIter; this.catBoxIter = new VisibleChildIterator(this.categoriesBox); this.categoriesBox._vis_iter = this.catBoxIter; this.favBoxIter = new VisibleChildIterator(this.favoritesBox); this.favoritesBox._vis_iter = this.favBoxIter; Mainloop.idle_add(Lang.bind(this, function() { this._clearAllSelections(true); })); }, _updateVFade: function() { let mag_on = this.a11y_settings.get_boolean("screen-magnifier-enabled"); if (mag_on) { this.applicationsScrollBox.style_class = "menu-applications-scrollbox"; } else { this.applicationsScrollBox.style_class = "vfade menu-applications-scrollbox"; } }, _update_autoscroll: function() { this.applicationsScrollBox.set_auto_scrolling(this.autoscroll_enabled); }, _clearAllSelections: function(hide_apps) { let actors = this.applicationsBox.get_children(); for (var i=0; i this._applicationsBoxWidth){ this._applicationsBoxWidth = nat; this.applicationsBox.set_width(this._applicationsBoxWidth + 42); // The answer to life... } }, _resizeApplicationsBox: function() { this._applicationsBoxWidth = 0; this.applicationsBox.set_width(-1); let child = this.applicationsBox.get_first_child(); this._resize_actor_iter(child); while ((child = child.get_next_sibling()) != null) { this._resize_actor_iter(child); } }, _displayButtons: function(appCategory, places, recent, apps, autocompletes){ if (appCategory) { if (appCategory == "all") { this._applicationsButtons.forEach( function (item, index) { item.actor.show(); }); } else { this._applicationsButtons.forEach( function (item, index) { if (item.category.indexOf(appCategory) != -1) { item.actor.show(); } else { item.actor.hide(); } }); } } else if (apps) { for (let i = 0; i < this._applicationsButtons.length; i++) { if (apps.indexOf(this._applicationsButtons[i].name) != -1) { this._applicationsButtons[i].actor.show(); } else { this._applicationsButtons[i].actor.hide(); } } } else { this._applicationsButtons.forEach( function (item, index) { item.actor.hide(); }); } if (places) { if (places == -1) { this._placesButtons.forEach( function (item, index) { item.actor.show(); }); } else { for (let i = 0; i < this._placesButtons.length; i++) { if (places.indexOf(this._placesButtons[i].button_name) != -1) { this._placesButtons[i].actor.show(); } else { this._placesButtons[i].actor.hide(); } } } } else { this._placesButtons.forEach( function (item, index) { item.actor.hide(); }); } if (recent) { if (recent == -1) { this._recentButtons.forEach( function (item, index) { item.actor.show(); }); } else { for (let i = 0; i < this._recentButtons.length; i++) { if (recent.indexOf(this._recentButtons[i].button_name) != -1) { this._recentButtons[i].actor.show(); } else { this._recentButtons[i].actor.hide(); } } } } else { this._recentButtons.forEach( function (item, index) { item.actor.hide(); }); } if (autocompletes) { this._transientButtons.forEach( function (item, index) { item.actor.destroy(); }); this._transientButtons = new Array(); for (let i = 0; i < autocompletes.length; i++) { let button = new TransientButton(this, autocompletes[i]); button.actor.connect('leave-event', Lang.bind(this, this._appLeaveEvent, button)); this._addEnterEvent(button, Lang.bind(this, this._appEnterEvent, button)); this._transientButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.realize(); } } this._searchProviderButtons.forEach( function (item, index) { if (item.actor.visible) { item.actor.hide(); } }); }, _setCategoriesButtonActive: function(active) { try { let categoriesButtons = this.categoriesBox.get_children(); for (var i in categoriesButtons) { let button = categoriesButtons[i]; if (active){ button.set_style_class_name("menu-category-button"); } else { button.set_style_class_name("menu-category-button-greyed"); } } } catch (e) { global.log(e); } }, resetSearch: function(){ this.searchEntry.set_text(""); this._previousSearchPattern = ""; this.searchActive = false; this._clearAllSelections(true); this._setCategoriesButtonActive(true); global.stage.set_key_focus(this.searchEntry); }, _onSearchTextChanged: function (se, prop) { if (this.menuIsOpening) { this.menuIsOpening = false; return; } else { let searchString = this.searchEntry.get_text(); if (searchString == '' && !this.searchActive) return; this.searchActive = searchString != ''; this._fileFolderAccessActive = this.searchActive && this.searchFilesystem; this._clearAllSelections(); if (this.searchActive) { this.searchEntry.set_secondary_icon(this._searchActiveIcon); if (this._searchIconClickedId == 0) { this._searchIconClickedId = this.searchEntry.connect('secondary-icon-clicked', Lang.bind(this, function() { this.resetSearch(); this._select_category(null, this._allAppsCategoryButton); })); } this._setCategoriesButtonActive(false); this._doSearch(); } else { if (this._searchIconClickedId > 0) this.searchEntry.disconnect(this._searchIconClickedId); this._searchIconClickedId = 0; this.searchEntry.set_secondary_icon(this._searchInactiveIcon); this._previousSearchPattern = ""; this._setCategoriesButtonActive(true); this._select_category(null, this._allAppsCategoryButton); } return; } }, _listBookmarks: function(pattern){ let bookmarks = Main.placesManager.getBookmarks(); var res = new Array(); for (let id = 0; id < bookmarks.length; id++) { if (!pattern || bookmarks[id].name.toLowerCase().indexOf(pattern)!=-1) res.push(bookmarks[id]); } return res; }, _listDevices: function(pattern){ let devices = Main.placesManager.getMounts(); var res = new Array(); for (let id = 0; id < devices.length; id++) { if (!pattern || devices[id].name.toLowerCase().indexOf(pattern)!=-1) res.push(devices[id]); } return res; }, _listApplications: function(category_menu_id, pattern){ var applist = new Array(); if (category_menu_id) { applist = category_menu_id; } else { applist = "all"; } let res; if (pattern){ res = new Array(); for (var i in this._applicationsButtons) { let app = this._applicationsButtons[i].app; if (app.get_name().toLowerCase().indexOf(pattern)!=-1 || (app.get_description() && app.get_description().toLowerCase().indexOf(pattern)!=-1) || (app.get_id() && app.get_id().slice(0, -8).toLowerCase().indexOf(pattern)!=-1)) res.push(app.get_name()); } } else res = applist; return res; }, _doSearch: function(){ this._searchTimeoutId = 0; let pattern = this.searchEntryText.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '').toLowerCase(); if (pattern==this._previousSearchPattern) return false; this._previousSearchPattern = pattern; this._activeContainer = null; this._activeActor = null; this._selectedItemIndex = null; this._previousTreeSelectedActor = null; this._previousSelectedActor = null; // _listApplications returns all the applications when the search // string is zero length. This will happend if you type a space // in the search entry. if (pattern.length == 0) { return false; } var appResults = this._listApplications(null, pattern); var placesResults = new Array(); var bookmarks = this._listBookmarks(pattern); for (var i in bookmarks) placesResults.push(bookmarks[i].name); var devices = this._listDevices(pattern); for (var i in devices) placesResults.push(devices[i].name); var recentResults = new Array(); for (let i = 0; i < this._recentButtons.length; i++) { if (!(this._recentButtons[i] instanceof RecentClearButton) && this._recentButtons[i].button_name.toLowerCase().indexOf(pattern) != -1) recentResults.push(this._recentButtons[i].button_name); } var acResults = new Array(); // search box autocompletion results if (this.searchFilesystem) { // Don't use the pattern here, as filesystem is case sensitive acResults = this._getCompletions(this.searchEntryText.get_text()); } this._displayButtons(null, placesResults, recentResults, appResults, acResults); this.appBoxIter.reloadVisible(); if (this.appBoxIter.getNumVisibleChildren() > 0) { let item_actor = this.appBoxIter.getFirstVisible(); this._selectedItemIndex = this.appBoxIter.getAbsoluteIndexOfChild(item_actor); this._activeContainer = this.applicationsBox; if (item_actor && item_actor != this.searchEntry) { item_actor._delegate.emit('enter-event'); } } SearchProviderManager.launch_all(pattern, Lang.bind(this, function(provider, results){ try{ for (var i in results){ if (results[i].type != 'software') { let button = new SearchProviderResultButton(this, provider, results[i]); button.actor.connect('leave-event', Lang.bind(this, this._appLeaveEvent, button)); this._addEnterEvent(button, Lang.bind(this, this._appEnterEvent, button)); this._searchProviderButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.realize(); } } }catch(e){global.log(e);} })); return false; }, _getCompletion : function(text) { if (text.indexOf('/') != -1) { if (text.substr(text.length - 1) == '/') { return ''; } else { return this._pathCompleter.get_completion_suffix(text); } } else { return false; } }, _getCompletions : function(text) { if (text.indexOf('/') != -1) { return this._pathCompleter.get_completions(text); } else { return new Array(); } }, _run : function(input) { let command = input; this._commandError = false; if (input) { let path = null; if (input.charAt(0) == '/') { path = input; } else { if (input.charAt(0) == '~') input = input.slice(1); path = GLib.get_home_dir() + '/' + input; } if (GLib.file_test(path, GLib.FileTest.EXISTS)) { let file = Gio.file_new_for_path(path); try { Gio.app_info_launch_default_for_uri(file.get_uri(), global.create_app_launch_context()); } catch (e) { // The exception from gjs contains an error string like: // Error invoking Gio.app_info_launch_default_for_uri: No application // is registered as handling this file // We are only interested in the part after the first colon. //let message = e.message.replace(/[^:]*: *(.+)/, '$1'); return false; } } else { return false; } } return true; } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/trash@cinnamon.org/0000775000175000017500000000000012625666430024250 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/trash@cinnamon.org/metadata.json0000664000175000017500000000027012625666430026722 0ustar fabiofabio{ "uuid": "trash@cinnamon.org", "name": "Trash", "description": "Control the trash from the panel", "icon": "user-trash", "localedir": "/usr/share/locale", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js0000664000175000017500000000660412625666430026101 0ustar fabiofabioconst St = imports.gi.St; const ModalDialog = imports.ui.modalDialog; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Clutter = imports.gi.Clutter; const Applet = imports.ui.applet; const PopupMenu = imports.ui.popupMenu; const Mainloop = imports.mainloop; const Util = imports.misc.util; const MESSAGE = _("Are you sure you want to delete all items from the trash?") + "\n" + _("This operation cannot be undone."); function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("user-trash"); this.set_applet_tooltip(_("Trash")); this.trash_path = 'trash:///'; this.trash_directory = Gio.file_new_for_uri(this.trash_path); this._initContextMenu(); this.trash_changed_timeout = 0; this._onTrashChange(); this.monitor = this.trash_directory.monitor_directory(0, null, null); this.monitor.connect('changed', Lang.bind(this, this._onTrashChange)); } catch (e) { global.logError(e); } }, _initContextMenu: function () { this.empty_item = new PopupMenu.PopupIconMenuItem(_("Empty Trash"), "list-remove", St.IconType.SYMBOLIC); this.empty_item.connect('activate', Lang.bind(this, this._emptyTrash)); this._applet_context_menu.addMenuItem(this.empty_item); this.open_item = new PopupMenu.PopupIconMenuItem(_("Open Trash"), "document-open", St.IconType.SYMBOLIC); this.open_item.connect('activate', Lang.bind(this, this._openTrash)); this._applet_context_menu.addMenuItem(this.open_item); }, on_applet_clicked: function(event) { this._openTrash(); }, _openTrash: function() { Gio.app_info_launch_default_for_uri(this.trash_directory.get_uri(), null); }, _onTrashChange: function() { if (this.trash_changed_timeout > 0) { Mainloop.source_remove(this.trash_changed_timeout); this.trash_changed_timeout = 0; } this.trash_changed_timeout = Mainloop.timeout_add_seconds(1, Lang.bind(this, this._onTrashChangeTimeout)); }, _onTrashChangeTimeout: function() { this.trash_changed_timeout = 0; if (this.trash_directory.query_exists(null)) { let children = this.trash_directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); if (children.next_file(null, null) == null) { this.set_applet_icon_symbolic_name("user-trash"); } else { this.set_applet_icon_symbolic_name("user-trash-full"); } children.close(null); } }, _emptyTrash: function() { new ModalDialog.ConfirmDialog(MESSAGE, this._doEmptyTrash).open(); }, _doEmptyTrash: function() { Util.spawn(['gvfs-trash', '--empty']); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/0000775000175000017500000000000012625666430025556 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/metadata.json0000664000175000017500000000022412625666430030227 0ustar fabiofabio{ "uuid": "show-desktop@cinnamon.org", "name": "Show desktop", "description": "Minimize all windows", "icon": "desktop", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js0000664000175000017500000000145212625666430027403 0ustar fabiofabioconst Applet = imports.ui.applet; const Main = imports.ui.main; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this.set_applet_icon_name("user-desktop"); this.set_applet_tooltip(_("Show desktop")); }, on_applet_clicked: function(event) { global.screen.toggle_desktop(global.get_current_time()); } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/0000775000175000017500000000000012625666430026375 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/metadata.json0000664000175000017500000000024312625666430031047 0ustar fabiofabio{ "uuid": "removable-drives@cinnamon.org", "name": "Removable drives", "description": "Access removable drives", "icon": "media-eject", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/applet.js0000664000175000017500000000630412625666430030223 0ustar fabiofabioconst Lang = imports.lang; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Applet = imports.ui.applet; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; function DriveMenuItem(place) { this._init(place); } DriveMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(place) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this); this.place = place; this.label = new St.Label({ text: place.name }); this.addActor(this.label); let ejectIcon = new St.Icon({ icon_name: 'media-eject', icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon ' }); let ejectButton = new St.Button({ child: ejectIcon }); ejectButton.connect('clicked', Lang.bind(this, this._eject)); this.addActor(ejectButton); }, _eject: function() { this.place.remove(); }, activate: function(event) { this.place.launch({ timestamp: event.get_time() }); PopupMenu.PopupBaseMenuItem.prototype.activate.call(this, event); } }; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("drive-harddisk"); this.set_applet_tooltip(_("Removable drives")); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._contentSection = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(this._contentSection); this._update(); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addAction(_("Open file manager"), function(event) { let appSystem = Cinnamon.AppSystem.get_default(); let app = appSystem.lookup_app('nemo.desktop'); app.activate_full(-1, event.get_time()); }); Main.placesManager.connect('mounts-updated', Lang.bind(this, this._update)); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _update: function() { this._contentSection.removeAll(); let mounts = Main.placesManager.getMounts(); let any = false; for (let i = 0; i < mounts.length; i++) { if (mounts[i].isRemovable()) { this._contentSection.addMenuItem(new DriveMenuItem(mounts[i])); any = true; } } this.actor.visible = any; } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/xrandr@cinnamon.org/0000775000175000017500000000000012625666430024425 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/xrandr@cinnamon.org/stylesheet.css0000664000175000017500000000005512625666430027330 0ustar fabiofabio.display-subtitle { font-weight: bold; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/xrandr@cinnamon.org/metadata.json0000664000175000017500000000026312625666430027101 0ustar fabiofabio{ "uuid": "xrandr@cinnamon.org", "name": "Display", "description": "XRandR monitor control applet", "icon": "preferences-desktop-display", "localedir": "/usr/share/locale" } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/xrandr@cinnamon.org/applet.js0000664000175000017500000001212512625666430026251 0ustar fabiofabio/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const Interfaces = imports.misc.interfaces; const Gdk = imports.gi.Gdk; const GLib = imports.gi.GLib; const CinnamonDesktop = imports.gi.CinnamonDesktop; const Lang = imports.lang; const Applet = imports.ui.applet; const PopupMenu = imports.ui.popupMenu; const N_ = function(e) { return e }; const possibleRotations = [ CinnamonDesktop.RRRotation.ROTATION_0, CinnamonDesktop.RRRotation.ROTATION_90, CinnamonDesktop.RRRotation.ROTATION_180, CinnamonDesktop.RRRotation.ROTATION_270 ]; let rotations = [ [ CinnamonDesktop.RRRotation.ROTATION_0, N_("Normal") ], [ CinnamonDesktop.RRRotation.ROTATION_90, N_("Left") ], [ CinnamonDesktop.RRRotation.ROTATION_270, N_("Right") ], [ CinnamonDesktop.RRRotation.ROTATION_180, N_("Upside-down") ] ]; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this.set_applet_icon_symbolic_name("preferences-desktop-display"); this.set_applet_tooltip(_("Display")); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); Interfaces.getDBusProxyAsync("org.cinnamon.SettingsDaemon.XRANDR_2", Lang.bind(this, function(proxy, error) { this._proxy = proxy; })); try { this._screen = new CinnamonDesktop.RRScreen({ gdk_screen: Gdk.Screen.get_default() }); this._screen.init(null); } catch(e) { // an error means there is no XRandR extension global.logError(e); this.actor.hide(); return; } this._createMenu(); this._screen.connect('changed', Lang.bind(this, this._randrEvent)); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _randrEvent: function() { this.menu.removeAll(); this._createMenu(); }, _createMenu: function() { let config = CinnamonDesktop.RRConfig.new_current(this._screen); let outputs = config.get_outputs(); for (let i = 0; i < outputs.length; i++) { if (outputs[i].is_connected()) this._addOutputItem(config, outputs[i]); } this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addAction(_("Configure display settings..."), function() { GLib.spawn_command_line_async('cinnamon-settings display'); }); }, _addOutputItem: function(config, output) { let item = new PopupMenu.PopupMenuItem("%s (%s)".format(output.get_display_name(), output.get_name())); item.label.add_style_class_name('display-subtitle'); item.actor.reactive = false; item.actor.can_focus = false; this.menu.addMenuItem(item); let allowedRotations = this._getAllowedRotations(config, output); let currentRotation = output.get_rotation(); for (let i = 0; i < rotations.length; i++) { let [bitmask, name] = rotations[i]; if (bitmask & allowedRotations) { let item = new PopupMenu.PopupMenuItem(_(name)); if (bitmask & currentRotation) item.setShowDot(true); item.connect('activate', Lang.bind(this, function(item, event) { /* ensure config is saved so we get a backup if anything goes wrong */ config.save(); output.set_rotation(bitmask); try { config.save(); this._proxy.ApplyConfigurationRemote(0, event.get_time()); } catch (e) { global.logError('Could not save monitor configuration', e); } })); this.menu.addMenuItem(item); } } }, _getAllowedRotations: function(config, output) { let retval = 0; let current = output.get_rotation(); for (let i = 0; i < possibleRotations.length; i++) { output.set_rotation(possibleRotations[i]); if (config.applicable(this._screen)) { retval |= possibleRotations[i]; } } output.set_rotation(current); if (retval.lenght == 0) { // what, no rotation? // what's current then? retval = current; } return retval; } }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/0000775000175000017500000000000012625666430024105 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/settings-schema.json0000664000175000017500000000017312625666430030077 0ustar fabiofabio{ "display-name" : { "type" : "checkbox", "default" : false, "description": "Display user name on panel" } } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/stylesheet.css0000664000175000017500000000030012625666430027001 0ustar fabiofabio.user-box { padding: .4em 1.75em; spacing: 6px; } .user-icon { border-radius: 4px; border: 2px solid #a5a5a5; } .user-label { color: #fff; font-size: 16px; margin: 6px; } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/0000775000175000017500000000000012625666430025220 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/logout-symbolic.svg0000664000175000017500000000615212625666430031075 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/shutdown-symbolic.svg0000664000175000017500000000277312625666430031444 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/switch-user-symbolic.svg0000664000175000017500000000252012625666430032034 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/guest-session-symbolic.svg0000664000175000017500000000164512625666430032376 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icons/lock-screen-symbolic.svg0000664000175000017500000000051412625666430031765 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/icon.png0000664000175000017500000000302712625666430025545 0ustar fabiofabioPNG  IHDR szzbKGD pHYs B(xtIME)IDATXõn]Gkf}>c;vT1 UI!>;/ kjEUhD4T4-}13{}ln4goѬ_9L_1tsa;wNVWW`04áY`"0@_>ϪnwHg lr7Gf_&V.u/G9Vdl} x$fNA$L@jKrn ;E6 jD 9@AV3d,[ DPRvbTT#ck~=p|6WAYuܹ6@Up!*1p"7 2˿(Jʲݷ[/o,s 1Sā pezpۯ  e- yT 䧎[ 5 JԴƭ?_Z{^/-fQ ӜNTUM`U`09FfIEx:eZ8eb<nsu.8B]~#!L0 aF#gc8<ucehG(Iq83{FChj <Shh`1孜<ԩ >OFdym;D;@R/ 'V7x ?Ԏy*X6Q]I J$|%!R(D!8*!j# -44\Rظxs CH2kO0Xpm9Rr%jLj9)!1Ay"ƀ! 3Z/f!T=V`Mv*)L,6PV=br eZPuF/BH*,& )-Mq})"]vMLѬft&|98^b6a" s<qWɞaDH}&S FC")oK+UX%vwY?ɓO]|b^G@T5}{oUY,#|V]h4/ llHf*^f/W8wpE̔uY %NM+8jLOjSzC\QajM"6=!*q7YN+_B@ņZc,.DEt۪s["1F7|y :A[3Fϖ3ۺqt w^:[zz4Wy[zQB;j;$>WoU6&'ݎ#C` ni$SQgIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/metadata.json0000664000175000017500000000036612625666430026565 0ustar fabiofabio{ "last-edited": "1334576529", "description": "An applet to access your account details, switch users and quickly logout or power off the computer", "uuid": "user@cinnamon.org", "name": "User Applet", "max-instances": -1 } Cinnamon-2.8.6/files/usr/share/cinnamon/applets/user@cinnamon.org/applet.js0000664000175000017500000002047412625666430025737 0ustar fabiofabio//System Shutdown and Restart Applet by Shelley const Cinnamon = imports.gi.Cinnamon; const Applet = imports.ui.applet; const Lang = imports.lang; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; const Util = imports.misc.util; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio const AccountsService = imports.gi.AccountsService; const GnomeSession = imports.misc.gnomeSession; const ScreenSaver = imports.misc.screenSaver; const Main = imports.ui.main; const Panel = imports.ui.panel; const Settings = imports.ui.settings; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instance_id); try { this._session = new GnomeSession.SessionManager(); this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); this.settings = new Settings.AppletSettings(this, "user@cinnamon.org", instance_id); this.set_applet_icon_symbolic_name("avatar-default"); this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._contentSection = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(this._contentSection); let userBox = new St.BoxLayout({ style_class: 'user-box', reactive: true, vertical: false }); this._userIcon = new St.Bin({ style_class: 'user-icon'}); this.settings.bindProperty(Settings.BindingDirection.IN, "display-name", "disp_name", this._updateLabel, null); userBox.connect('button-press-event', Lang.bind(this, function() { this.menu.toggle(); Util.spawnCommandLine("cinnamon-settings user"); })); this._userIcon.hide(); userBox.add(this._userIcon, { x_fill: true, y_fill: false, x_align: St.Align.END, y_align: St.Align.START }); this.userLabel = new St.Label(({ style_class: 'user-label'})); userBox.add(this.userLabel, { x_fill: true, y_fill: false, x_align: St.Align.END, y_align: St.Align.MIDDLE }); this.menu.addActor(userBox); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); let item = new PopupMenu.PopupIconMenuItem(_("System Settings"), "preferences-system", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings"); })); this.menu.addMenuItem(item); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); item = new PopupMenu.PopupIconMenuItem(_("Lock Screen"), "lock-screen", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { let screensaver_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.screensaver" }); let screensaver_dialog = Gio.file_new_for_path("/usr/bin/cinnamon-screensaver-command"); if (screensaver_dialog.query_exists(null)) { if (screensaver_settings.get_boolean("ask-for-away-message")) { Util.spawnCommandLine("cinnamon-screensaver-lock-dialog"); } else { Util.spawnCommandLine("cinnamon-screensaver-command --lock"); } } else { this._screenSaverProxy.LockRemote(); } })); this.menu.addMenuItem(item); if (GLib.getenv("XDG_SEAT_PATH")) { // LightDM item = new PopupMenu.PopupIconMenuItem(_("Switch User"), "switch-user", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-screensaver-command --lock"); Util.spawnCommandLine("dm-tool switch-to-greeter"); })); this.menu.addMenuItem(item); item = new PopupMenu.PopupIconMenuItem(_("Guest Session"), "guest-session", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-screensaver-command --lock"); Util.spawnCommandLine("dm-tool switch-to-guest"); })); this.menu.addMenuItem(item); } else if (GLib.file_test("/usr/bin/mdmflexiserver", GLib.FileTest.EXISTS)) { // MDM item = new PopupMenu.PopupIconMenuItem(_("Switch User"), "switch-user", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("mdmflexiserver"); })); this.menu.addMenuItem(item); } else if (GLib.file_test("/usr/bin/gdmflexiserver", GLib.FileTest.EXISTS)) { // GDM item = new PopupMenu.PopupIconMenuItem(_("Switch User"), "switch-user", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-screensaver-command --lock"); Util.spawnCommandLine("gdmflexiserver"); })); this.menu.addMenuItem(item); } item = new PopupMenu.PopupIconMenuItem(_("Log Out..."), "logout", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { this._session.LogoutRemote(0); })); this.menu.addMenuItem(item); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); item = new PopupMenu.PopupIconMenuItem(_("Power Off..."), "shutdown", St.IconType.SYMBOLIC); item.connect('activate', Lang.bind(this, function() { this._session.ShutdownRemote(); })); this.menu.addMenuItem(item); this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._onUserChanged)); this._userChangedId = this._user.connect('changed', Lang.bind(this, this._onUserChanged)); this._onUserChanged(); } catch (e) { global.logError(e); } }, on_applet_clicked: function(event) { this.menu.toggle(); }, _updateLabel: function() { if (this.disp_name) { this.set_applet_label(this._user.get_real_name()); } else { this.set_applet_label(""); } }, _onUserChanged: function() { if (this._user.is_loaded) { this.set_applet_tooltip(this._user.get_real_name()); this.userLabel.set_text (this._user.get_real_name()); if (this._userIcon) { let iconFileName = this._user.get_icon_file(); let iconFile = Gio.file_new_for_path(iconFileName); let icon; if (iconFile.query_exists(null)) { icon = new Gio.FileIcon({file: iconFile}); } else { icon = new Gio.ThemedIcon({name: 'avatar-default'}); } let img = St.TextureCache.get_default().load_gicon(null, icon, 48); this._userIcon.set_child (img); this._userIcon.show(); } this._updateLabel(); } }, on_applet_removed_from_panel: function() { this.settings.finalize(); }, }; function main(metadata, orientation, panel_height, instance_id) { let myApplet = new MyApplet(orientation, panel_height, instance_id); return myApplet; } Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings-users/0000775000175000017500000000000012625666430023645 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.ui0000664000175000017500000002472312625666430031013 0ustar fabiofabio True False center 640 480 users False True False 6 True False True False vertical 300 True False 6 in True False False False True True 0 True False 6 spread gtk-add True False True True False True 0 gtk-delete True False True True False True 1 False False 1 False True 0 True False 6 vertical True True 1 True False page 1 False True False vertical True False 6 in True False False False False True True 0 True False 6 spread gtk-add True False True True False True 0 gtk-edit True False True True False True 1 gtk-delete True False True True False True 2 False False 1 1 True False page 2 1 False Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.py0000775000175000017500000011047112625666430031025 0ustar fabiofabio#!/usr/bin/env python2 import sys, os import pwd, grp from gi.repository import Gtk, GObject, Gio, GdkPixbuf, AccountsService import gettext import shutil import PIL from PIL import Image from random import randint import re import subprocess gettext.install("cinnamon", "/usr/share/locale") (INDEX_USER_OBJECT, INDEX_USER_PICTURE, INDEX_USER_DESCRIPTION) = range(3) (INDEX_GID, INDEX_GROUPNAME) = range(2) class GroupDialog (Gtk.Dialog): def __init__ (self, label, value): super(GroupDialog, self).__init__() try: self.set_modal(True) self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_title("") table = DimmedTable() table.add_labels([label]) self.entry = Gtk.Entry() self.entry.set_text(value) self.entry.connect("changed", self._on_entry_changed) table.add_controls([self.entry]) self.set_border_width(6) box = self.get_content_area() box.add(table) self.show_all() self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK, ) self.set_response_sensitive(Gtk.ResponseType.OK, False) except Exception, detail: print detail def _on_entry_changed(self, entry): name = entry.get_text() if " " in name or name.lower() != name: entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING) entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("The group name cannot contain upper-case or space characters")) self.set_response_sensitive(Gtk.ResponseType.OK, False) else: entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None) self.set_response_sensitive(Gtk.ResponseType.OK, True) if entry.get_text() == "": self.set_response_sensitive(Gtk.ResponseType.OK, False) class DimmedTable (Gtk.Table): def __init__ (self): super(DimmedTable, self).__init__() self.set_border_width(6) self.set_row_spacings(8) self.set_col_spacings(15) def add_labels(self, texts): row = 0 for text in texts: if text != None: label = Gtk.Label(text) label.set_alignment(1, 0.5) label.get_style_context().add_class("dim-label") self.attach(label, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL) row = row + 1 def add_controls(self, controls): row = 0 for control in controls: self.attach(control, 1, 2, row, row+1) row = row + 1 class EditableEntry (Gtk.Notebook): __gsignals__ = { 'changed': (GObject.SIGNAL_RUN_FIRST, None, (str,)) } PAGE_BUTTON = 0 PAGE_ENTRY = 1 def __init__ (self): super(EditableEntry, self).__init__() self.label = Gtk.Label() self.entry = Gtk.Entry() self.button = Gtk.Button() self.button.set_alignment(0.0, 0.5) self.button.set_relief(Gtk.ReliefStyle.NONE) self.append_page(self.button, None); self.append_page(self.entry, None); self.set_current_page(0) self.set_show_tabs(False) self.set_show_border(False) self.editable = False self.show_all() self.button.connect("released", self._on_button_clicked) self.button.connect("activate", self._on_button_clicked) self.entry.connect("activate", self._on_entry_validated) self.entry.connect("changed", self._on_entry_changed) def set_text(self, text): self.button.set_label(text) self.entry.set_text(text) def _on_button_clicked(self, button): self.set_editable(True) def _on_entry_validated(self, entry): self.set_editable(False) self.emit("changed", entry.get_text()) def _on_entry_changed(self, entry): self.button.set_label(entry.get_text()) def set_editable(self, editable): if (editable): self.set_current_page(EditableEntry.PAGE_ENTRY) else: self.set_current_page(EditableEntry.PAGE_BUTTON) self.editable = editable def set_tooltip_text(self, tooltip): self.button.set_tooltip_text(tooltip) def get_editable(self): return self.editable def get_text(self): return self.entry.get_text() class PasswordDialog(Gtk.Dialog): def __init__ (self, user, password_mask, group_mask): super(PasswordDialog, self).__init__() self.user = user self.password_mask = password_mask self.group_mask = group_mask self.set_modal(True) self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_title(_("Change Password")) table = DimmedTable() table.add_labels([_("New password"), None, _("Confirm password")]) self.new_password = Gtk.Entry() self.new_password.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "reload") self.new_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Generate a password")) self.new_password.connect("icon-release", self._on_new_password_icon_released) self.new_password.connect("changed", self._on_passwords_changed) table.attach(self.new_password, 1, 3, 0, 1) self.strengh_indicator = Gtk.ProgressBar() self.strengh_indicator.set_tooltip_text(_("Your new password needs to be at least 8 characters long")) self.strengh_indicator.set_fraction(0.0) table.attach(self.strengh_indicator, 1, 2, 1, 2, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL) self.strengh_indicator.set_size_request(-1, 1) self.strengh_label = Gtk.Label() self.strengh_label.set_tooltip_text(_("Your new password needs to be at least 8 characters long")) self.strengh_label.set_alignment(1, 0.5) table.attach(self.strengh_label, 2, 3, 1, 2) self.confirm_password = Gtk.Entry() self.confirm_password.connect("changed", self._on_passwords_changed) table.attach(self.confirm_password, 1, 3, 2, 3) self.show_password = Gtk.CheckButton(_("Show password")) self.show_password.connect('toggled', self._on_show_password_toggled) table.attach(self.show_password, 1, 3, 3, 4) self.set_border_width(6) box = self.get_content_area() box.add(table) self.show_all() self.infobar = Gtk.InfoBar() self.infobar.set_message_type(Gtk.MessageType.ERROR) label = Gtk.Label(_("An error occured. Your password was not changed.")) content = self.infobar.get_content_area() content.add(label) table.attach(self.infobar, 0, 3, 4, 5) self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, _("Change"), Gtk.ResponseType.OK, ) self.set_passwords_visibility() self.set_response_sensitive(Gtk.ResponseType.OK, False) self.infobar.hide() self.connect("response", self._on_response) def _on_response(self, dialog, response_id): if response_id == Gtk.ResponseType.OK: self.change_password() else: self.destroy() def change_password(self): newpass = self.new_password.get_text() self.user.set_password(newpass, "") mask = self.group_mask.get_text() if "nopasswdlogin" in mask: subprocess.call(["gpasswd", "-d", self.user.get_user_name(), "nopasswdlogin"]) mask = mask.split(", ") mask.remove("nopasswdlogin") mask = ", ".join(mask) self.group_mask.set_text(mask) self.password_mask.set_text(u'\u2022\u2022\u2022\u2022\u2022\u2022') self.destroy() def set_passwords_visibility(self): visible = self.show_password.get_active() self.new_password.set_visibility(visible) self.confirm_password.set_visibility(visible) def _on_new_password_icon_released(self, widget, icon_pos, event): self.infobar.hide() self.show_password.set_active(True) characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" newpass = "" for i in range (8): index = randint(0, len(characters) -1) newpass = newpass + characters[index] self.new_password.set_text(newpass) self.confirm_password.set_text(newpass) self.check_passwords() def _on_show_password_toggled(self, widget): self.set_passwords_visibility() # Based on setPasswordStrength() in Mozilla Seamonkey, which is tri-licensed under MPL 1.1, GPL 2.0, and LGPL 2.1. # Forked from Ubiquity validation.py def password_strength(self, password): upper = lower = digit = symbol = 0 for char in password: if char.isdigit(): digit += 1 elif char.islower(): lower += 1 elif char.isupper(): upper += 1 else: symbol += 1 length = len(password) length = min(length,4) digit = min(digit,3) upper = min(upper,3) symbol = min(symbol,3) strength = ( ((length * 0.1) - 0.2) + (digit * 0.1) + (symbol * 0.15) + (upper * 0.1)) if strength > 1: strength = 1 if strength < 0: strength = 0 return strength def _on_passwords_changed(self, widget): self.infobar.hide() new_password = self.new_password.get_text() confirm_password = self.confirm_password.get_text() strength = self.password_strength(new_password) if new_password != confirm_password: self.confirm_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING) self.confirm_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Passwords do not match")) else: self.confirm_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None) if len(new_password) < 8: self.strengh_label.set_text(_("Too short")) self.strengh_indicator.set_fraction(0.0) elif strength < 0.5: self.strengh_label.set_text(_("Weak")) self.strengh_indicator.set_fraction(0.2) elif strength < 0.75: self.strengh_label.set_text(_("Fair")) self.strengh_indicator.set_fraction(0.4) elif strength < 0.9: self.strengh_label.set_text(_("Good")) self.strengh_indicator.set_fraction(0.6) else: self.strengh_label.set_text(_("Strong")) self.strengh_indicator.set_fraction(1.0) self.check_passwords() def check_passwords(self): new_password = self.new_password.get_text() confirm_password = self.confirm_password.get_text() if len(new_password) >= 8 and new_password == confirm_password: self.set_response_sensitive(Gtk.ResponseType.OK, True) else: self.set_response_sensitive(Gtk.ResponseType.OK, False) class NewUserDialog(Gtk.Dialog): def __init__ (self): super(NewUserDialog, self).__init__() try: self.set_modal(True) self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_title("") self.account_type_combo = Gtk.ComboBoxText() self.account_type_combo.append_text(_("Standard")) self.account_type_combo.append_text(_("Administrator")) self.account_type_combo.set_active(0) self.realname_entry = Gtk.Entry() self.realname_entry.connect("changed", self._on_info_changed) self.username_entry = Gtk.Entry() self.username_entry.connect("changed", self._on_info_changed) label = Gtk.Label() label.set_markup(_("The username must consist of only:\n - lower case letters (a-z)\n - numerals (0-9)\n - '.', '-', and '_' characters")) table = DimmedTable() table.add_labels([_("Account Type"), _("Full Name"), _("Username")]) table.add_controls([self.account_type_combo, self.realname_entry, self.username_entry]) self.set_border_width(6) box = self.get_content_area() box.add(table) box.add(label) self.show_all() self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_ADD, Gtk.ResponseType.OK, ) self.set_response_sensitive(Gtk.ResponseType.OK, False) except Exception, detail: print detail def _on_info_changed(self, widget): fullname = self.realname_entry.get_text() username = self.username_entry.get_text() valid = True if re.search('[^a-z0-9_.-]', username): self.username_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING) self.username_entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Invalid username")) valid = False else: self.username_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None) if username == "" or fullname == "": valid = False self.set_response_sensitive(Gtk.ResponseType.OK, valid) class GroupsDialog(Gtk.Dialog): def __init__ (self, username): super(GroupsDialog, self).__init__() try: self.set_modal(True) self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_title("") self.set_default_size(200, 480) scrolled = Gtk.ScrolledWindow() viewport = Gtk.Viewport() vbox = Gtk.VBox() self.checkboxes = [] groups = sorted(grp.getgrall(), key=lambda x: x[0], reverse=False) for group in groups: checkbox = Gtk.CheckButton(group[0]) self.checkboxes.append(checkbox) vbox.add(checkbox) if username in group[3]: checkbox.set_active(True) viewport.add(vbox) scrolled.add(viewport) self.set_border_width(6) box = self.get_content_area() box.pack_start(scrolled, True, True, 0) self.show_all() self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK, ) except Exception, detail: print detail def get_selected_groups(self): groups = [] for checkbox in self.checkboxes: if checkbox.get_active(): groups.append(checkbox.get_label()) return groups class Module: def __init__(self): try: self.builder = Gtk.Builder() self.builder.add_from_file("/usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.ui") self.window = self.builder.get_object("main_window") self.window.connect("destroy", Gtk.main_quit) self.window.set_title(_("Users and Groups")) self.builder.get_object("label_users").set_label(_("Users")) self.builder.get_object("label_groups").set_label(_("Groups")) self.builder.get_object("button_add_user").connect("clicked", self.on_user_addition) self.builder.get_object("button_delete_user").connect("clicked", self.on_user_deletion) self.builder.get_object("button_add_group").connect("clicked", self.on_group_addition) self.builder.get_object("button_edit_group").connect("clicked", self.on_group_edition) self.builder.get_object("button_delete_group").connect("clicked", self.on_group_deletion) self.users = Gtk.TreeStore(object, GdkPixbuf.Pixbuf, str) self.users.set_sort_column_id(2, Gtk.SortType.ASCENDING) self.groups = Gtk.TreeStore(int, str) self.groups.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.users_treeview = self.builder.get_object("treeview_users") self.users_treeview.set_rules_hint(True) self.groups_treeview = self.builder.get_object("treeview_groups") self.users_treeview.get_selection().connect("changed", self.on_user_selection) self.groups_treeview.get_selection().connect("changed", self.on_group_selection) column = Gtk.TreeViewColumn() cell = Gtk.CellRendererPixbuf() column.pack_start(cell, True) column.add_attribute(cell, 'pixbuf', INDEX_USER_PICTURE) cell.set_property('ypad', 1) self.users_treeview.append_column(column) column = Gtk.TreeViewColumn() cell = Gtk.CellRendererText() column.pack_start(cell, True) column.add_attribute(cell, 'markup', INDEX_USER_DESCRIPTION) self.users_treeview.append_column(column) column = Gtk.TreeViewColumn() cell = Gtk.CellRendererText() column.pack_start(cell, True) column.add_attribute(cell, 'text', INDEX_GROUPNAME) column.set_sort_column_id(1) self.groups_treeview.append_column(column) self.builder.get_object("button_delete_user").set_sensitive(False) self.builder.get_object("button_edit_group").set_sensitive(False) self.builder.get_object("button_delete_group").set_sensitive(False) self.face_button = Gtk.Button() self.face_image = Gtk.Image() self.face_button.set_image(self.face_image) self.face_image.set_from_file("/usr/share/cinnamon/faces/user-generic.png") self.face_button.set_alignment(0.0, 0.5) self.face_button.set_tooltip_text(_("Click to change the picture")) self.menu = Gtk.Menu() separator = Gtk.SeparatorMenuItem() face_browse_menuitem = Gtk.MenuItem(_("Browse for more pictures...")) face_browse_menuitem.connect('activate', self._on_face_browse_menuitem_activated) self.face_button.connect("button-release-event", self.menu_display) row = 0 col = 0 num_cols = 4 face_dirs = ["/usr/share/cinnamon/faces"] for face_dir in face_dirs: if os.path.exists(face_dir): pictures = sorted(os.listdir(face_dir)) for picture in pictures: path = os.path.join(face_dir, picture) file = Gio.File.new_for_path(path) file_icon = Gio.FileIcon.new(file) image = Gtk.Image.new_from_gicon (file_icon, Gtk.IconSize.DIALOG) menuitem = Gtk.MenuItem() menuitem.add(image) menuitem.connect('activate', self._on_face_menuitem_activated, path) self.menu.attach(menuitem, col, col+1, row, row+1) col = (col+1) % num_cols if (col == 0): row = row + 1 row = row + 1 self.menu.attach(separator, 0, 4, row, row+1) self.menu.attach(face_browse_menuitem, 0, 4, row+2, row+3) self.account_type_combo = Gtk.ComboBoxText() self.account_type_combo.append_text(_("Standard")) self.account_type_combo.append_text(_("Administrator")) self.account_type_combo.connect("changed", self._on_accounttype_changed) self.realname_entry = EditableEntry() self.realname_entry.connect("changed", self._on_realname_changed) self.realname_entry.set_tooltip_text(_("Click to change the name")) self.password_mask = Gtk.Label() self.password_mask.set_alignment(0.0, 0.5) self.password_button = Gtk.Button() self.password_button.add(self.password_mask) self.password_button.set_relief(Gtk.ReliefStyle.NONE) self.password_button.set_tooltip_text(_("Click to change the password")) self.password_button.connect('activate', self._on_password_button_clicked) self.password_button.connect('released', self._on_password_button_clicked) self.groups_label = Gtk.Label() self.groups_label.set_line_wrap(True) self.groups_label.set_alignment(0, 0.5) self.groups_button = Gtk.Button() self.groups_button.add(self.groups_label) self.groups_button.set_relief(Gtk.ReliefStyle.NONE) self.groups_button.set_tooltip_text(_("Click to change the groups")) self.groups_button.connect("clicked", self._on_groups_button_clicked) box = Gtk.Box() box.pack_start(self.face_button, False, False, 0) table = DimmedTable() table.add_labels([_("Picture"), _("Account Type"), _("Name"), _("Password"), _("Groups")]) table.add_controls([box, self.account_type_combo, self.realname_entry, self.password_button, self.groups_button]) self.builder.get_object("box_users").add(table) self.accountService = AccountsService.UserManager.get_default() self.accountService.connect('notify::is-loaded', self.on_accounts_service_loaded) self.load_groups() self.window.show_all() self.builder.get_object("box_users").hide() except Exception, detail: print detail def _on_password_button_clicked(self, widget): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] dialog = PasswordDialog(user, self.password_mask, self.groups_label) response = dialog.run() def _on_groups_button_clicked(self, widget): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] dialog = GroupsDialog(user.get_user_name()) response = dialog.run() if response == Gtk.ResponseType.OK: groups = dialog.get_selected_groups() subprocess.call(["usermod", user.get_user_name(), "-G", ",".join(groups)]) groups.sort() self.groups_label.set_text(", ".join(groups)) dialog.destroy() def _on_accounttype_changed(self, combobox): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] if self.account_type_combo.get_active() == 1: user.set_account_type(AccountsService.UserAccountType.ADMINISTRATOR) else: user.set_account_type(AccountsService.UserAccountType.STANDARD) groups = [] for group in grp.getgrall(): if user.get_user_name() in group[3]: groups.append(group[0]) groups.sort() self.groups_label.set_text(", ".join(groups)) def _on_realname_changed(self, widget, text): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] user.set_real_name(text) description = "%s\n%s" % (text, user.get_user_name()) model.set_value(treeiter, INDEX_USER_DESCRIPTION, description) def _on_face_browse_menuitem_activated(self, menuitem): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] dialog = Gtk.FileChooserDialog(None, None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) filter = Gtk.FileFilter() filter.set_name(_("Images")) filter.add_mime_type("image/*") dialog.add_filter(filter) preview = Gtk.Image() dialog.set_preview_widget(preview); dialog.connect("update-preview", self.update_preview_cb, preview) dialog.set_use_preview_label(False) response = dialog.run() if response == Gtk.ResponseType.OK: path = dialog.get_filename() image = PIL.Image.open(path) width, height = image.size if width > height: new_width = height new_height = height elif height > width: new_width = width new_height = width else: new_width = width new_height = height left = (width - new_width)/2 top = (height - new_height)/2 right = (width + new_width)/2 bottom = (height + new_height)/2 image = image.crop((left, top, right, bottom)) image.thumbnail((96, 96), Image.ANTIALIAS) face_path = os.path.join(user.get_home_dir(), ".face") image.save(face_path, "png") user.set_icon_file(face_path) self.face_image.set_from_file(face_path) model.set_value(treeiter, INDEX_USER_PICTURE, GdkPixbuf.Pixbuf.new_from_file_at_size(face_path, 48, 48)) model.row_changed(model.get_path(treeiter), treeiter) dialog.destroy() def update_preview_cb (self, dialog, preview): filename = dialog.get_preview_filename() if filename is None: return dialog.set_preview_widget_active(False) if os.path.isfile(filename): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, 128, 128) if pixbuf is not None: preview.set_from_pixbuf (pixbuf) dialog.set_preview_widget_active(True) def _on_face_menuitem_activated(self, menuitem, path): if os.path.exists(path): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] user.set_icon_file(path) self.face_image.set_from_file(path) shutil.copy(path, os.path.join(user.get_home_dir(), ".face")) model.set_value(treeiter, INDEX_USER_PICTURE, GdkPixbuf.Pixbuf.new_from_file_at_size(path, 48, 48)) model.row_changed(model.get_path(treeiter), treeiter) def menu_display(self, widget, event): if event.button == 1: self.menu.popup(None, None, self.popup_menu_below_button, self.face_button, event.button, event.time) self.menu.show_all() def popup_menu_below_button (self, *args): # the introspection for GtkMenuPositionFunc seems to change with each Gtk version, # this is a workaround to make sure we get the menu and the widget menu = args[0] widget = args[-1] # here I get the coordinates of the button relative to # window (self.window) button_x, button_y = widget.get_allocation().x, widget.get_allocation().y # now convert them to X11-relative unused_var, window_x, window_y = widget.get_window().get_origin() x = window_x + button_x y = window_y + button_y # now move the menu below the button y += widget.get_allocation().height push_in = True # push_in is True so all menu is always inside screen return (x, y, push_in) def on_accounts_service_loaded(self, user, param): self.load_users() def load_users(self): self.users.clear() users = self.accountService.list_users() for user in users: if os.path.exists(user.get_icon_file()): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(user.get_icon_file(), 48, 48) else: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size("/usr/share/cinnamon/faces/user-generic.png", 48, 48) description = "%s\n%s" % (user.get_real_name(), user.get_user_name()) piter = self.users.append(None, [user, pixbuf, description]) self.users_treeview.set_model(self.users) def load_groups(self): self.groups.clear() groups = sorted(grp.getgrall(), key=lambda x: x[0], reverse=False) for group in groups: (gr_name, gr_passwd, gr_gid, gr_mem) = group piter = self.groups.append(None, [gr_gid, gr_name]) self.groups_treeview.set_model(self.groups) #USER CALLBACKS def on_user_selection(self, selection): self.password_button.set_sensitive(True) self.password_button.set_tooltip_text("") model, treeiter = selection.get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] self.builder.get_object("button_delete_user").set_sensitive(True) self.realname_entry.set_text(user.get_real_name()) if user.get_password_mode() == AccountsService.UserPasswordMode.REGULAR: self.password_mask.set_text(u'\u2022\u2022\u2022\u2022\u2022\u2022') elif user.get_password_mode() == AccountsService.UserPasswordMode.NONE: self.password_mask.set_markup("%s" % _("No password set")) else: self.password_mask.set_text(_("Set at login")) if user.get_account_type() == AccountsService.UserAccountType.ADMINISTRATOR: self.account_type_combo.set_active(1) else: self.account_type_combo.set_active(0) if os.path.exists(user.get_icon_file()): self.face_image.set_from_file(user.get_icon_file()) else: self.face_image.set_from_file("/usr/share/cinnamon/faces/user-generic.png") groups = [] for group in grp.getgrall(): if user.get_user_name() in group[3]: groups.append(group[0]) groups.sort() self.groups_label.set_text(", ".join(groups)) self.builder.get_object("box_users").show() # Count the number of connections for the currently logged-in user connections = int(subprocess.check_output(["w", "-hs", user.get_user_name()]).count("\n")) if connections > 0: self.builder.get_object("button_delete_user").set_sensitive(False) self.builder.get_object("button_delete_user").set_tooltip_text(_("This user is currently logged in")) else: self.builder.get_object("button_delete_user").set_sensitive(True) self.builder.get_object("button_delete_user").set_tooltip_text("") if os.path.exists("/home/.ecryptfs/%s" % user.get_user_name()): self.password_button.set_sensitive(False) self.password_button.set_tooltip_text(_("The user's home directory is encrypted. To preserve access to the encrypted directory, only the user should change this password.")) else: self.builder.get_object("button_delete_user").set_sensitive(False) self.builder.get_object("box_users").hide() def on_user_deletion(self, event): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: user = model[treeiter][INDEX_USER_OBJECT] message = _("Are you sure you want to permanently delete %s and all the files associated with this user?") % user.get_user_name() d = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, message) d.set_markup(message) d.set_default_response(Gtk.ResponseType.NO) r = d.run() d.destroy() if r == Gtk.ResponseType.YES: result = self.accountService.delete_user(user, True) if result: model.remove(treeiter) self.load_groups() def on_user_addition(self, event): dialog = NewUserDialog() response = dialog.run() if response == Gtk.ResponseType.OK: if dialog.account_type_combo.get_active() == 1: account_type = AccountsService.UserAccountType.ADMINISTRATOR else: account_type = AccountsService.UserAccountType.STANDARD fullname = dialog.realname_entry.get_text() username = dialog.username_entry.get_text() new_user = self.accountService.create_user(username, fullname, account_type) new_user.set_password_mode(AccountsService.UserPasswordMode.NONE) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size("/usr/share/cinnamon/faces/user-generic.png", 48, 48) description = "%s\n%s" % (fullname, username) piter = self.users.append(None, [new_user, pixbuf, description]) # Add the user to his/her own group and sudo if Administrator was selected if dialog.account_type_combo.get_active() == 1: subprocess.call(["usermod", username, "-G", "%s,sudo,nopasswdlogin" % username]) else: subprocess.call(["usermod", username, "-G", "%s,nopasswdlogin" % username]) self.load_groups() dialog.destroy() def on_user_edition(self, event): model, treeiter = self.users_treeview.get_selection().get_selected() if treeiter != None: print "Editing user %s" % model[treeiter][INDEX_USER_OBJECT].get_user_name() # GROUPS CALLBACKS def on_group_selection(self, selection): model, treeiter = selection.get_selected() if treeiter != None: self.builder.get_object("button_edit_group").set_sensitive(True) self.builder.get_object("button_delete_group").set_sensitive(True) self.builder.get_object("button_delete_group").set_tooltip_text("") group = model[treeiter][INDEX_GROUPNAME] for p in pwd.getpwall(): username = p[0] primary_group = grp.getgrgid(p[3])[0] if primary_group == group: self.builder.get_object("button_delete_group").set_sensitive(False) self.builder.get_object("button_delete_group").set_tooltip_text(_("This group is set as %s's primary group") % username) break else: self.builder.get_object("button_edit_group").set_sensitive(False) self.builder.get_object("button_delete_group").set_sensitive(False) self.builder.get_object("button_delete_group").set_tooltip_text("") def on_group_deletion(self, event): model, treeiter = self.groups_treeview.get_selection().get_selected() if treeiter != None: group = model[treeiter][INDEX_GROUPNAME] message = _("Are you sure you want to permanently delete %s?") % group d = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, message) d.set_markup(message) d.set_default_response(Gtk.ResponseType.NO) r = d.run() if r == Gtk.ResponseType.YES: subprocess.call(["groupdel", group]) self.load_groups() d.destroy() def on_group_addition(self, event): dialog = GroupDialog(_("Group Name"), "") response = dialog.run() if response == Gtk.ResponseType.OK: subprocess.call(["groupadd", dialog.entry.get_text().lower()]) self.load_groups() dialog.destroy() def on_group_edition(self, event): model, treeiter = self.groups_treeview.get_selection().get_selected() if treeiter != None: group = model[treeiter][INDEX_GROUPNAME] dialog = GroupDialog(_("Group Name"), group) response = dialog.run() if response == Gtk.ResponseType.OK: subprocess.call(["groupmod", group, "-n", dialog.entry.get_text().lower()]) self.load_groups() dialog.destroy() if __name__ == "__main__": module = Module() Gtk.main() Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.svg0000664000175000017500000010341012625666430031164 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-slideshow/0000775000175000017500000000000012625666430022647 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-slideshow/cinnamon-slideshow.py0000775000175000017500000003204412625666430027030 0ustar fabiofabio#! /usr/bin/python2 from gi.repository import Gio, GLib import dbus, dbus.service, dbus.glib from dbus.mainloop.glib import DBusGMainLoop import random import os, locale from xml.etree import ElementTree SLIDESHOW_DBUS_NAME = "org.Cinnamon.Slideshow" SLIDESHOW_DBUS_PATH = "/org/Cinnamon/Slideshow" BACKGROUND_COLLECTION_TYPE_DIRECTORY = "directory" BACKGROUND_COLLECTION_TYPE_XML = "xml" class CinnamonSlideshow(dbus.service.Object): def __init__(self): bus_name = dbus.service.BusName(SLIDESHOW_DBUS_NAME, bus=dbus.SessionBus()) dbus.service.Object.__init__(self, bus_name, SLIDESHOW_DBUS_PATH) self.slideshow_settings = Gio.Settings(schema="org.cinnamon.desktop.background.slideshow") self.background_settings = Gio.Settings(schema="org.cinnamon.desktop.background") if self.slideshow_settings.get_boolean("slideshow-paused"): self.slideshow_settings.set_boolean("slideshow-paused", False) self.image_playlist = [] self.used_image_playlist = [] self.images_ready = False self.update_in_progress = False self.current_image = self.background_settings.get_string("picture-uri") self.update_id = 0 self.loop_counter = self.slideshow_settings.get_int("delay") self.folder_monitor = None self.folder_monitor_id = 0 @dbus.service.method(SLIDESHOW_DBUS_NAME, in_signature='', out_signature='') def begin(self): self.setup_slideshow() @dbus.service.method(SLIDESHOW_DBUS_NAME, in_signature='', out_signature='') def end(self): if self.update_id > 0: GLib.source_remove(self.update_id) self.update_id = 0 ml.quit() @dbus.service.method(SLIDESHOW_DBUS_NAME, in_signature='', out_signature='') def getNextImage(self): if self.update_id > 0: GLib.source_remove(self.update_id) self.update_id = 0 self.loop_counter = self.slideshow_settings.get_int("delay") self.start_mainloop() def setup_slideshow(self): self.load_settings() self.connect_signals() self.gather_images() if self.collection_type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: self.connect_folder_monitor() self.start_mainloop() def format_source(self, type, path): # returns 'type://path' return ("%s://%s" % (type, path)) def load_settings(self): self.random_order = self.slideshow_settings.get_boolean("random-order") self.collection = self.slideshow_settings.get_string("image-source") self.collection_path = "" self.collection_type = None if self.collection != "" and "://" in self.collection: (self.collection_type, self.collection_path) = self.collection.split("://") self.collection_path = os.path.expanduser(self.collection_path) def connect_signals(self): self.slideshow_settings.connect("changed::image-source", self.on_slideshow_source_changed) self.slideshow_settings.connect("changed::random-order", self.on_random_order_changed) self.background_settings.connect("changed::picture-uri", self.on_picture_uri_changed) def connect_folder_monitor(self): folder_path = Gio.file_new_for_path(self.collection_path) self.folder_monitor = folder_path.monitor_directory(0, None) self.folder_monitor_id = self.folder_monitor.connect("changed", self.on_monitored_folder_changed) def disconnect_folder_monitor(self): if self.folder_monitor_id > 0: self.folder_monitor.disconnect(self.folder_monitor_id) self.folder_monitor_id = 0 def gather_images(self): if self.collection_type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: folder_at_path = Gio.file_new_for_path(self.collection_path) if folder_at_path.query_exists(None): folder_at_path.enumerate_children_async("standard::type,standard::content-type", Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, None, self.gather_images_cb, None) elif self.collection_type == BACKGROUND_COLLECTION_TYPE_XML: pictures = self.parse_xml_backgrounds_list(self.collection_path) for picture in pictures: filename = picture["filename"] self.add_image_to_playlist(filename) def gather_images_cb(self, obj, res, user_data): all_files = [] enumerator = obj.enumerate_children_finish(res) def on_next_file_complete(obj, res, user_data=all_files): files = obj.next_files_finish(res) file_list = all_files if len(files) is not 0: file_list = file_list.extend(files) enumerator.next_files_async(100, GLib.PRIORITY_LOW, None, on_next_file_complete, None) else: enumerator.close(None) self.ensure_file_is_image(file_list) enumerator.next_files_async(100, GLib.PRIORITY_LOW, None, on_next_file_complete, all_files) def ensure_file_is_image(self, file_list): for item in file_list: file_type = item.get_file_type(); if file_type is not Gio.FileType.DIRECTORY: file_contents = item.get_content_type(); if file_contents.startswith("image"): self.add_image_to_playlist(self.collection_path + "/" + item.get_name()) def add_image_to_playlist(self, file_path): image = Gio.file_new_for_path(file_path) image_uri = image.get_uri(); self.image_playlist.append(image_uri) if self.collection_type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: self.image_playlist.sort() self.images_ready = True def on_slideshow_source_changed(self, settings, key): if self.update_id > 0: GLib.source_remove(self.update_id) self.update_id = 0 self.disconnect_folder_monitor() self.image_playlist = [] self.used_image_playlist = [] self.images_ready = False self.collection = self.slideshow_settings.get_string("image-source") self.collection_path = "" self.collection_type = None if self.collection != "" and "://" in self.collection: (self.collection_type, self.collection_path) = self.collection.split("://") self.collection_path = os.path.expanduser(self.collection_path) if self.collection_type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: self.connect_folder_monitor() self.gather_images() self.loop_counter = self.slideshow_settings.get_int("delay") self.start_mainloop() def on_monitored_folder_changed(self, monitor, file1, file2, event_type): try: if event_type == Gio.FileMonitorEvent.DELETED: file_uri = file1.get_uri(); if self.image_playlist.count(file_uri) > 0: index_to_remove = self.image_playlist.index(file_uri) del self.image_playlist[index_to_remove] elif self.used_image_playlist.count(file_uri) > 0: index_to_remove = self.used_image_playlist.index(file_uri) del self.used_image_playlist[index_to_remove] if event_type == Gio.FileMonitorEvent.CREATED: file_path = file1.get_path() file_info = file1.query_info("standard::type,standard::content-type", Gio.FileQueryInfoFlags.NONE, None) file_type = file_info.get_file_type() if file_type is not Gio.FileType.DIRECTORY: file_contents = file_info.get_content_type(); if file_contents.startswith("image"): self.add_image_to_playlist(file_path) except: pass def on_random_order_changed(self, settings, key): self.random_order = self.slideshow_settings.get_boolean("random-order") def on_picture_uri_changed(self, settings, key): if self.update_in_progress: return else: if self.background_settings.get_string("picture-uri") != self.current_image: self.slideshow_settings.set_boolean("slideshow-enabled", False) def start_mainloop(self): if self.update_id > 0: GLib.source_remove(self.update_id) self.update_id = 0 if not self.images_ready: self.update_id = GLib.timeout_add_seconds(1, self.start_mainloop) else: if self.loop_counter >= self.slideshow_settings.get_int("delay") and not self.slideshow_settings.get_boolean("slideshow-paused"): self.loop_counter = 1 self.update_background() self.update_id = GLib.timeout_add_seconds(60, self.start_mainloop) else: self.loop_counter = self.loop_counter + 1 self.update_id = GLib.timeout_add_seconds(60, self.start_mainloop) def update_background(self): if self.update_in_progress: return self.update_in_progress = True if len(self.image_playlist) == 0: self.move_used_images_to_original_playlist() next_image = self.get_next_image_from_list() if next_image is not None: self.background_settings.set_string("picture-uri", next_image) self.current_image = next_image self.update_in_progress = False def get_next_image_from_list(self): if self.random_order: index = random.randint(0, len(self.image_playlist) - 1) image = self.image_playlist[index] else: index = 0 image = self.image_playlist[index] self.move_image_to_used_playlist(index, image) return image def move_image_to_used_playlist(self, index, image): self.image_playlist.pop(index) self.used_image_playlist.append(image) def move_used_images_to_original_playlist(self): self.image_playlist = self.used_image_playlist if self.collection_type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: self.image_playlist.sort() self.used_image_playlist = [] ########### TAKEN FROM CS_BACKGROUND def splitLocaleCode(self, localeCode): loc = localeCode.partition("_") loc = (loc[0], loc[2]) return loc def getLocalWallpaperName(self, names, loc): result = "" mainLocFound = False for wp in names: wpLoc = wp[0] wpName = wp[1] if wpLoc == ("", ""): if not mainLocFound: result = wpName elif wpLoc[0] == loc[0]: if wpLoc[1] == loc[1]: return wpName elif wpLoc[1] == "": result = wpName mainLocFound = True return result def parse_xml_backgrounds_list(self, filename): try: locAttrName = "{http://www.w3.org/XML/1998/namespace}lang" loc = self.splitLocaleCode(locale.getdefaultlocale()[0]) res = [] subLocaleFound = False f = open(filename) rootNode = ElementTree.fromstring(f.read()) f.close() if rootNode.tag == "wallpapers": for wallpaperNode in rootNode: if wallpaperNode.tag == "wallpaper" and wallpaperNode.get("deleted") != "true": wallpaperData = {"metadataFile": filename} names = [] for prop in wallpaperNode: if type(prop.tag) == str: if prop.tag != "name": wallpaperData[prop.tag] = prop.text else: propAttr = prop.attrib wpName = prop.text locName = self.splitLocaleCode(propAttr.get(locAttrName)) if propAttr.has_key(locAttrName) else ("", "") names.append((locName, wpName)) wallpaperData["name"] = self.getLocalWallpaperName(names, loc) if "filename" in wallpaperData and wallpaperData["filename"] != "" and os.path.exists(wallpaperData["filename"]) and os.access(wallpaperData["filename"], os.R_OK): if wallpaperData["name"] == "": wallpaperData["name"] = os.path.basename(wallpaperData["filename"]) res.append(wallpaperData) return res except Exception, detail: print detail return [] ############### if __name__ == "__main__": DBusGMainLoop(set_as_default=True) slideshow = CinnamonSlideshow() ml = GLib.MainLoop.new(None, True) ml.run() Cinnamon-2.8.6/files/usr/share/cinnamon/icons/0000775000175000017500000000000012625666430020161 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/icons/move-symbolic.svg0000664000175000017500000000230512625666430023467 0ustar fabiofabio Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/0000775000175000017500000000000012625666430021214 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cursors/0000775000175000017500000000000012625666430022714 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cursors/DMZ-White.png0000664000175000017500000000163612625666430025140 0ustar fabiofabioPNG  IHDR szzbKGD pHYs  tIME  n+IDATX՗MHee;E1N9yc0&e-b6ΪEhӢ mBHN (J4?p!Q1Q3\Mr}owfd̹S4V^Z _- 2C \%r677=O󵌄666$IJRV+@]- .k$iqqQQ xZ]]1F^### @+G \r2<[M-//+[ ILCՂҒNNNtvv5::!ƀ!#qbxDOO x:iPFAޗökMMMiwwWBA}}}/IXmmm~!3%7xĄvvv| [[[upp I nL9{>Nl6nMNNJQ]q-9N |NK7>>m| < 544$kJ`x탏>|Nm577bixl* E ~>p*"WB1pزwWv2מB.%@z}[J'7S'EliIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cursors/Adwaita.png0000664000175000017500000000221312625666430024772 0ustar fabiofabioPNG  IHDR szzbKGD pHYs  tIME  ;:IDATX՗_H[gƟ$'dIJm0aMW&E)^alLh2gMQ+X2Nh)z uz!vS(<9[h 9?&,/I[N}:l?.Vn pэ$s?-{nqŋɹ_%I:| Hĉ/4mTVV2H4MNMMCYQQAǶ6!DX4$Y__OI`0Ȯ. @ Hv3 2 $yi!EQJe3H$џKD@rz<:tHG˗I7eY-B*a4M0CCCÙsiz;P Q4MdY*d2ySBI(\+qMLNN"FBQuJ( ڵkX__G"t꭭C`.hp8|0fffC477;88 #_\u\..,,UJ.w;ƥ8{,v;=O>Mb?Ȳ|f}i}n>F  Eϓ$/]DP(L&t:  mYCK3@gg'ID"!I^zu2vdSS].^/>};w ]%R)d___ ,--zeYN ]+W&x$ɿ$].kjj۷o8?hBյ:dŸ5VU4;; !Dc[R)fYٳgd2~_ !MxGcc{M^~fR {$IR.sXX_Nzě`EFIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cursors/unknown.png0000664000175000017500000000176712625666430025134 0ustar fabiofabioPNG  IHDR szzbKGD pHYs^tIME  ;(셾IDATXŗK\W?fMeЄRpSEiR䩸SPPAD\u!Lw;.]ꢛqLHP B6i?{卙ͅ{9{督|Ms֬ҕ,}_d6Xw]WG+|-hx<܀WE <^ F@|ZkyӖeooovnnʇ_A@.FkQ͏uk6.E=C%WSҖr{xx>GGGW~7vk~~>;::Nfgg/U=!Cy/w](?P(4^r,+Vq `8Xuw}}b ƾ^SSq5S A(E$?DL>nssD"شedAAr0fysn7|٧B}+%Y1 Q S28 xA{EwY Z3%Z\\֔靡!=>>^/<7 J+ @,--522rllX/IUќ 1r~-;VqjJh.IQqآ/m[|IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cursors/DMZ-Black.png0000664000175000017500000000221312625666430025064 0ustar fabiofabioPNG  IHDR szzbKGD pHYs  tIME  ;:IDATX՗_H[gƟ$'dIJm0aMW&E)^alLh2gMQ+X2Nh)z uz!vS(<9[h 9?&,/I[N}:l?.Vn pэ$s?-{nqŋɹ_%I:| Hĉ/4mTVV2H4MNMMCYQQAǶ6!DX4$Y__OI`0Ȯ. @ Hv3 2 $yi!EQJe3H$џKD@rz<:tHG˗I7eY-B*a4M0CCCÙsiz;P Q4MdY*d2ySBI(\+qMLNN"FBQuJ( ڵkX__G"t꭭C`.hp8|0fffC477;88 #_\u\..,,UJ.w;ƥ8{,v;=O>Mb?Ȳ|f}i}n>F  Eϓ$/]DP(L&t:  mYCK3@gg'ID"!I^zu2vdSS].^/>};w ]%R)d___ ,--zeYN ]+W&x$ɿ$].kjj۷o8?hBյ:dŸ5VU4;; !Dc[R)fYٳgd2~_ !MxGcc{M^~fR {$IR.sXX_Nzě`EFIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cinnamon/0000775000175000017500000000000012625666430023016 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/cinnamon/unknown.png0000664000175000017500000023043512625666430025232 0ustar fabiofabioPNG  IHDRp{i=bKGD pHYs  tIME   tEXtCommentCreated with GIMPW IDATxis$ɝoxܑ7 Pnr&99gtjMfHfkzz2=iV36;҈Cv7y}A"ITuf- pB!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!K@!^|ZY]ʲ˲Dk1T+ZcYJ)˲,j$ !#ĵ/u 0E8;{|c:RB!crs3?[.j:[,yN4_Km dhM^m"N!bOj7Y9??ܐ$ UU}'x> .óiz~?ᗚN)N!^|t}}gI^"2Kb IkDQ`0 "|qB)xgkq+I$aX0͞E񬪪gO$ !Byy>󜪪<0 (dM񵄸-ވ\ey$ !BY4MC]׻\vM!R ߛ(v5MCY_XRB!cЮ`YJ)l}2.#oBԽ9e'BuWS%M qݿA]'BqQNl;S܄D-jG$ !BxhC׽mjus7+rݠnnJ74! _=Z{}o\7 {VԮ|ߡ)IB!Gk .rsd08q:U5y)܀V <?*PQF!>B,/HqAm폸 Q5yS֊ʛa` ei:#R`Yx:)ѹ6I_ sTQk>! @׿1ݲ 99XseASUJa,0wwUUmuoGکwpc ֨ti2P5Q8#zlH^]SR|s:я6vGHʲdg!#`m覆9j)?m3@4`^0fjP9zWMXs& ^Xr?tq5h189暟18jp(m\k jMA,\>0`i&zEy2xM θ?f55Nd'QM_\Y r6*L\n\4xӣ1<{BsdMmCg(dYD1nMH]%dUuSL?x2 +첢6[}j +? バggG<=q]|5?5w\yS{G|KuO!SOZCI)SG.Ph]/3a6}Ÿ<2,\*hB( .`{2ed7X+lSꪏӛ8$C6zErɇSNOF 58q~яfasϯ85{< lqlUJvz}dl_ԍF6 :9`J;a>'LNpc'O<湢WF|p4ĭ+mЖkg%GrCzOFr5`c2#w`GCKٜ/i|wƜ []\_0>]+U&{3k:2AWWq<뢍ƪ3H= [;([a;6m[uBYv#o pZk,&g9Pi3>=ɔhMB4>dtvQ!d~s9xʓ!'Dpyгl|}w#T)im#S~p:OϷp46 p:ϫB!ķ([a~"˨*ONo'_F-F|h@+0(A>TK3L8`8rrg-!Ñɐ8?Kkp08}~Ĭ7KRd'q85*t i*TS܅w-J#N!Pw9s˲,˸}Zo;Mi dInQS%+旗lFo:= mr0X(ikl16e),ۢ,^q})G{ 6kaYUI$Tf)}fqu%)Aڀ6 m`@yxttj=x?zuy>Fg)frYRg˛0`EgyR H-[cz''#L"\,/`|PO1XX(e( zM(xUc_q'3g''÷klt-t͊L88:xb7$sxtt8" hd/7}N<9`2=&)\~hN9e\,/|S0`ueS5Zy.Ͳ,L])eq^S[j;h MCUo׬Xݠ(=잇. o61l M?r%[BfA&L/_: הxq HuӰ)+ʪ^neӚu!15=˲1Kci%G$ !B[?az4IVYl2zfuKg>oE\eC J8q4פk{aélna׷pwW'ft07`|1Da DVA4we( MYm/_mY-?/VA`7#޾7*3Nqt~7gG|" ێ]䦻J >8gbs}g?;7Vbl݇u(N8=s|8e즬WcFxz&ޱ8!B?!RoVl,Džj;jZw{X2_Ugˆnڨ(e\U!?³w. ՔYOuŋW5U0 }~p Fv4l;vkn4@a>}xC!wgY"6%1ej|I< bL|ܶm[vF漎-,eó3&1ś,f-ERPNC檭n/BMtmCoVlϕ!.,}mZ7ye~e=<|ʕV9F:{[D3w XP&cVْyRS}\dp* =ku]"lJQ!b8+@B!ķ5ĵ˷{1kp7{)r2O}lYQ,(:6 36OcL=|&vk;U.ᢌB]4*riސ,Ϩ{}?k}2y)nz;Ӵ9Yi)׋ LJ3߬YS.X*m>ij$k4;bΗ,u$ِ. C]kpx3Oo4c=/0dcuFUf'G?!)ϾS4I Yl 31Ӡ?+wu~7hXmq<3 q1N,cwSU8YR>wnLmY`dtijC9 L$,`bhpgڡ0y<}[ 4ĦiA:C>x'#0mL8w4O\j"N0xM:ݠWs~Vǟ<9 Q]LJ%|am.n`p(u pB!߂׆h6 4Ml2|rq]˱vB v@hm'рƲ &#N}6E0[abd,j;5Rk}7WI28cj2uWudm>3Nqp&PlW)߹8!B?pkbfu]k6YAb(U%eB'e_جn u&ѫ5`[tIi xZfEni61iemq\O`dn95WbD>UʬPP7)˲$Yo׼ZE zJu% !J_3GdYBYX2a5lhнʒu9)p]UfIb׿SeTOF.ݯ ub5NCTzi*Jj-_Z^s o,kY(?³Kʛ+r\]Ick8?(V+Ҭj9|`N|>',+ ]zXɆŪկ (M\jW ?ɎM]q:v׭J<-7d \8%.=<σ{vo`C*,xunm46.#?e9K±VYS.^z9)ӏ,F׆JkuFH5˗F+9tz6Jċ˗\ #GAS IDATIXuC fe 5"68'Un}'BH~s+,oSt1_--ʠ\c6,הIDϺfxـV$,O AC8n)_Z v(B `VT8 lUŔ%J.7=ސhvTvC\g|yf3XyI >CԷs?wm2aYS\V#\]hЫyY/xJc`UTɂzj_Ip UtIV6 j1IˋˀvX-^M8Ȑ K)p5*F^-]W JXEBXS|rQ"/?SCQ80 KҬ`yS|Ea霛+7>Ǯ**cQ*$%׫ Wc~U%䬫Cdr^48ņrQparvӦi eNY)MW ,^˫_1WMjQu\6q^1!M=> 5*fDAgڎ.3|M4)M o VZu1KD8B7U Z#OG\ݹX=`ח6bww[jzش >`LpeYԵC]T+ˢi<&h( WJ4~a|l.Yޭ Y`lu/KnXaq[y3}'m:٬kUEUUm=_CsM5fj#cxCdr0O[ֱF)gOɚȚfR%{O CC`X E|>sX* ,%Pa ŇR r7P`63JM72J!Bo\|҆SZ0W{xoV'4wڦiv?v<]ꔿJ9pC,YY]]1p}{m랧,]YnՖ}eY:#K=28!xu,>^7uP9 c|_t=18`~8!BSvz;N[]ޔx[3]m.ѝʷ|2V1+Coݍ߷q;r}7@uCCϑuS]ϻn>t/|C8!Bo }ՎWyc`Dw dYT*cw kK{7"]~?~ٶ~U8!BGT -o&78!B uSGx)!BQB pB!B!N!B!8!B!B!BB!B pB!B!$ !B!8!B!B!c0HEH=#{GP!qv -˺}=xgjy_wOB!H_k֚ihf׉TJ8mZ:u^jW-{!}`m4 u]X)Rj׮m~wнL_WIB!wh۶, ۶iuq];n^Ͻse񮰦wnmX7[)k6p}ٺc^i{vmHk!_GIB!X%EQPu].8M:KHֆ,wl۶mۻky}ۈI [FTZB ~_WW5yUU9UU}<{5|L۰/מg?uA7[,뭝yvtP;WU /ш0 mq|/{o4 O=s91' N%5e'|`8`|urppcbvZ=N}+^Pe js1HB!ķ85MCQdYv#>x9gggz=...vmkzGSr=O9=s0xq)XRs@Ig*k;;d:qfSnjCSہn;϶m '7ވ^cks>)Kn8cv<Խ0݆m}!:dY{ h`g98htf%:! 1y}hӧL!kOE y(f|o2ٓ 3"6 M5 Xm8 pB!ءoCYySNOOǻ#u[d1-&M uf5Mbm(3'#+^m4oa75FW%k'g Uۨ8!BoikG.0F8j;@Un-s | $b^G|,s |HN"z Um rhG>8CZޔI@(Llv98 M]kn Bq{6x#w9 |̘h|A#ħ' E $,V &g#mp@Fc>%4LZ|CU6lFG:z}0`KֵK 8O#N??^}QLFc<2T1DJCh:'/KPjFpB!9(899tJ8y{w)O^={ k, qgO9=p-i2a[΀[3Me =jmc4Y1c<0=~}se0)>CB;6\ e#LS+ElF13agcl?)rr*GkGQꡅ8|g28A8 {w4M*.HW9kv`(4^G·l6ke$ ya;>S^{>z^Pqq )rrqwz&豦4KB!xO[eYaHg0얀>P{ߵ]@Sݰ\¯튢  OGAA$dEŦ(ӒtPV$kHp49ƪ*[?"ƽ@'yI4ԥn_a+C,Gmcʸ؁m65E`T(@ِq] 4M!_2X* ڮuCkq(b4Zv mo_X&'\RY6@B!_Cel6#ގt|>S STfq{V6 ,@cꂺ>61k;ͱ=vUXޤ2ض@S"o\FtAmku!VU c[(Ro~}۲jh\\mcFB!#v(X,qf!2NOOL&rcwe Px"]<l], YL:񔦮r"iX8fj}Izs Ѥe )*oꦤ,rҴ FC`dxl5RX{D2M'7nnY0"o* `*͍eʄx-DMm۾sb˗xbahM3`p5륅UVԵ]|s\8O'B` W$k%Y, OP_\zy$CʯH n۶8!BGMve+8&s*꺦nNݾW0vzoYmJӷm˓'Ox1~)'''(˒`@Q=k;1-Zex _VT&,e 43Ťts|{ Ty⒵8ɲdgіWu; \$D"WX,ܹaiqGm{7>h}g9)Hs$"I(vd:ɲ9 LvuR[#$ɦd2a2_0N eynh^?گL2ew" ߦiz:NEQGQ"H$D&"+ _Hx_e5]lmc}u2U6n_x^{S~׾QH$D"lenEmy}y9D"H$5*:՞7횉^H$D"l2kfwUd͘E"H$|Esnө:] v;C|wC')Zڶ%֚$IvǮq}ynL]g팓iIvp[`#]ۤh [ɧk-RM(ڤ[k'rn}{VwǞ$fDvKvގaW/oDD"H$leSc"4h,fS,kQ32sq9*9L[>;> w?62(r,!M@;' KŬٴ϶lg.&vqQ'UMk #;+<(*r·/APTQo=%Y{ʙ !P2_&[|m<K9d_v4.CeSf,g,>AG:I448M%.SrD3-]R/,f Nmrn%AB5"4eSH$D"Z8d8*9HU4 [e:]/%]6|YV{1fqAq#4u}n)c h- IDAT4s~tϲK!I(maRPdp0qΘ4>وd'lZMl!MX9h2cĜs|8D I0*K !ۼv#y^{ D~6d^ IdM<"|}tQ:쬦jݹa]FݫJ8`4`ヂ(g,fjbfI^P͕c:>PW-!j5 S(]PK"D<(ZD8,=&e5m X+\.EPyWeh+Jyǃ5J3PNQfAh̽(s`ă"il?I 8wz&&qdD Q`8،f TF8\ľ03(픉 U$yƠ,h%U+<}@Zc;A+=kt}Yu/ {i<ͨǨT 2|6g̛IQJcpQ"H$D,pf!QREۚ5L!G@jl~Hg5ԋ3t^mW(П[KIBezehqdBhyIkt.6_&g̦Ǻ!-R 06)qq0L.YNNгUI)RQrIL#wU XH=@{.vά0|K B:hA%`MMXg}* yq(1_O|cR!ḤdmB!L-d>,+䳧8_vr=QHÒ9$K[|`kv.q[R]ל6J8,5cň2%K4Jw{2KH$D" P.LiF( ?x:ЍF!e #sd7c2ŀl_BkMJTqq^f4V`:,}ɨiM^L]%6дiTRK;d3dt98~{zLc9dNCMmx |yp΁rfp+B1bTOxj$ T~)CYI8H,KRQHh̛*sCL_OjȪ4cZڶe\qX`BΠnj&>%YHR`i[&*kux 22wH=b4J)'+" Fudi@dAyp!D2mzE450U7(pH$yUytJϟô h0`| )O& y_-)5y/!uAe4.Tn$aYF |`ZT8yX oӟ3WQsdAx|/p19)u^^a}]Rd5S.3?[5p+J `|jT!\F΂Ʒ>jrT[S:0-! (.uW\T(!*'IRyl3^zΚ 6d/ Iճ=` R,Z|;t.Җ{-5A$KVu͡׏txx_=4de &0pb4B!exnۿŀyƓ&-[%]}׈D"A..ܦ̾"S C<]0R+Fi Ovt_&)3 C;mYpZZ3#rѷ(Q8!!*1YNi \Cc2 +EAS%5J*zg}jn}ghAο_rjth@xi̘URg 4<Ś\2Z)5~H R<ڀ)|L*y~m9J;k|ZLxo @DoM%A$h%h{KJG,Z?% \$DJ N@K^3\[7/)A 'OWـzd>?CBahZ0Ҁm"$DL+Qjq LS_85&IOfrr2%ЃBzuuEjy[ж-puK8岢=FX,MH*[ԇ Le[JeiI܌E,qsOR?@pZ$B%VXld6oI] OSW4>UvK%J4Q jYcSl\J-UU!:'bWGILh!c_[V25#ޭl ۸u\T4a@X^0mk{\>b$l@=7(pH$*IkyM\>/x(rW&qRJdh[ (WSل2 sM*&NIY)3 fJU:W]f2;]ޯn:c 9e4bnƴ#cY'JbU6%Ψ+Gk+ŴcI܌٬")gL RQ-+LRxT0,kqu2@,gmp ӧ5(-d7-i[lH-*!W jڦ uGYZ̤(QQ/=^ f"ԈjGx"s_5]?·1'M9ik痘FRup^t/i7.,-1BabB.)[oR]ZE"H*n¶3yE[1/(rW"pRfiWUmKJB8CBm~+2!CM&msV(%7қC2P W Ƚjߕ2ٔR1-e-jIuӶe'4[W;61s6%ɺ.R{C&Uqg~m$I0kɲ6Ι/5uos!h֒)U pCR5 p.!猶}V<̵!^GX׏P^ᲂ QT*EBsb{C4cF)YoJY:]L7kH$|m]{;wp޶|Eͳt*Z#TJ,Yg,Ouv>7K)7-.ݯRmI<WUcOc~4`]~~~*b%\w$I~;lX[9j}F*ݱvK_LxL@uxIGw[{ 8w~GH$|m魟u%7%laK..(qubˮU=??:sKH_e`XE1mՏYFוۮܳ]k{oUyYކ$mK0{D"7E.MoL]~c_yvݒ?IE^3Y'me:ߗmE%c(߆\|풹n۲}6s[uOD"7IٶeY{({ZohB-<BB< !Є9{Z򧧧Ǐ?7~n{NdO4ϥ_ȝe;`^v嶘E߹6_e_j̛ێU_?ӽ:.D"lZ?,/e7[RJu7T-R9y!~u]|>>m۪'kf]3Ndȫ|X_vpx}cD"W-oz[ʲᇿW.])eR G/gkY3c.CwQ6.J\$DE"H䍔]6|;IuPJ {ѣG h֛Y?&[ \'o2J\$DE"H"oݖoRw^EYo}wz'?4Msj-qt//%.D"Q"H$[_ܒ[?R~BFk/~ѣGje9ޣD"(pH$ydn[ҏ>诏^76oGg` dR< ڮ&J\$DE"HMѷ8I/ P>#`Jtʖ -J\R\m{5b,1$oq (pH$oo<!o?`1։D^H4uM۶_[ѼN'7cʾ,.%MS<'˲FE"ח[]U8;?z'vH\X [7r[b.Nf]sBk\o]J=ZhId"w !ж-{ic4¹XغXsdYD"m_r|-yۖ6-rYڶ*( $!޶u vQUJ)40sp]p='ݐKXB(eBRxQJED"k#nW!xͧO~ZUչ>ZmEc5EQ0dUtEyMz|.{/Xb,],!ܿk.k&D"ϯvk9X,Nd\żmڮ0RJi$IeY6F{eYZ'ر.?OOn ~nI$f"W4%I.wvŗUPJmpn|&J)R81ˋb2RW&p}w3 \$DJv}C4˓G'''NC$uýBUUǟcP>|h4zxxGGG>99x[oղvmω2o0wvfocfi^˚p8rl"I˘^UtorEnRMu..D"}18}_~ggggg}I۱9;۞ŗ_~p8g8޿A~WNNN>['qfKvhE!o9cp=g (6p^֗J!ĹL]AG?.K+Iqm宲E}b$.S:I?r]+ED".p~wkvg} .%Cwn"??{sdPJ׋'΅Eb J)mK۶!60CIw-J}i>ũ!Zc=W0Msx:a;vk/k.+qD+<}nJ\E"Hu}9~g?z_n __κlzn{ؽpFW'|GwݿvM~L&/ײZ⚞ԩ-U9i}φ̡hO1 tgWP%vq\c{snsx٤$2Y@sL`V ϲqSnIYA&i s\SQW5sQWYqQ&Kꊦch5~pБ`̈́RczһXd" "SS-X4 އz1`zG ʌA4b.o2Q"H$PଵG~~dhy'sSvz[~NNNO~G) \ \%rzKD"n$.hH9z4(F,Tְ8%5 d>m9Y>`,Q ܰ楅Q.f1e)ӄQYP -JB3G霙6-j%|݋,cXH9@3c4J#%h&f3Gۛ3yUfdɇ%2 K=z\^5ֵI۶ ! eF 8cgC(_ռ(pH$~GKגzbeZ:a\mەokˀ}|spI b z[7/qe$iQRTd4V83g50w,i6$e-id:yQ\7Pc<(*<l>OWk8okefQ\neR9XC``̥H$¶k!ksO`7yғNL[y3\>zO?_?:= -ے'q燖FX,X1nu?;sY4K'x b#o*'|1R}Aަ@^F]R5+#opwMWjI2C's/p=rJM;Z/ tr<9VKd F-ٌ3sqsnVڒH V Hsh? ^dGH$I&w|/4M?O p`Cku[@H{-8ѓpO>iGY{O`K޾V 2^*t~ŢbQZl&b 9jCHUq?,N2pRJTR"ʄ,Q rAZ3"=ټ[r2m˰KٖnMd"键j5Gn0 K<9iHݜZPD&fɉ-h}.o2ߏxYxj"CjUEPIԛx.sW.!w4n+傅Z$ɥd:tǘu i SGJHXOk,KzIXWuYlUwU.4'4Q풦Z0ޖD"W%sGN4ztٰuL<ܕy%Xm/&9W]렽['Mmҗ>C'#7i 4 M0 kS"Gkz/.s|3c9,P[t!t80#TS8א{,8yԄ EWrrYaIHK)֒,E%&( @ͳ5BH%IuNl[u-iDW@ez{D"Hm[g'5]v[_,I]7\kBZ^iȭ}gC?G:W5EotOED"EY8Q(mm#qtKo"_#j|]5:%p߯e0._]VlJ%ҏɋ;yCe_7E"H"/i{.7BJ9NNNNlf7óe _eβ]`JPmT2cyQ,5^1qQ"H$r!{YUWDU8)~?Z_kO~֓2{lEa\,(￲_v!iܿ?cyi,!֯>bD"7YnKe4-~Lo^wG_|/'ifKͅ8uW=/` \;yYF#c^Ugy%=E"Hȥm0Hޚ>?Yfؗ`Dvu?2~:͇ڞ u"_ w5myu[G,yQ,7Ylmv:eE"H䵖WFk{~ӟ9l[։(m[X_9J;6(J+\W6qc6~q.}S$CO_ v>c蟗]1^kci]merUYذ]e?#?Go⚉Q"H$|{I)߽>??;;;Y֭.lɛp04Fn3~%@F$*f9@W $'IB$;I6Ҷ-ιMtmin*~KR禋j5ZkJV J'!Ƙ6pNf.J)bNN9}b>G/k8.D"Q.!oko/|x=O:yrp>OF$LH*[bꊶY2sŹ!˺90ɘHP.i9Fb\}=X}@^d_Ihp.=VYJ9T= L=ƣaHUm:KRe#| j=`41RT f,cy]G5sق6R  s \SQW5s`6tA RhEI(9E*CmkŜ^(pH$[~(UU5??]Vq>Ws>-7:!VE OH.YPOLzY ] %\!̂T/܊+͇~rTr<, J|DLNLL2rXe2A^ 7,!qLj4p$B[ٔ X|x,gLO[B)H)ʤ44Tsқi%JyӄaɠHb@h(uB83s٥eD&HӒu)IpP,@p= D"ER WݱGۜ[s٦L̘$6k%'cNxkqAn<^6uXiiZ\(0P -.*x'Ϙփ]b}L>`P(D ]uyP#H$ye{||77l6ٲ>d̾EDڶe^Kho1-&*zM_B³6uަ|9XԎK*SMA HenM0y`CNVT >m2vVCkT ӥ1vuYC;x<TΟ+qj;Y섳 9 \`om˲zK`fsٲe^M1Z@E"HuB nةp?Otzʳ:m[7t۶EiN̒$#%>Z8J0/# 28[кnWm&WwGվ1VHiSfv]2-_xmRD)83R F$ oi,4֤$]W*A')'Q TTCnXeE*Q_{w{ JjUbI`/DhI@k/S`}ݒ i2ian:TvokED"WI䶼};/Ho(o͏~?Ӟ{ U\pI$7yR"u%|KhZj; g\ !< -'ۇcI$>IB!d@SӶ-h+0=Â,jsG%Nf7TdHfN&ωmb9,s rጅh2aI+pC b1cv.ep,(U{H9~x$Th2rL~(hƃb.I 58z" 4zȽ"jHmi]k0" \$D4a{}{/u tVs~ۜٷuY~B"%Zk<E1Jy'ZK/8|qRJ)Ő\IdS$` MFLdYʰikՌj>ajҗVX63M:D_HaF eij6Zmǥ"ӊ"q뱑!X"Pm1VdZGF2Jm@zAF$ZPv?9sD9R9rA>dT&hmi Vm71 .D"w)o}7r9'mv gfmړŖ9b-rt@3&k!E)yNRvȓ$Y x,b(kd,L$i痍\!֠)md0"K,,995,Cg4)sVC4u0 bA/mMU5,BRjDD"ț(ol \k?{o˒}n>{L>CfʪTu;A!z=KVVPy7|I4#8Fp8~ d0ܜ\ZwB9t:=?Z/tEL\7 \} zfFFBւ)DT,ˀHNWh!huHi߇C,ϩ1 %a6-f$^|\H){=%{Y^5,`LCNiWOb!7ƥu$YRYv߸q[sN9_^?KzCq_ЭN}W9?Xkݾ5˰2pj1vU t0@6^7[{tsBX)*2o9c̹"̀<1Clf\17.c]I$ō;'|EJyc\1?z/y7_sޞ:\amn{=|/3{ o.Vstҁ[:%n ÇxTM׿`o.QF_Ʋ{(/nlܶ~|cgg5.9n޼Wx th[C??i9??okҽ&7W}rh.x zt`^[( cg 4ɾ)cg}2. _Ry>vl m7 YUUsN?7t[6o  z@-oi,ޚ}xj t_~aYLsϛo{ʼ--? z}N&P[ i<_|__[[8WPr~א ~]Q Wd{,ܺbn^]_9%gߚ֏`0{ʁ>}|>gtRqݷ@ͯ:bJL_˃,%L{V~ @e1*hsWv\w"~Aɞk6xoAvU3py]^^bO{B/^jphw~&2MSNOO_}\$I٫f`eW?>`bu]し˾KJXI}-дټ5χ4,>tp}eC:_͟}1},f߽ OQ2I{ʁf̾\mcn=n!tP{lv¯`cjʼ .~xnx /~h۵/k8Gۻ<5̇}mb6f>[ '9@Eѯri+VeB%7Mx z%Z)d  ce4lBKy,96I$Ƀ< \)%RJR'擛+IeY鍹\pz|ſ·<ޚu)~ʁevpiyux :=|pBb|0q @ >}{H0>,!n.m^|xqiJvn,$98^\uYkQJ=zlv]#c RJGJy}]!\Ü>¦Rj {Z,ۚ|lfi|qݸ9^e79oԍJd=,q.((((=C{DQr[I6n2oAo1"sE _Hcxrć$A3^|V%YQ嚸ͦ|t aɠh1Ev .ƘUŐS?MsG F dudk=Aa9lΧ] R,͹2g{q阪,G3}'7\ (+tg\t!!P:: IDATK;s#L%AƐ9oꆹN,y bY@(5rF;Y\5N!Q$idG4,žpAAAAA?K(ʟjٔu=6[x z2#QYV1mL.P-mo0|UL89mKwuż*p{5qQCG(Oဓ!\wt ?fv/ >SD36m"IV%U!lIf|8hTQlD3T-~X-G8e{4L>oAoK"1Q,ȋSɄɑ%i!Z eo.Q{ʋ$$,EDȲ`PĢZzNGk(*KM/Q'8=ɋYxȠغgt ٴ83/q/@,Dž&"VE B`I9a__M)J)饢7`iAw儏>Lr(XI?-l?S_ds QQPĈqƠ*IiLΠ(0vL8KY$yVUG(m}L5Fl &iFQ .Գ#\PPPP{[nYo8 p\6-MeX\p& D"',!5R^*zs2p~6J)Y\p%'S(կ jъ3+E7 uM=?ǜS(kECFehPR-՘h٧eIz%hX zŽFιj#kH 8'Oo$"IhQj/ƢuGs,E%EPeI7+w}1/X,f|d$FTKJZWOma, :$=9vYm.;Ͳ}$:YkQ L 58b4P'yL,r24%eU3[A Ѩg~Ih1iSshAKe+`*0Y$DS~< ˓Đ% Yh:$<:Gm(( LעQ (T^Wܖ{5Z5ˇ'yBb$ "!> *G90L$/LV@o[gp tHp{ xv8hfmABCEdY$Y BmN 2&dqz  ;/Y*Ȓ6$C-_!Lr  ~JUY-JIKoL_# D`2{~qI$E8;\INTō^yO?K"sᘣqxX&`odXьZGdTV#!ESz=kKHQ0? SKUV Q #{t <]p"1rRy"2 c7g""=Ck>̹T8aEpV\Ւd`CN"}qpTU _X! a֑FVuf}Y3U01hyw&eA9("ѪF5}yI.p4(˒2\2!ږVdu?̾zdHZUFV&(d#DDFO:ـ¦{9_ؠ3>|eQڢ(Ķ$kt7"Lzӓ1M?kǺ&V1㷃"2t͌[.+L Z0μYB\ % Ujhm16'Q$W?9Gd#4%!x]ApILi"cёD%(Fbdз-bFʨ쳑;^'K/bP Y41'2QX%i zƔ^.oUKNTȉ3Hb徴+X/MAn- DR'ryeELU=͌Yӱ%i*Z |1DpĐyJ;vqɕnd&qdB%"QRuDΩ1&J+X#f1+ \PPPPPNsc99Hd٥[pqhe4zY0M0tYFf-FĤ=Q ^?ʔ/igE,Ost;eEe;zN ID lPj Q7kC`tYphkaU_aLK\-bQHk{Ȯzi fy JE#mѯA8YþpAAAAA&=eAN.puAF.5?t0ٳs{#Ky{C`y}w%,Ý-t?$/>_%peSAtm[9wQxgr {d͒8qc>WCl>v{9oo]A` j+?2l[C{4:n {>fu;2g{-|豟r/ 9B{V~OǫQ{G\:1(ՠPSշޒ Ͳֳ|_Ø%I^V5j}],e^{\W5.((((-sKe=tqxV-7Un^-/)}5}l._w'ܾ@`댜[g~S) : jVwbj5L\z4qz3pwЛg߷\Hkr}«}.."$2!ᐢ(n۶·?2yv]1hv'}+8k-[2`_*iڃ7s Zw_%<8~]W0+ \5b>aylxȶnp^ |k /x ٴ~g.r]U܄mAͲo9l }޻_(@YkxjF?7\rmf˲l-\9z8\PPPPKga\n-fNyy nW zuYk˭^5%4g!DŋFp8?+g+p0 pn[k-Ipvv8wMZ.\PPPP! eK%椹޹e}B#7o~p+C:8ᾌ/˶F~)q=([F!KL iv.((((Ve\~@q;`-d=&;W+M>&|mKCU۵\pKy<ܞ=?:m}̸<&u}ڇO~!/:m.t q~cot.8๏96ۼp~Czd^Ժ۶#΃v~|ɇc`ht&~/S$\PPPPMrw=,\RL\2~AAff6%cRGERun?= !Ȳl_Rm^ܸf{Gko/nopi~yʅoĿqq?; qۮ5}#.e /c]15F.((((Bxߘ{{ o]Ξ bY$5jF_YjSb2JHMj[R+\?Z]zɳ"3drN_k拈yrB6(mPmMvղҐ^irDZӵtMü0^$S̒)|jƢ'|;4ҤWM\>2qk"hg<*?*j[uά3d]{fR_NAЗ' &(LZNӚ c̳wzNiE1,4Mf\ _:TU,cʕƹ;3QB$ Deb8#s:Zꅢ7u/h3FUIYQ.JghSDU12R1vs9|Lϋ tF*-)UYP#nN,/D)u/Ju#,~ͷN ^M5 <Ƌ1CBE.(򊴒Ti`8! FBxI 1d!X;x̾])戼!)UURUih\5M( &-% ˶ O\uK=AAo\)D=X)-pೊA">"Y ȕ1MV%Qd0$;+>MZv KtRjgm}*!ËhR*ƣr\"%1iY2#O:C)^θ/E-e<ҏb(hѱ@4B$e^ΗCͰH(&1Y%0B@VSc5bFBv9&^qqA,SsŔv1G2}Gj2`_QF(%+ae:ctpAAAAA/ rlS4f8ò"\AkqfsAo@0G=0-)# >)"GJ G+/STQZQ)R]|ےi-Gـr`EDl$w^ #,R -L<0V?57y/t2?0L+*%) ⓯8RFyLRY(٣t4=ԋ1-g趧YIF"MaA0^Bk F˵Z3S2CQiAʂ/o]]1Q1s S{E2$Z@3g {gHkxe8hp", zgZkt4 (#Rԟ/F9Df *[Ȇih͐,JT_Y|glaYV`ښ~M/ȋc*J1aGUi6}4^B_Ӷ-*>[)T)U.Feف;9^Z]eZEK|Fm"l>fT%$ňBY9{mF4MCdH4*lY" ;R3{?;H//jLJ1! )-bqLFu(b25tor\ނȹ(M>dP(gk+}.hc[t8!#_ƒ~ӟoc$5g{[ѐM/#O'򗮢.uײh%Jhl#rJ܀ 1$>7'%X"g_ls!UeaQU3kA䦗doOݸo1qa2h$!McnUjyIWg%C>qޟ19.9.6 Ɛ &sq3ovGX#_h/|76i9Bل${m;.((((A6{ND7[xul\ixsNƜdjbtfFT92B9qeQERV|< W\ή\|7#~sZ2:MbyŜyFW iq|8J?(wy7.?gTz0$@\ sUѐ}@}^.)9F7]\5$WJoSVO$=>]z9HK.g|U¸_NII i`:Ezd‡2BDQ?cC<\PPPP{89c̿1';YtH8w(01)cS z4l4TՀ<[ϙu G8e\LcƤ mU𱮙%^{mq<AWL8%UGFiIe7*syQ3?3VM2 .唄8d0{HӪ&??wSNG fS.aIEYRhUk>u KGisf')`tGԴ4QJ۽^pAAAAA6)WL&[1(}(B A@r_V`1ٰe`sLaQ -S TXӣz 1:IB #Rm^0u(C nj !ZŌK 5.ZZ/W9SZYF͙u)q[LIH1G8mi3>[?:QtË%ƔGŜ/"d2L4VIł~xG6 :Ew4.Fi \PPPPPo|`so.>nU0m~1n zF Z7\\XdlHtTp:߮]01(d=niRQ=kǾ-SX-]k۵4n}5*hlGnt,Ԧ I/h%1FlBR!^ċh JD#%5hꆅɈd,fM/Zu-`.STm\q|縸sݗ]u5ZG}D7ecC8]fH$y.((((z,[n 7[)IJB1a"J-EzU(odApngfJ. P(Z/փ$^-+/jZE B̐e\J> X] QeJc  el&x IDAT$Y{u%},?Kk>b}/ 5$MM)y' -d݂~r, >2pkx·҃%_?MuV6),˶ZkxI/>ȸH縸?.I//Xߛ{!s }5M=*\PPPPPPPK[b$٤][=D~`>]bp7l+g|?/ſ>O?^vG{ 5 o_ ҰfW6 q[CBJ}ظeir!^6P./~Ca\vc<彻mzpAAAAA/ rO N{<(*2| 7xezy=[+6[vXxS>ȅl\ЛXqeKo[2pAAAAAp $o8~ -)s^C֫6 \15sk}sE͉׺v s7~0&yz@ڤ{]=)/` I)Ճtz#?vAf@>nhy-/[^^/{y-ܼ4(pAAAAA?;x eX.o\gZ Ǜ^AXz 5p Z6R /AA&1x,Iq޷/`p~V5s(ҶNP٧mB_m݋e=M?w3uˆ~0=[XuνA\%IЛfLUb%^c^\龥zl UŇ}ZO/kˡĖύbb3o̠@{}O]ٽ.@]ЋZ^nuvv8Q%g!ěҶ-Zk&ɍ|]}[ҼM&^ c)%wjZ erthkҚm. F\Cwr]IO ΟrԌfv)s-zq)Ig]^}7m lw(8-t7,bBdJYo>4 }mm92͊w?S}' O-e:_6/s%oN?3~ O=m6ξ[erv;3ƬظgIF =_e~&+?Ϡڋل^ ύɁsyO&7>'Kr&IbP\ %k3&v@v́f唽ڶ6l8(E챁81 \WK4MR&˲u z7:/}#ZK$dYFx>mӋ <_Rj=VkYfcd]R/>/nkvFyݼu?O4M= :Աr~V.ẰKoz޷w y%gd'!3ڹd%?qF2(%%}[Sw)Ώ" S̐WL=_ h0}KW7uB%wJ'آĒSZS_N^~ FL%!E\G(>Af2%Bjq8~P;}!Rƃ"TyI `dG#L;'p1\Į'37URDED^89$]J-_Jf6Z/<Km28U D1͜8 (ǟ;V[$bT ʜ,5SqD'Ʒޤmy '|#\\IU)\PPPP{8lq,G:}oA\+D=JdS*q>b*HZ+n鳃31M6aP+ <#=uF ˘dqΗf8'IuݶԚ].V&8sd5ID2IK.j}f{|J"(yIGVTtcޡ;z`Z6 d080*>"ϺX/-=d4}ד,B$$ŀ? &1:9=p|[X- ^p tf䉍b@m+ZvtZ3%i`"kÅ̪ˀޗ[CۖZiE5HCX9 =0 u]~j1JIǘ"&bҢ$jda7[;5$IEs/n_g')L NlaF GU }{}Zkži!UbDsV4Jǔ*!` F|Z猋sgNRFYDj2h:z vb 8@.p 0.F޷4MC ӌc}n{dRfmcu{6K?(mitIrZpj%]- eSeFGDrܾ](`EOK*!@JɠWq*oxm ;E=$!25rA~뚹:!YmX(y_(MOdbQw+쭶w>Lr>V"X6jup.((((B&/@H @n,*G90L$糔h0&. b~4%äXAGDmtXρ1Đd>-%~/9dD4)I)ijhx~sm<79Gl<@$`#rr ޸ih _njTai.~>Ŧ uDQ%Ֆy2 Nʎ(Ir*NQA9,8)q2rYghPPPP ߿M='(M: WG|}RP!Qz@ *aHi-%ej*TG_ۯ|{V1_9_ -(0JadO'5#*;%i\)Dby<鋿>(,CD:nye +'Qţ?gD_CX(dp5L젍7፸x /mFtZ2H}ܸ"C/W(b#DFiXBt'7(*"Lzӓ1M?k9'Z3dB2dX؀~s0 G8Լa,FgL2ȒȮu5}B 1HAnɊ*#J򷃔*}Mק`r+MSGN9堤L,#N[b΅-^d!NÐC0Ĥ1`7zeRDQD3Y¨deCuXј|}MQo."3~b\H)",n=% ,k#L:Lڠ ,ZJzA'>'uГ A*,bGmRTEF;vqɕ !HDBU U.tՌNIQE,U6+6Vaߐ IY(D"&q/Jؒ4%J5IHKA ʜɨdXDd黖zvIץ&8fW=US?޳Mk-)UC*V!jEEgYxȻ˂8\}]ZiY4] `%5M3#RsdP/$m$ɺsmv 12NJڢb R2I"-j6'N;<8unO;QOft's?6g%U"UDhY/B&ſ^q#tO'D4ϸbDX/qeu);4 d^ q^HRzi 7ʡkeQgrR t_0R7*e*'1(Bkdizo`ܵFHhmCXkQJu^$vJ.[sX>.(((((((hAC+`Ǯsn2WRܕA;TFA /+<4 cY0}G|,}|Py"y]^`;?w %^61 d0m+~>}|]T8؇mR^|?S<~ 3{[ X5>>@uXOCT|켕q7U Fa5؟urR^{lҜ ^ edk 9\Axm/5kK\##x-W@-= 'u]}LY$Xkf7Ľ|j`oiRmxqW5EQ JJIu,;鮦. \ueˏ/sڋ ʵַ\PPPPPPP;[6v5~ ?>lKzqKR7;9K5_6Kͥ>>=˦мO/ۀ.((((((((,I)QJaI3y/N>W|I}j7.Oy$Y?OI7_^8~5znLַrs羬s*:͍C-2MJ^n$:ptU`Bp {tm~PK2.J5HPH)e٣<&|D"˲'s]8מܼqpv]GXkIA/I'WHȝϽWkM!t RR*N&e1`ikvμIP/L.ta@S)Y4&*`ueSKft^:^%k:󦵥IN r0&9G霨^S.U|KHe6"/JEO͐s~*x4hhbTe~r𦔢#F!g6t5U<:H@{q=:?򛓜q 0FɞH:&2YJgߴ"bP挫Ԉ'ItFRJj}DudT7ӔDJ2YݗX.(((((((ˇ8)$Sd{A(q2;}ӘKΎ*LâOgȺFׁ!M~A )%]#Xyar+SV%UMiEj?ZKs ˜~RqJ),ǽW:;'5`{Vl&cBKShM^눮!VXRL]X1#(sxzCEiT MQ[ūRX[*ܫJS՗s;EU]FUEER k}  |fA%wdiSX>v!6O2*_4RTnDR}, ϋ &c.\x}}|?"E`!6SqME9~TV̖ܡ+tO[6UqAרlAS% y IDAT֐9[ %jĊq=--*E JSW\rv^R^9}Q/rup  1p:]puc;6}-,@)9椉q3_ҖKq{[2RXB؋;'r֖YX&^|r^S`k.uPbK{!&TIrf1//r*?gME8imKS%uc6Z/Qe}^¶CAocN  +q 5xhý(7km3e^/Z8eYhQmfZd^eKeY^^b3ohĥlضyM[.˯ԇStZ"AAgTW,/˖',2ޛڋ6`\m7)ziFfoM[^v=)mu۴Ťon tM KlC"AAcBeӖ_:8U_z./ۖv{i)UJ_B3_e˦=f&NA5ur*^^t fuU$NAAZ܊-"AAUT+y~#ySzpTUU*lۦ,++^-br=e,"NA;HY$I\v 9Fms^7Nb"l9nQ$IB"AAx8{f(up<7?}9uEl])Ð0 )Wl{;7}ϲ,iMe:msnsv6-n}̶PmPD  |CXyEqE)PJ.J'L[,mMvm!Ҷy% 8#ʲq,ˢ*h(euF)} ,,MsH.ƶ(qm[\׽"ku]\Ŷ6f9nKNmFp  /# F&*)Y5bN)v-lکFpl 7y[lFڂ-۩m1Te#V]헸s|jnl<u)˲))m۬v2qFXmRn[nfdmʜeySUUc8My,{"uSύ8Z7|b= /v,ܳ<,æ2l Ͷn_Ӷk'NAAoeYqVJf+Η9D6#GZkjO}okt&c~_׎8DLt7 `j` q /dYjwoQ ,,)yfAB u{.ȦFHV5ztJ:vF,<+VEGy>8 lƝ?.%ҟQU {V t˄ժjpmBqN?3<A  bTZkހ;!ۣq"+k ]cKɘp8!&жmB il8)OEq }hCZk\Ų+Q71iL%4"|-|';z겉:tV7f(Ղe69Fך4EF~:߹pF/Z7Q3˲Ȳڎ7.˒Bwqݘ>}7C>]MVe5C.ΐ:s8OAJ'?ifmO{X5ASEQPeh!DM "AAg=ʲ,Kc{ytl؉Μ?[;- 3OOѐ7rFYbX!89K9rmԖCltD݀Zg#Ə&<?j:a7PM~wq]8Vt’~:X_FIKe/(ʂ>@ˊl=e6yzyBV>Te[ NFfD9YϨz;g2y{E՘xb^FT{;]|t9'($st6f^c=8dN^J ̎eW.SIKt#9@HUTyʣxm 'pE>H ]޹k1ltEU?{wbBΘ E|%Sp"AA)jM2=}~𣐟 nT~bE(c~8˶\ @EL׍9Vq ?&x04pcu]FVkH(L?߲9'RVW %wzw*-:}TOʛMBhʹe w8qLuȳ%փί>Dkf?zT8 hN2!Qnʁ0WCǶ>f{n{}# ^=I*BZQH7rPzJ29%l|e?A(Ԑw%cyk7B6ʺH_ 쀕a>:Y6U3,g0n7gw(.u[Ǜw TAZxvu"N<⭃{.!uYL4b]49A  \̼kjIo8=KR99挢!~$qVsL,ր;wȋ5eE&z^*c89qs!QcZMyXTH,%*]vH9w؝nFTS0$+:!ҍ ^\L/WHcLHӔzyYN׌V0&IgkPT'$D:J13jFlXvpHW32m() NEg$, );tVSs>N̛w;!~@=?D[ryFɌ"'Sb^ ,O{^hJVؼkGqgHS'naz=AN~&Is,/|GA  5k^}gS$[JtLs).^ 6+ |MwWoަ,HUSZD3>/%q,9JE]vz JWP3ԃ_ӿE%Q[.w_'8uop5ʮ@k o[/ú\pí/V- ꋨ]dck7*>7Nt9GØZaG!3iɒ> R~=A\<"tC߳o {[!權5}lCpȦ$ j>SgdقoZez_FhXSzu.w쀞_Sp?~.(222,)I-4YEQ+X "AA 2Bߣݣ܍8aSuJk*nZEF>am x롳ɒq,"J֋eb&w:tzXeBfTqI>M,Ӕ3Œb}zcY)>u1N*/m ۾\u%ڶEp#:Fe]khPɔ^gOӔZ+"J嚢̰:#?* y,r]BG2&',PdiBEIE\Y 2 yC!jۢKzj M7~rbn[OV9 - Q G 3N2YQi7iD  \SDz0Y=V]y`TɄUnpow@Tu`{J)*˦m0f)|DVtݐNX:Y+yB_3:qo[=a6PfE{ۢrRQV5ѹshG/W4{9µ\%/VZۺܻr(.F.ry}֔؀eTeJRhK>qe*$eu뺤րA=#([y}ctTdyAxYA^-Rb`XiAuXmaۊ Yy˶*ҍHl^k j1eUQ{C;t&vyGﳽ!psq'x8AAoB{3hۂ҉;!Q~Wώ8{w%ve)ZS*VCAC_O4sȕ %y3S(] W(ecʨeVfC !;c\}F?WN/$sָxMY͆kSVAyv]p.gYT~jߏ] 8c;ır,tuNV!AdS(&.ʟUUUC{=.a6#7!_.p㑻 7Zdg1uf5Pt{C5~8aYY#s-Q~L`D^l 䐮O !g ޤzDnM*|4+¶<)AAxu݋9`%iI`񈽠&P'sN?5?3adӄdLwab#:XY pp}߹\XZLPEEQ8k& m3g^,s'"欗)EQ8Sqӵ-$c$46{YvɒŬD YbE.YrttɼZRN,K$N*J]قr`];`{z.tpws|N=t<US%|cV>○2˸wEuRt],H3Nf>a[AJ< 8AVȲ+zJΝŒ.Y-Ŗu/⍵E C8Ș/Y i!'NAAxh1bu sp \Q%真sX1^ %I>ee8W mSl{+t6%^񛳐qnq8X(kJ]н1arP) ]k: XSKǮ[KzH3˲s!ۥ\8a$IAYضR 1[%bYQW9nibYP9ER/&[ )tOCAQc/W9S>N2Ò-/5QA@pyMtXzC'QDN_ͱW~bJ)$K<_<.:ӌUMisv :?,.6=rIUUSf5uB5>aF ,vpMWqzr<|i5)F'  <fF󨪊xzzR^TVU`(|J!gGs8<5˷9OX%+ MYWFs9y3E#zM,>K !s\#eY*ke1g><آ:؞Gh/+tOtsXo8/2Vƹj; |0̈7>J&UiěYx_KӴo݈~otatkkaC9<<AAh?_DQJv:dB$hm["p-9ofY#<' CF&l&_㸙;{]*妀sb8F_q0 ?|"AA^8RA ÿݽ^C5 +Q ·MmQEt:bAQOlr3-s4eZZn6u9R0 Q=aCl=KE  >ZAptdYv/Ф9=Ax5B2 wdr,Kˏmo IDAT%=+niȮV+t:>uUUm 8#ڂѶm\]\.X ~,7p  |y>O|P$¢(Jě |q0){u]GGG, GH\;$ rD޽˽{Q²)Ff"p0>}M  [1RaضZuCy#u]ln7m^S5~wrΝw: yͷ^fD  7>zDk mq>|t:mD֚ pu3m^c6ᐻwrppp4>aG1~e'  wKq5qE ?PJZd2ieY+g PJ1>{{{GCnYև_g8AAAg?ѯ~D|8Ρmیc5Y5852x7?Asԇ?O^eyD  ?ѯ[}`eY$I0 7h8~yϕR'?9ze'  w'Go?4};eY'''k(b{{z;w }Ru(8AAAG}чe1>mfXvy7sh4󼟻J缉AA{{wh6[[[{hVyя~t:/;T  aQdY_VUUڶ}u}ݣnp   |/O˲,>,H)RGt   |y7nX̎&Ƀ?鳣^m(mu}m,˺oADkMYWڒDQDTUEUUu|`YKʵ7myI4{+ˍt8NUUM[4?Kk>W~ZgnW?3?/_-"o6˰S)Tگ]}0< 'ޯqe5ӫvڞu~] 2 C8j;98].os`eTeL&)I.a.UU9Z+׾=,jmȗ?nC/*(4M\*F֣6gks4L)EAYWng}QWŦ0q_;uZZQMvfNI nUn=;@ṠK49[w9xd*N=eeYDQDף~k(ss{m:/Av>8f0.juQtPJm4U˾n+W}wln4eXPq!A46Zuyy7qv)J)<'I0ygʀ׵D&Jl?KUuyߥԶm C`\X,n^oYutg`zfƦVnpl oYY6}-޴ẁ|xS۽S^V`d-"c:u]ZXVu}%5Oӎ:\WJyNQt:''*$ǜPU3Pml5` 9Eqse;TmB6@M9lh^%ж^môv{U ͲQi8 q???oNimhWM9V2^6C&MS$鰻;Cm֚lFUU,K4% &iMyNYt5;#k"珖Gro=jGg,Ss˪q'hu~!vvvw3X.ϔtqLLS<' C8<|ϲp\%˲fpܓPj?=}iDLaUߛ"tZ öOU)sԙ{=(3DEQtc]Z&ϤΙ8Ӥu+ݾ`vgc a=MU΄, ?~bZ7;zl6(F}b8]~j]0ɗ'GU JQaYZU<<]?k ⠠ J)f9'''T]߿OM̈ݻwyo֚S8& C#nfiþ?Ղ"$znmaxe4t abME펧( $aXisoà׮cW;2U63mܿ_O37҈̈^;yk}0}CJ.xGExweMs,XV$I<7rM" "9Ƥ/WF#vvvizu"k4&ENTmffֆ,6J4zv3{63|Kb\+/i{//xZ3< "FQaYՊdjj6>ϳ,#2<ϣﳿh4q[֜cqnp{{{R5|N]ͱ=9`vz8L+2nSnu3O-UjW+LoԵ3DS͠ڶ2f(|̩,(:67I]׬mvn梙$^' moPܼIj+^q6yf5O(ߢ,eSMg:v[EZgNvN>%\>Vb=JW.nk_(\.9;;ѣGT^۶]NCc86Ԉp!*Ldx<\.4pf”ۤIt: FpðQsC;;a?[M{nOo/ѲeOi8hȠiscr3zW5ry=&{ 1a4ޮC̈uih$IX.y .n#Lzy;%=Ƃ4a] Fo{՜MGX_65Ǜz=988hY5>֚$I  m7E|3w]?h>;VAx)S+z}uSD`i7>k= gX <ȆMЮO[UQns]޽KXrÇ<||Dۃ])`08YV,fӲ6i;i3%jsp}{M6mʹ31 qbjb2Z&0b(qp8dww(:>L^)֚4Msxa6SL2l ޴7bQ[;{шn{ yQcqS&qP7ʌHQi66p$WFv{F;[Ď ؚ>X)ϦCwlolwoR-1vɲZGn" q325s(m&$Ό9 ,rjljcqm|κT.AK׊ȧLvDV]` vp=u7F=n)X%.Oqk1N.~}JIjF֫]xI= CܹE۱}tbG5UJ]]*9E\{<"r2>="mU$mj>o Ϸm$L׶Rx)J&~X0"1lmm5k7;60_X9z5|VF̵f U{ѭ1]2Ci u}VmM6RoF<'MSBs/;e'o-]7z2yܹsBgYlʵ~TJ>ՊcVѨڑ$ gggDQ~#BiҐk_$I8 vww t:lfXprrl6kDܙk53 mpv8My7>Lɻw"z[NXLYNJ,kEC 91?cRd]IwG.zN6=&[m6wC_)u:a9>g:h٥+˲ٹul4SrF4׍'SsB7#&V&h6M7MSӧ#T ^e~{|h<ͼtL#3s( No9ee+ò\`q2umeG-Gx֜b$_Ե5uYS.踋o vɂsD~GjcnVP\>;WhD5ي`YgJy:s$)2ڪ{ScݒjU%y~mI x1~i3(rA_>q9'O.0; ;DMfܢ4V9Y3׌(TUIYU袸И$3Nn&YW+VټQHǶKEKT@ IDv^QNnZI\۲,9??oR5s24s;%]Ԏ;t:gEAWDWۙm]7|8mssqP{^qz6Az:' kY5ɤyٯ$[v;L5ݺlIФLM D6 $[OC0`@@I&$f;թ3yvT<5ݷY  ;^k}M?MD(˲@xfC. u'.+(E$yCn&MyJDB-&by5)эꊘuKQm &ݡv$mdz3EM/ƒ}zۢzoʹp c(\cay{_wɿ1R4m C Hwm8ɤ-8!J)...򲍭CoE1ƴz Ʉ$ EQ`dY^/qFfp̓Gc=~ƒkf|qu qQ7!33qcx.v$)>=4F5 n_%C)HU.s_QO+gT6Mh[K)j~VՊjvAmOKPlgT}N}+ >? ~) ׿ʩwMFe8J\&=%AܰZ*יz[^WB\/)py0nogDy2o0Ֆ!ZDq#B` qq m:!B84菔Ga ] 2x@GUfQBrB U]#so*p{n:^s.WP{s?dM㭨%\Q;lA8{F7P=LXȲP#)(-9 Yq^YY^P_鈇F:xxRdW̝P/?Z/s#fŦ\2;E8bNm5m$)jg esbXң#& r .>p8a:5 Q5G7:I;ۛ5F}v¬|cL&Bkg0chCU7aq?47f2z '* tz#eį6 ˜e0"rX#USE#AMstf"B ޠODf 9԰0ciaFjxcw7K٧<&I[U~^ĴʲdٴL+ࣵx48p[2xJ 9b>zDZ]/hV| ِi1 n6Jň#ʜ@(n\>“Q ­ iE>:ME@o8bU62<9Rjլ|8R88??ފi8Y}?*c&2",lp-g<L_\3= j{Os<<3Ւ,VJG=.Rf~aONw-ⱨՙ」( xq˦8R-v\UUx<'Op|ml6i|>gAYf䄓!~}~$a4€u]e YMZƠ1`@=a3OdwH1)?ӄlA1)},(W%!)bK\wZ/1IEu[zmU=m012Wzi#Fa% "~ĵ0C2mʐ 5x4i &] )%uyskc܌lQڡ15F)QNHE5N)c 63* 3V%k8e?ěui6FzwsrlW ~=f&nLOsLG1jCQhT~t< i\rVP/kl /#  T48bFcܴ 詔\6ɤ:tnaiEQ[iX,K,k&{mD$<~>-V(ɶ3]U6+6ynh4d= Hd)_{:)A"+NG>OyfΥ0#H\hElW.k/{daLDv*qB)Rgy///xz-[1KOͲb+l-rҝO*crJگ[.iŧ1UN!$~4 ~ AP`09 ~G]4)(/Sދ_yxL5[UnMcV%$䨇C4F<}\mZUwf B q[oD[~Mrle nPÉ}%ZRT:_zs1[ht4Fh4://f$ތ;GP c& 8%Zlɲq8Bj]/&0BhT;jVję]/swN E&e`tw$ཨE>sqC=nCxaB tY)KַtG| b v ~0 Gٖ` qf$ u-X׬1mF}UCC ՊrPu@ s_2:3p}͚e xKA<<&㘓& UUqyyM;$4tJR Z7uR!uC]X=B'CFPηQ '#8@,(݈0 يU1L|(}zqxNMHɂ$O579ꒆEy ?OU.;kLfY;qrrx<+v;GP~w`4rPՖ2Ϲ)%F5=cƝ7x@IՆ-H3Nq]t΅3ܔf s1qQPK"ْG}&!(V)Mg:RSACQUҙq\1&O2QdI?FWS㣄E- *݀0HGO8 xXI/KBMԵ_97)޺ ]֙Ewfb-˲hY6g粵Pz{?{)z7?Y:!@k#eY[rttD$Az|g\]]3}V1!Z*Ů MӐ<+ O o;!]L!=yq:DESIAL߉31ꚦQ(c0 BI9x6(9r\\q"k! z; AjDWKh UR5LC=c)%[{(d2bwI%J;xQ/dԩqez2a8=I  KgJ>J K|\ `T}p~ĮrG3{C:sR"ŮD"FnhtuGO;W vVK*\271yd)De$+hӰ6Ax*|V U+WrxBzQK Co6€R;`htȾ u 4)]ɠMFV 71j?W[Hݰs8쨹~8 jV2zq2'_]?pG_iR9՟Gg=+k_<-(dWƫI6SoW%vO>8=Fy dQ>*>:qZtMՉ"> P- 9::jg\m)mf8۠iys4R MM@}O{>N'J"Y\+V"Q:X<|%* >#>ӂR8  zUV; 3++\Yc"W4RCY}}Ʒ<Ȁv,2fϞ=c:pݧjiT  = &jEws](u"1 Έ.?fo&,2;Ȓ-5B=?B$J\?NsL8dһá"eݸ/MZ%_V#>脓zĬ|w~Cb*ѪA5j\qY-ɚ]Bmg~7i. }oSQV[٦CE4Mm^6nnn$IЊ;cUme[,w(*,fU2?{S&[,3wM7)M d #nMxp'Lzf$(_nX_+> K6b_74qDOLψDMYR,|O6ےP:܁ bw#)eke~Zf-E;XYh+[ιNMץwiz_1˔|I$$]m1D4 (s..,GFkg0f׽?~ͣqpӘ]gX\G;~Fi6-+\gXY#pp . ] B){и5^ڲ*6ro=E*jꭠp*qɠ' S^#V!ncK,#4I3FhcPfmwȞ6-jU#{v];ɞ'IUhNa*]g&a|ҠRwߑH)! [7[Ւ抋|Fx#F)EL j6Uo yNf vo7oP|{$oEp)6C'hE<x$~$#z.FI|%ޢh~E_PeJvyߍ?FRh2TӰmŊ戾s^G?O;8RoRWhgK$Ėi{' f k\L#  >m_l4\.Y.QaaM'Ikb @+x=,˸d<3N[jjK<'jrFka +Be`7_o6l$"yǭd|駜"%V-R!-dik(]꫊8{m(b[Un8`40{Cz!acimpvA I)0lMkf)]{m;]5$I}p8l<#d]no`n.dp<2ڐ+4LM^NlE%B蝂5&[t*04]7BטgS(5 i\LA쬖M@z' 0F7hv qFQo3_e@6>Z&lbQ,jd26x5PTR7fo{ an7` eU ks/2BRT{=0hwYAMb>$X L/S4=#t]  CdЋ}4eJ tS 42 ]#]!'! Fn(ƺ7;J1$N% 﫼KS\` NИ*4݋{U(p]a)=Fx '''-fɻ4"qpRBR_Ss5 &z4Buc@8AL/[6-y% <*DXƀr z7dϨ b#fl7+!O: }6AL{}X*םyh9 "?l~7#nxX9 R-X wBڰw׋'@BTeFY,DAH&NAcR`4(Xo\6t0>zDԸ=Iu%Ap15R[2\q8ow+ w@$Fޢ0EQsx0M"x1J(p|0a#N)6e@zƂp0BS(P+1&~I'7eJ6hB<{b-+%0vFǺwcX'*hm۪4u*$I3TUAYT[Tʤ\ ;.4e|2 :'H`>=SԽcGZpʑ9S)] Fᐁ 5 ȗP6d)kMa[U۵&)mH gFv%j]U,c a?^&y5F=b_\DYb 1hфoTCz!=d\^[yDo<ݦl#s:{}xhUEܾěEZV[jLD=u58U|p })BmbnyI$ē_[SYg?`y­fWxYI2z|-Lq\߾0dqYP2@NA  'wM~+集y*^W\Vf2VT!oͺcF:.Bkz1( b0C GG1^P;>ޝ}`I U ;&]5v8&cio<{4qttvmY{uGwYܰ^[tp:2 zw? z00NIl6[YaHmA=bp8luK}{N6sss͝.!I`- s;TVdO5ESue< q^g7*Eij' e^bbL8kʲj*e)caC10d \]Ylnsʻ{tC(q ~h⺉Mvr6H#q>@fp(Ր4yʬ(Y-s'!rI5NӌɊfe(#&O$8ԙ4.D (mR"s\]3[\FÐAECY-o8ϟO/ bkՐhQ8&ވc9 ʺ_̸Rcw4m*uf4 0IYl7)uB EOqԚW[xaifu2<~u[<_aB>l~'IBoK-evv<srr 6 咛˝U-@ʰh֚,_,;tL;wttԢp[GHUAnXf]W4XP9{=EïפSfq'Hҡ2d+eU(M]U`yqJo4WC>cz*x5k =A+̏n}xupH]}zmԏvIT[[<y46$ #ǣ1M!B́sΦT+ѡbeFƔ|xk h\]W 6#9tlGivϋMd=r5MU_FB; ghkz`.r|7 .9f#a\$"3>jQ  r#,#&,ɷ9('B4Cn:pđfq˂θkY&- Wb8Ayޖly_H\ujUUzݼE:lxٴbbh;UUEώPm׬LV}"CT 2+B]xF"tŦ9&ZdAM{5+3"ZtV-pФĖuZQnKr l3\s#GPnna)@hZV^dE/Q( EAmo}[Hb'p88q5qjb\>kFlm]UDz,㘇2 β,cbê }fu+bKʒܹeYXzkdm 4M@kVÎpJ)TD[V^1TY>?gK61ذP5Ko6W%9Kǣڤ< JِGD.\fUp#zl]þ]V<{>'P/p]SM{ ^%RJ$#qi E8T!qBo53c0f|AưJ$C=פưm?$ ܘ[#˔Y%C:湕]?-P4q_,Bf>`QB-ˈ>?z;d]Γȃ[SXFhW= E b9eV|cQ[ͷA_2>7wK/8*p p~v$ Z#aecNwjY`n6l7ii + F 4oY h(}PRm;^^ 3At+hXwBEhkXa"oX+BJ "r;9FP 9%sv3 pԮ`1Ȳ 9i4z1,P+)PW*q&Bf8b kHaP@h&tr]kV]f>-pB$Vbv#S}+y_j,'FndYSsz ̚ؽO*N k!H۸CT(TKfRX[}JX!شע5 v}6wgZ+B޽^xܷwm `%M:=U 1Rl Һfdcc86ȕ$] 6`Lܘ$q>ýbylc(3*j!ZR9i8ar]gyJ(U 4pD,/jIg O qt4%= iTCuM]ٟe?ùtX qqݝ_XxJ߶QWaBb=˲d^r_5Q!u]3͸퐢("Vo9zE)[Jl6)Ý[k$MvԽǴT- 뙧&Mɳg#[WH \4r4\%UG]\KdMY*{ٹ"[+q./B6j--fӪ|Cׯ`+7j.˲AAlvl=Ÿ%R/Avi]G7y;]R>uh!]HMAxlÊXY Tge[8lێuJ"j9A%& !i -(uNij6xmg(O{na Z[|e?dža֛zp'&%+yeYVnR*׻!|E }ύ wą|_]RۤքjHS6[Kng׹;{:eݶ,VK_A]׭{Ur9nu]\.sȘE~oW"ro+ZQ쵱5L=6mCrlu<,:ٽXVtCzEk_ֽGf/W]dHﻑv/C}Ex]*a@c(3_ hg̖}4M9??'/9$-sۮ)+I,ַ-ʼn; (>f; t_FQ(ZR*ARU R ;cYݟ;T8z> n|]x݀mt;ſE7QIt+i܍-w7n:05nyձd7>׋~%σ{ v-in1e윾Mx7Y:+80hE Se,3IucBu{}+=~??}οMCqmh]6m(rw=)˲MދhC,Ka ,v ' Xo\dWн\ݢb-؝?`nqkѳy7AVǥW=nrٟޗ.bE!C{Y8F.lMZ*¡ȷ 7 ·6Cj;TW2u@b<.< ʀf]J?uӠ.qq'1 08:Q^x \R2[YlDlD777VQV]oj7yC׽Dt"dzEJ-P]<º6ķCU16ޡo skleTԽg6׈Zby(WYڻΆ[UXD& ڧ‹Cղz|?lJDՂfhͥR{s;*B}>6}uxXg2l̳}\7VٰkkS`s\ +daue׼-4xp2麈PmnP[0S8Ͻi7{9Qy̫kB N>\Ma]ٟw~C&ܾKWǡČ(t0[Kpĭ:1p|qCNw:$ z`t|}hޡPP";E:匏F7*̾)6ۡP& vCn*ֻm6i~Z2_ZON^[tP1 ^5bCi} uHƛ6'6n$>/K_5וۦDWU(>Jz8Ts(p,͐y.u)67o$5g7mBt;xKmWC#}о&ym'y- ׬K n]Ti voػRӅߴgUΘuCS$v;wsbrc`G}_.Y^7+-+`d-v7Ixޤc^E]ޭ/..v  <{<ge9D/<Xy{*vHo̿*Vgq^{QOC?o*Mcobe\gݡ.qۇ]w/¼d,bv'߭}z/.cxZ<$X5A߽~qUj?b}YQֿ,9Ojɳ6?THIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/0000775000175000017500000000000012625666430022277 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/Box.png0000664000175000017500000000674012625666430023544 0ustar fabiofabioPNG  IHDRx#_-gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk> IDATx{Uŝ?}ι3Ν 1!AJ0q7ĊR7kjvW7RX1e`L JKP #8d``^=Ǽ`");}ߣ#Z %y@Yb 6h@F (ٛ)@8A `mg_˞N$1c`)AJK PZaLy>U DB^5knVICU#QJ ) 64s=Ύ}M'f+:Al =dΣ5ceE]w/pM L)f vmd,ـlZB$&A1 ֽ9YX%BaJՓuWD,uG|z\ 2p{* !*}GkMgkyݸn'c}v,'fƓ>Q>wMP[HF3j`j,PRl 49ɶ%a9c`7}7䡣%<b&P RPGh^͋nsQEΟb^{I+:H~|{^E߿_WTzgK,+d_CSN ,}!ʯؾq+mîfh`\=bv̭9seong}ߛ/#yf|u yl(#~~] 3RJ"RqUe8'Ĺf};%X2U1:wxöR*ܓ"5)m[KX~_cmuk>BP\}d)xӕxznz<\,*TW;<1 F~J!} oϟ`SiwV1X{OBRhؒ%Lȡl'MF$.B*"R^덄R H`•ek2,HNWl&SVU?òocGy速Ruo~΂mN36 #FK ll3#IG#-v0)Be] ~<[t~e'Woe[Kh lZY@xʧb?ִ2 :ڒN.cX ym4i?CYgя 㿫e[ݞ_B4QQRiG'[:6Q]ʄGغci<N;ڳϩkfXoմ>keFb\]YmX2TY;yY}#qf+~o ѝJ`$&>O=)7>.Ҫ8s&`sM"eC5֚#u̙PǧFǹonU_k)u.%kp79mBXglOXO)CL2A?%!#SAEaaAt {PE6"KY e@ +DoR!BHL5!%BAlRIT$XQF3j9KݶI|Ŵa|祏2ŕ M88R$XޮkCIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/Adwaita.png0000664000175000017500000000635712625666430024372 0ustar fabiofabioPNG  IHDRx#_-gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk> IDATx{p?{KHl6b&Hȍ" HhS80sSV=i홶Vx3g JGE[RI$@.l d<ϻJOO*-,S-Uua|RT5iHLHĝr(Ts١DBt),(pN\`(HSs6#^+P-*Ph4B7`u 1 v>0 RS, `iljdά9S=ߗ~6@00 V넘O%dqNŊS-eGT DJɱF.@+w3uyi=J 0iOUUf&$7wLפ../ ;dܸ<#\42~V. 4L K}c=e2]qiv-0veŞ =/GHLH) #=ݎ_8~eť$&&^B0 !_43nq+n=;*[?4^yKqq y1ʙͬٴ4\qyX{zZZ{iy~zn\i?1GՂ-!6q{OzzBm[XJ $ iXg%K͋;yq *c@Ql,yx]ҏyi]̄Smbw4 ϽyXFu=?l(d: v<0䃣g6|uzGx< %gN_#V$7(Du5қ'8DT;j1*w p'?dW߻۾j S [wMY5_Ɠo9TSw_oopW6屝^J6 )MNts{[{RHiS<{n)/S؉=,Mdµsxrr]2s@ PNʷ?O`{w4V] p:~oݸGn| Xӹid A2(-Kz/`=X}+B4]twY=w{ /Y{[ yi?ɶ[eՒlvRp08jCaxAJgr?D,LN:E3tsFƦG"W|ooK/ZĈ !a^Q|GnrHӐ$J&ݏ&$dX->)2b(P3=|b6o(&hH7h;7ᦥ$&&bfԤ/EEMkRDXp:14-iiZU^R288ȐfU*X[ɇ&CAy cyI$ Eh=qf&cB(KK]dE\ȥ룪 !CN͛gfY63f@iL@ۃŢbQ,L״" W-QUʝ+2XTTUyK))ĮLL0C}p78Œ.K־W{tykYi7s08۰SY>[,K^]ףkEdpP0nϥǁv*k;h?9^CRRҧ?y;v!6uٸ2}[*ԴpMnVfaˏwFG$,@g$5~!t܋XF!Sf2m{Ǡ\7g_@&rpIDDi;tˢb(8a8 f]i3f̈FE rO̧AZN45.nUE阦Lcfr*!-Dfz{zYyO\ÆjJ -Â=1$Bp8lXHabb* jWV@2dlņajQFHt bOh WdN#lY'/ȺukΚ q9$%%oޮNv_/Ygʎc͚ո\YLn(hF9?>_'_ɦ/o&999.1,#䘄;)ƐG / }=hFpP1A00Z5C>Ce}\^ZAee%+W]E# A>.9TX񮯰t]âP0D]1>6mLrRy\{Mh4QUu\ϲ`BLaƮ OMIeXI9%%{- B!'\:<&)) $8+SON& Q8i3¢E)ZMdCQg~ ,Zg(bu]'wqχnt]nD&22k`&ݽN8r;^Po_/,T2%tg#eM8NIOKtr_Wl6V-b!yę]Qd7;U"&*x㪂9*x㪂9*x~|%tEXtdate:create2014-09-17T15:48:35+02:00T%tEXtdate:modify2014-09-17T15:48:05+02:00(\tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/unknown.png0000664000175000017500000000030612625666430024503 0ustar fabiofabioPNG  IHDRx#_-bKGD pHYs  tIME  2!RSIDATxA0Rd%l*I?[XXX 0````,,, XX )@IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/README0000664000175000017500000000141512625666430023160 0ustar fabiofabio HOW TO MAKE A GTK THEME THUMBNAIL? ---------------------------------- - Install ImageMagick ("apt install imagemagick") - Open a terminal and run "cinnamon-preview-gtk-theme mytheme" (where mytheme is the name of the Theme you want to thumbnail) - Press Alt+Print-Screen to take a screenshot of the window - Go to your Pictures directory and move the screenshot to ~/screenshot.png - Open a terminal and type: cd convert screenshot.png -gravity north-west -extent 120x35 thumbnail.png WHICH THUMBNAIL IS USED BY CINNAMON TO REPRESENT A THEME? --------------------------------------------------------- - Cinnamon first tries to find a gtk-3.0/thumbnail.png file in the theme directory - If it doesn't find any, it looks in /usr/share/cinnamon/thumbnails/gtk-3.0/ Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/gtk-3.0/HighContrast.png0000664000175000017500000000313512625666430025404 0ustar fabiofabioPNG  IHDRx#h>gAMA asRGB cHRMz&u0`:pQ<PLTE9?A.46FKM?EGUWSbd`Y[Wnplvxuݼ~mnk^`\gifxzw⑓prnlmirtpˊL6;TszHK{}ylnjopmL;ZѱN0J}|ZDžB:eb?X|IHrG.xuEGxɊPB{쓔Я}E=_e6OϔR5cuGP͏R?t쥦bda|E:ZhAUoBF?CvhifͭzB4G}іVA]Ԥc;6D͍PAczG>WēWDFAFHQbKGDH pHYsHHFk>IDATXc`HdY YIbb) 4b1'5,M2@`__PrQEbaQ1q I)iaŲ B%)-XQSYEU@M]S^35iU[Gf by9C 9Moili Qj%9[8T&;{N)4sttvquseA!aQ1q x,6IĒ8pJc85-=jq&Y9yE%e؂Z d\T t, pJ`*]Lkj99[ZڱỴ#b~IɜI}݃iqo_?焉&O9m؂zl9scy8V/=,\x eˁY $WB|b`80żS,[]~Mlݶ=s]`b8Xd1$qhVkPQPUQUđ;~gzgN8wK "򕰫׮GᷘSb>'Vnܼ+^1-&"!:_\B\L6jBa^>~#d1jdjXLF;,\dE1],v5#;+YaQ0 ;0%tEXtdate:create2014-09-17T16:00:10+02:00ٍ%tEXtdate:modify2014-09-17T15:58:09+02:00.tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/wallclock.png0000664000175000017500000002730312625666430023702 0ustar fabiofabioPNG  IHDRll pHYs  tIME7 IDATxyurr UaGJWgyd}Ծ/H@D- AHXeٙ_Y`||>O_P Z5k*i \/[pVJӂZ? r/-YgśrW1 Knt[x<^QYp3f{eeeqoI_P◷f|]V)hߍKk׮_O:}mj/SZQ&MSݏ<ȓN8aĈ}Y߾%%ӦM{>Y:m݃۠ }?>+++9[kjj^{7wH$GVpx=8ӾouҥoVhtɒ%>货\ZW)|6gA,H\lo-oMRAk*H:zm~:nOi0ԼWw"AllCmxO %+ǩ.Q)p8{]:'6,oUQ999w6,j]pxn{jkj v}/}YYYXtMms>[nY|cj"K.GgqFnZ=n~(%DQ"JDT gg爨VcĈ;Ҥ+<⢢Tʲe˦=tkjYD_qբ6 ڵ!.}޼/`Deffs17xW3=שfdd1⛇qƒυn "kѩnBP^}UU-jf(%DQ"JD(#K.xॿ%\_kuYQ [=~|ߎpxĈFzBETNnOu( = ?~CCCJ"Go{;ǣh}}}, B?w-[,\9ǒQ"JD(%DJKFAAv{_0C G[oD"A( BpM7t?/77~:3##c[F"H$[PP-ͽg?+,,k:5eo5^D"O>dܹϟaÆΝ:um{^{nr=sss/%%f0"5jjj˷_PxryÇU\|um**+.X .dҺp8WԳnvtA}M+xॗ^zwXvۑyYgmxii9s^z~jժX,QTTȑ?SO9.R)9 ^Dy}=fjr@P(?nܸO?}ag .o)/h?XvvvuOi6mK/&|Go%rw5'tRW_})S}fm] z1c<>}$B$}k;rP(t 67{g!==}#.C9$*+_-ߢ;~.Gm<.Ks9_{oRGcJ|i.s9_;n:4Eҽګ >opUͳ>f͚NN=唄 s1ϓZlذaUWk?ǫ{y3g߿~ =>dȐgW윈9r еkao\.׭[]/\UY{&3LOO=z;s][ ~|L؁uuu]-VhM;SQ^'pS^޴~(%DQ"JDT *U?h4E|ݺu=~׮]ݪgQ;/9֯_<[x|ҥ?=ׯ駟޺1S"K.W}dəgskXr]ҦԣGK/K.Xnݺ?>_{z[Eчy{СM0@D}ϟ?IWuw~vᅳfY[[SL |UUWLkb6mte͞=;yY9-=#<\ .-h4SO]1iRW;V?tЄӕiii[x{ﭪJޥKAH/ҳ9sbŊuv-q%OM̳϶>IW^qƄEEEMzyg'_XQQW\`V3=5mڭݶeddS{ycpɺE-^ubŊ> abVV.# xIIIUUN<`رÆ Ki{79 Zd–ApGѱi?~Ϙ1HCCÔzMn 8o=z$O\hQߎ꒒-zR"p8|O|ƌ.Yƙ766>#^0cǎ-%{;.v;L;TQQq'=昖VJz;6lЖt/-+KD1:whVggfffOHd(Zw JKK۸q?p ˗-[lY~wd+Ƀ?mL.~87[tE#yđ=ǎ֭[Vh[lYte|~cӦM)6pKy¤Z;w߿w}@S훙Vm&VUW܆$E"/늊%%%n$u]sѭ8 v\nn~opdpEW%Oy[m޴ [OMVUUuW۷OczA tI'[sɿVYYymqu `gp&ˣ|&Ap1t=UKi % jj"***iqH4[wԋ/SNy?`zzz>},իW_|ŷzYhll|y'~7դ&9eJJNFP҂ -nV/R!TYYٌ!rssDUUUk>V"*---/Y䜟Gz/k =766uMh/m?4p࿽?99s{6?77ӧOX,Ь}={o?j%yYM]4XYUojll$AVnڵ;wy{}w}W^I<:T6mc%qe]nÆzpxС7p%\?M6Ѥx<ۆ漼;jjjib&+U{sKzV {-'?p> 9{'&7jԨ{ʫz} 閕տ_?+.*j[b#嚼"1 b)UvV'?E#͛7Ϟ=^Xfȑ#7ƌӵk')&L5z&͜9("[v_|y ӧ-ZT^^ 蔗׽G}[vlIP Sj5M]N BMjut;wq7S 6<쳏<ҥKΝ{xqv= `A.^hLQ /L>{>ݣ<%K͛E+WܼiS]]]8==//G2d!C ύ7W/55x+%LllllMeAy}vC>83cUUU>N;m9ɻO>K.|E땗___߬_>U󬬬lrnk֬혈*-+K^Pqqv.+++y5A-^kN-:|կ7qDmRWWwwpIOMVQQі\jԩ'|C? chڵ v-鰈۰q_wVGn֭_E-Z(azFFƏO;a hX,sO<ݷrժfM,Xpm?K.;wn~NwTVV~bElNUUƤ޽[{,SN kjk76# U]] 7޸p¶+WN<9ƾ}~C̙3wݺ3+_ʨQשSp8 bƺHdƍKJ/^lٲ?x5HSON5kt̆~}IH}re7xۙ6mT]]Uz'Թs鯿]***ω"%a0}կZL]]ٳa'GjtPx<o$_L؄r.!ņYgp*=36m+UI4(UOI #tsQEE7 Qh*yz޽Sx, u-yeeemBE\vezJzzh|__r9s&fffOlݻw?{Ko.p/6oN3cƌIoVIDAT>rs0~](:Nvv矟|!%>SSSO$ߋ5v Ƴ=p~|h'oz .LÆ mT]wuذa ˗h D%++383,XGى1k֢EW0`@[ Fhov/#8߯_E=wz!w򤥥M6*//zoC''O3իפ+8߼ymW^y=ǎM>\u\zi(޽_~!$e,{g>1#ddd6n?zԨ&b/L~ՕWV' /|Guԉ'<}oV"*--ow}'pff)?n\qK,~B=FUW}k_K.X,裏2㕖niiiM7<;ΉAp!?u5^;s̆f0W.?y=,Yr)[DT; SNiiiAڿk ffdt-,llںvڐN:cرt٧x[Dѧy//۲VF{8lܸBo|><'\fMBPWq <㌢~onhՍFoxA >oy䑅 V5\dee 8pĉ'pB_c sZ55cI|]V)ho;f̘>33cРA ߡ+*+W\ٜ;Wn'xbl癙YYY999zӧOۿWSS3m97|6DsK.moҷo)w16¹BhÆ oٳ?X,7xӧO/^|ƙg._£?CV$;ww]lY$ihh陙˞{{ン;wnr&7t-ޚ| V[? RlςvYJ[gݱ [ޞpm;RTu >uܞhӪaAAG-y/ cpEf}j VןAJV$hϏS[3]LTC6rVϡK{Gs~{nAeArrr>_炚s'pO?]__|!ߑUVr˘ѣ7*ch1+++ւ:/_ޖu+++Kn8~ᄆX,TVVVUUEѼΝ;S z;s@ K6######/P(u\ee域|O|'}N>:sfΜBp8+++77w~obz̳Zxqm\x3flg pnnnQQڻwSwuoODʖŗ^:SϿQWXqO:宻QsީS:e˖j֬Ysyvu뮘4i555sr/ʞ={6痣_}~s#Ɩ^}oօ\0ž̘[^}h4k+**&_woqy>999UUUoqE  H6lfz뭷֭_;RvC4+;Ǐ'1bD^^_U]]hѢG}/?_^^NF_ 2ӻu֫W‚p(mٲenذa뛕a"JD(%DQ"JDGTG}<ۥ;|Q"JD(%D9et> DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQ( DQZM+IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/0000775000175000017500000000000012625666430023171 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Atlanta.png0000664000175000017500000000125712625666430025270 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<~PLTE!!!֦zzz)))000***~}}| ubKGD- pHYsHHFk>IDATHǽk aNXieivhreF9BF#Jd()nݹ,P>—P< kiHd{ǐ@rS "eHd N〫rDFL ŞϡK~J ײH}v$InEH׋>\wH{OM i^ey J(#XIH+7i6ig %tEXtdate:create2014-09-17T14:48:56+02:00E)g%tEXtdate:modify2014-09-17T14:48:53+02:00 |tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Crux.png0000664000175000017500000000203112625666430024614 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<DPLTEpppjjjeeeaaa___^^^RRRװ\\\BBBܸ˥WWW888ěTTT222ԩSSS000ФQQQ///}}}OOO...~~~MMM---JJJ+++HHHFFFEEE<<<'''&&&ŝvvv9AbKGDi pHYsHHFk>IDATHǵGS@aP@`E%XA)"؂bGH89df y l6.đәq#(Oo~U>?H@`0¡Ȱ 4DcxbGFAwdjbGLC͘ lzG,)|1 %..-ȊUC9>ِȆ,PmCP,KM؂U '6TIށK;] ؃ʾ֠b wqH14MC5pF4CuD- :pE!I"WozjԕCټH$}DTIQ'@"/r*$жW@IM*yG>OxK*G~_X C%tEXtdate:create2014-09-17T14:45:26+02:00~%tEXtdate:modify2014-09-17T14:45:19+02:00 dxtEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Box.png0000664000175000017500000000076412625666430024436 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<3PLTEHHHIIIJJJ VbKGD- pHYsHHFk>IDATH @ Q;ڣO[֪   c#F߉D.'d"wY5~#\ښɌxHHty$8JVs*5930>x1+Rky:%tEXtdate:create2014-09-17T14:44:14+02:00:%tEXtdate:modify2014-09-17T14:44:06+02:001tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Esco.png0000664000175000017500000000304412625666430024571 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<PLTE!!!֚|ݳٮժݽ̻Ѧ۽ںӷ͢ٺ׷ֵ'xQ% #OwB I۫xM-$Cnњd; +CnɞַԴӲ qM4@:% H}$8DD<1#%ŚӴЯ!q:hD-s!v׆)tNkбͫvelt"z7;>&9Pmֿ)l%s!zKhoooooovЦcL;- 5Ȩʬr_äG S򟵊ťҺǩJ )=EA6(7$1,IDATHc``=```=```&Pl-a'ppr(- x*A4іH@ED%4DFVN^AQIYEUM]YSKUGWYf1Nd TDT\̇.K+ amckl '@&+$o-  cdgMHLJNIMKĝY`aAy`K KJ˘+*٢fI#NW3 hIwOooo_&Oa: ft kL!Y<4K̝7BE,]uUaDE`4_ \tK6nb޼e;Zwڽy>%a \d?::2=rG'N:}9f `\%pK^RW_ CLЍkw@4і%ܻDmC і<]i=-yBcl [*`K@,yF[@GK@,yA[@GK^@,yE[V8a%tEXtdate:create2014-09-17T14:49:54+02:00Sp%tEXtdate:modify2014-09-17T14:49:51+02:00}ktEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Adwaita.png0000664000175000017500000000117312625666430025253 0ustar fabiofabioPNG  IHDRd +.bKGD pHYs  tIME  .tEXtCommentCreated with GIMPWIDATh1o`u33B eBL+"P5HA-W mǐUv{dIWɲ#>߇8!j)8Fo_$2HIvKZR.\Yڵ!5;A`UaHt fH0Cf 130C̐sO|xJ|j=@77O}al<{IIDATHǽRPߩEDD *b%!]T|>7Y0>cv3atL1Di\V׎y$g$yOaю%GBQŠ֤6(ۑXp*/P##ݽ~};Q؎FSZə`gxoRw}>tK|Kȏ>՗Α?`H\0,%tEXtdate:create2014-09-17T14:51:50+02:00W%tEXtdate:modify2014-09-17T14:51:45+02:00LtEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/unknown.png0000664000175000017500000000132412625666430025376 0ustar fabiofabioPNG  IHDRd +.bKGD pHYs  tIME   d;ltEXtCommentCreated with GIMPWp$]rK9\J!uɓbHuR9?Ki2v`c5~"Z `d:?ߖ.`AJ%8Bm[00ΜGف \t˩ZkϝvG*'Z.}LQR BꭨCRƉ܇qi<k"@jWo(kolw؀,%(NX>-rKq:9bp1J)pοCsIpS4Ƙsf4_.NSjH_9Atɝ\AJkX`ҲB=He4D@! ZdyོIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Simple.png0000664000175000017500000000124512625666430025132 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<PLTE!!!=I1kkkp|ЄnnnBBBttt%%%000XXXeee,,,jjj]]]LLLij111CCCGGG-sċbKGD- pHYsHHFk>IDATH0 +2 *ų5@fe[¾klcp;wq% I($6*w$H5Ȓ8J&dI3%%Q\O"j. \mkҜmU.$쮦daYI8b)YrXi]/H[ 챜&_gj?1_%fPreƞ JؓIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/README0000664000175000017500000000126712625666430024057 0ustar fabiofabio HOW TO MAKE A METACITY THUMBNAIL? --------------------------------- - Install ImageMagick ("apt install imagemagick") - Open a window (Nemo for instance) - Press Alt+Print-Screen to take a screenshot of the window - Go to your Pictures directory and move the screenshot to ~/screenshot.png - Open a terminal and type: cd convert screenshot.png -gravity north-east -extent 100x32 thumbnail.png WHICH THUMBNAIL IS USED BY CINNAMON TO REPRESENT A THEME? --------------------------------------------------------- - Cinnamon first tries to find a metacity-1/thumbnail.png file in the theme directory - If it doesn't find any, it looks in /usr/share/cinnamon/thumbnails/metacity-1/ Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/HighContrast.png0000664000175000017500000000131112625666430026270 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<PLTE uuuYYYbbbhhh !!!222///&&&''',,,***000)))%%% ...###iii555 444(((pppObKGD5޶k pHYsHHFk>IDATH0:uVR(UyoH6An^ TTkul }x.MtP+!DD Esj1gP怤gҰyL^ǥB"E nʤ^ B \ogEi+,2޾cÍwΜUV$s4P"> <G'/1SO\)Þ%tEXtdate:create2014-09-17T15:04:08+02:00%tEXtdate:modify2014-09-17T15:04:00+02:00[ tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/AgingGorilla.png0000664000175000017500000000422712625666430026243 0ustar fabiofabioPNG  IHDRd +.gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>gIDATh[lS?8vnv$RĠ2[UljҤIq2:6کn4B DZߎC @²S]]ͥKPX l8xJ ~̙رcw׷lٲ3pr]]]=#IOOϢ4п7{Go^y/etxضTUֱ57}&\Эloa66Jg8÷9GTL$ =#?F ׷tH6d|d<-ɾZuA UHf%qLE ::7dǨu aNITŞR ?M!P@4 M ɺfcZBnMS1{^Cy\զ`@k_B27Wɐpi78 u!m[ZBp@ؐL A<1PG1%+ZԆQz{$>[C\%CZYT7A.[ZCzϻ,\D~wOd5?SȠ>43Qۅ=%WL0|wB_ T+PEBlv)uue붗ij9WH)4#Div[C"Fz%0 3)I p7OSRp!foR!i:U vѻۑ%4 tmĮڮдxC6]U47_rgpGt۩Si͟ITB8ᶝkэ sXd:%K֚s \U}g׬C* 2ydWJ=F{8˿J*>LtpIW@;8Mҁ7/yTQP*L} N|f0#IΖ]eI'n$ '(8 f]m E:Y\G07~wrT:O,^U %ݿNN:ů^c*-u >clH2``lw. ;Uw}N8QKJp=eC" CQ&&&XxŶpq0j!TZbˎ1d j9EץeZl@Pw[o{nPpƧKr؜d:XC9+BƵTC6[s,|>mx_yvй Ou U,um;}ogXt&+<8l)%tEXtdate:create2014-09-17T14:37:21+02:00P%tEXtdate:modify2014-09-17T14:31:39+02:00itEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/thumbnails/metacity-1/Bright.png0000664000175000017500000000107212625666430025116 0ustar fabiofabioPNG  IHDRd -gAMA asRGB cHRMz&u0`:pQ<EPLTE!!!֚|444)))000***'''+++(((...bKGD- pHYsHHFk>IDATH0 (ŚAu #hd'=_!@3Er*ہͬ nX:FݙTquǨMyqol 0(bܵHUr"! 恷W"~Xz/RLb3+V6?Y~W?c3p4Nh"o97av%tEXtdate:create2014-09-17T14:48:14+02:006%tEXtdate:modify2014-09-17T14:48:08+02:00tEXtSoftwaregnome-screenshot>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/0000775000175000017500000000000012625666430023076 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cinnamon-menu-editor.ui0000664000175000017500000006174012625666430027475 0ustar fabiofabio gtk-cut edit_cut gtk-copy edit_copy gtk-paste edit_paste gtk-properties edit_properties gtk-delete edit_delete 5 675 530 True Main Menu GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER False True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True False True False 2 True GTK_BUTTONBOX_END True Restore the default menu layout True True Restore System Configuration True GTK_RELIEF_NORMAL True True True True True gtk-close True GTK_RELIEF_NORMAL True 0 False True GTK_PACK_END 5 True 0.5 0.5 1 1 0 0 0 0 False 6 True True 200 True False 6 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True False False False True False False False 0 True True True False True False 6 True False 6 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True True False True False False False 0 True True True False 6 True GTK_BUTTONBOX_START 6 _New Menu True True True True GTK_RELIEF_NORMAL True new_menu_image Ne_w Item True True True True GTK_RELIEF_NORMAL True new_item_image 0 False True True 0.5 0.5 1 1 12 0 0 0 True GTK_BUTTONBOX_START 6 gtk-cut True True True True False False 0 gtk-copy True True True True False False 1 gtk-paste True True True True False False 2 gtk-delete True True True True False False 3 gtk-properties True True True True False False 4 0 True True 0 False True 0 True True True True 0 True True 0 True True restore_button close_button True gtk-new True gtk-add True gtk-go-down True gtk-go-up Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/0000775000175000017500000000000012625666430023642 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/MainWindow.py0000664000175000017500000005224312625666430026276 0ustar fabiofabio# -*- coding: utf-8 -*- # vim: set noexpandtab: # Alacarte Menu Editor - Simple fd.o Compliant Menu Editor # Copyright (C) 2006 Travis Watkins # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA from gi.repository import Gtk, GObject, Gio, GdkPixbuf, Gdk, CMenu, GLib import cgi import os import gettext import subprocess import shutil from cme import config gettext.bindtextdomain(config.GETTEXT_PACKAGE, config.localedir) gettext.textdomain(config.GETTEXT_PACKAGE) _ = gettext.gettext from cme.MenuEditor import MenuEditor from cme import util class MainWindow(object): timer = None #hack to make editing menu properties work edit_pool = [] def __init__(self, datadir, version): self.file_path = datadir self.version = version self.editor = MenuEditor() self.editor.tree.connect("changed", self.menuChanged) Gtk.Window.set_default_icon_name('alacarte') self.tree = Gtk.Builder() self.tree.set_translation_domain(config.GETTEXT_PACKAGE) self.tree.add_from_file('/usr/share/cinnamon/cinnamon-menu-editor/cinnamon-menu-editor.ui') self.tree.connect_signals(self) self.setupMenuTree() self.setupItemTree() self.tree.get_object('edit_delete').set_sensitive(False) self.tree.get_object('edit_properties').set_sensitive(False) self.tree.get_object('edit_cut').set_sensitive(False) self.tree.get_object('edit_copy').set_sensitive(False) self.tree.get_object('edit_paste').set_sensitive(False) self.cut_copy_buffer = None self.file_id = None self.last_tree = None self.main_window = self.tree.get_object('mainwindow') def run(self): self.loadMenus() self.tree.get_object('mainwindow').show_all() Gtk.main() def menuChanged(self, *a): self.loadUpdates() def loadUpdates(self): menu_tree = self.tree.get_object('menu_tree') item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() update_items = False update_type = None item_id = None if iter: update_items = True if isinstance(items[iter][3], CMenu.TreeEntry): item_id = items[iter][3].get_desktop_file_id() update_type = CMenu.TreeItemType.ENTRY elif isinstance(items[iter][3], CMenu.TreeDirectory): item_id = os.path.split(items[iter][3].get_desktop_file_path())[1] update_type = CMenu.TreeItemType.DIRECTORY elif isinstance(items[iter][3], CMenu.TreeSeparator): item_id = items.get_path(iter) update_type = CMenu.TreeItemType.SEPARATOR menus, iter = menu_tree.get_selection().get_selected() update_menus = False menu_id = None if iter: if menus[iter][3].get_desktop_file_path(): menu_id = os.path.split(menus[iter][3].get_desktop_file_path())[1] else: menu_id = menus[iter][3].get_menu_id() update_menus = True self.loadMenus() #find current menu in new tree if update_menus: menu_tree.get_model().foreach(self.findMenu, menu_id) menus, iter = menu_tree.get_selection().get_selected() if iter: self.on_menu_tree_cursor_changed(menu_tree) #find current item in new list if update_items: i = 0 for item in item_tree.get_model(): found = False if update_type != CMenu.TreeItemType.SEPARATOR: if isinstance (item[3], CMenu.TreeEntry) and item[3].get_desktop_file_id() == item_id: found = True if isinstance (item[3], CMenu.TreeDirectory) and item[3].get_desktop_file_path() and update_type == CMenu.TreeItemType.DIRECTORY: if os.path.split(item[3].get_desktop_file_path())[1] == item_id: found = True if isinstance(item[3], CMenu.TreeSeparator): if not isinstance(item_id, tuple): #we may not skip the increment via "continue" i += 1 continue #separators have no id, have to find them manually #probably won't work with two separators together if (item_id[0] - 1,) == (i,): found = True elif (item_id[0] + 1,) == (i,): found = True elif (item_id[0],) == (i,): found = True if found: item_tree.get_selection().select_path((i,)) self.on_item_tree_cursor_changed(item_tree) break i += 1 return False def findMenu(self, menus, path, iter, menu_id): if not menus[path][3].get_desktop_file_path(): if menu_id == menus[path][3].get_menu_id(): menu_tree = self.tree.get_object('menu_tree') menu_tree.expand_to_path(path) menu_tree.get_selection().select_path(path) return True return False if os.path.split(menus[path][3].get_desktop_file_path())[1] == menu_id: menu_tree = self.tree.get_object('menu_tree') menu_tree.expand_to_path(path) menu_tree.get_selection().select_path(path) return True def setupMenuTree(self): self.menu_store = Gtk.TreeStore(object, str, bool, object) # bool is unused, just a placeholder menus = self.tree.get_object('menu_tree') # so object is the same index for column = Gtk.TreeViewColumn(_("Name")) # the menu tree and item tree column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) column.set_cell_data_func(cell, self.icon_data_func, 0) cell = Gtk.CellRendererText() column.pack_start(cell, True) column.add_attribute(cell, 'markup', 1) menus.append_column(column) menus.get_selection().set_mode(Gtk.SelectionMode.BROWSE) def setupItemTree(self): items = self.tree.get_object('item_tree') column = Gtk.TreeViewColumn(_("Show")) cell = Gtk.CellRendererToggle() cell.connect('toggled', self.on_item_tree_show_toggled) column.pack_start(cell, True) column.add_attribute(cell, 'active', 0) #hide toggle for separators column.set_cell_data_func(cell, self._cell_data_toggle_func) items.append_column(column) column = Gtk.TreeViewColumn(_("Item")) column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) column.set_cell_data_func(cell, self.icon_data_func, 1) cell = Gtk.CellRendererText() column.pack_start(cell, True) column.add_attribute(cell, 'markup', 2) items.append_column(column) self.item_store = Gtk.ListStore(bool, object, str, object) items.set_model(self.item_store) def icon_data_func(self, column, cell, model, iter, data=None): wrapper = model.get_value(iter, data) if wrapper: cell.set_property("surface", wrapper.surface) def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter, data=None): if isinstance(model[treeiter][3], CMenu.TreeSeparator): renderer.set_property('visible', False) else: renderer.set_property('visible', True) def loadMenus(self): self.menu_store.clear() self.loadMenu({ None: None }) menu_tree = self.tree.get_object('menu_tree') menu_tree.set_model(self.menu_store) for menu in self.menu_store: menu_tree.expand_to_path(menu.path) menu_tree.get_selection().select_path((0,)) self.on_menu_tree_cursor_changed(menu_tree) def loadMenu(self, iters, parent=None): for menu, show in self.editor.getMenus(parent): name = cgi.escape(menu.get_name()) if not show: name = "%s" % (name,) icon = util.getIcon(menu, self.main_window) iters[menu] = self.menu_store.append(iters[parent], (icon, name, False, menu)) self.loadMenu(iters, menu) def loadItems(self, menu): self.item_store.clear() for item, show in self.editor.getItems(menu): icon = util.getIcon(item, self.main_window) if isinstance(item, CMenu.TreeDirectory): name = item.get_name() elif isinstance(item, CMenu.TreeEntry): name = item.get_app_info().get_display_name() elif isinstance(item, CMenu.TreeSeparator): name = '---' else: assert False, 'should not be reached' name = cgi.escape(name) if not show: name = "%s" % (name,) self.item_store.append((show, icon, name, item)) #this is a little timeout callback to insert new items after #gnome-desktop-item-edit has finished running def waitForNewItemProcess(self, process, parent_id, file_path): if process.poll() is not None: if os.path.isfile(file_path): self.editor.insertExternalItem(os.path.split(file_path)[1], parent_id) return False return True def waitForNewMenuProcess(self, process, parent_id, file_path): if process.poll() is not None: if os.path.isfile(file_path): self.editor.insertExternalMenu(os.path.split(file_path)[1], parent_id) return False return True #this callback keeps you from editing the same item twice def waitForEditProcess(self, process, file_path): if process.poll() is not None: self.edit_pool.remove(file_path) return False return True def on_new_menu_button_clicked(self, button): menu_tree = self.tree.get_object('menu_tree') menus, iter = menu_tree.get_selection().get_selected() if not iter: parent = menus[(0,)][3] menu_tree.expand_to_path((0,)) menu_tree.get_selection().select_path((0,)) else: parent = menus[iter][3] file_path = os.path.join(util.getUserDirectoryPath(), util.getUniqueFileId('alacarte-made', '.directory')) process = subprocess.Popen(['cinnamon-desktop-editor', '-mdirectory', '-o' + file_path], env=os.environ) GObject.timeout_add(100, self.waitForNewMenuProcess, process, parent.get_menu_id(), file_path) def on_new_item_button_clicked(self, button): menu_tree = self.tree.get_object('menu_tree') menus, iter = menu_tree.get_selection().get_selected() if not iter: parent = menus[(0,)][3] menu_tree.expand_to_path((0,)) menu_tree.get_selection().select_path((0,)) else: parent = menus[iter][3] file_path = os.path.join(util.getUserItemPath(), util.getUniqueFileId('alacarte-made', '.desktop')) process = subprocess.Popen(['cinnamon-desktop-editor', '-mlauncher', '-o' + file_path], env=os.environ) GObject.timeout_add(100, self.waitForNewItemProcess, process, parent.get_menu_id(), file_path) def on_edit_delete_activate(self, menu): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() if not iter: return item = items[iter][3] if isinstance(item, CMenu.TreeEntry): self.editor.deleteItem(item) elif isinstance(item, CMenu.TreeDirectory): self.editor.deleteMenu(item) elif isinstance(item, CMenu.TreeSeparator): self.editor.deleteSeparator(item) def on_edit_properties_activate(self, menu): item_tree = self.tree.get_object(self.last_tree) items, iter = item_tree.get_selection().get_selected() if not iter: return item = items[iter][3] if not isinstance(item, CMenu.TreeEntry) and not isinstance(item, CMenu.TreeDirectory): return if isinstance(item, CMenu.TreeEntry): file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id()) file_type = 'launcher' elif isinstance(item, CMenu.TreeDirectory): file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1]) file_type = 'directory' if not os.path.isfile(file_path): shutil.copy(item.get_desktop_file_path(), file_path) if file_path not in self.edit_pool: self.edit_pool.append(file_path) process = subprocess.Popen(['cinnamon-desktop-editor', '-m' + file_type, '-o' + file_path], env=os.environ) GObject.timeout_add(100, self.waitForEditProcess, process, file_path) def on_edit_cut_activate(self, menu): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() item = items[iter][3] if not iter: return if not isinstance(item, CMenu.TreeEntry): return (self.cut_copy_buffer, self.file_id) = self.editor.cutItem(item) def on_edit_copy_activate(self, menu): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() item = items[iter][3] if not iter: return if not isinstance(item, CMenu.TreeEntry): return (self.cut_copy_buffer, self.file_id) = self.editor.copyItem(item) def on_edit_paste_activate(self, menu): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() if not iter: menu_tree = self.tree.get_object('menu_tree') items, iter = menu_tree.get_selection().get_selected() if not iter: return item = items[iter][3] if not isinstance(item, CMenu.TreeDirectory): return if self.cut_copy_buffer is not None: success = self.editor.pasteItem(self.cut_copy_buffer, item, self.file_id) if success: self.cut_copy_buffer = None self.file_id = None def on_menu_tree_cursor_changed(self, treeview): selection = treeview.get_selection() if selection is None: return menus, iter = selection.get_selected() if iter is None: return menu = menus[iter][3] menu_path = menus.get_path(iter) item_tree = self.tree.get_object('item_tree') item_tree.get_selection().unselect_all() self.loadItems(self.menu_store[menu_path][3]) self.tree.get_object('edit_delete').set_sensitive(False) self.tree.get_object('edit_properties').set_sensitive(True) self.tree.get_object('edit_cut').set_sensitive(False) self.tree.get_object('edit_copy').set_sensitive(False) self.tree.get_object('properties_button').set_sensitive(True) self.tree.get_object('delete_button').set_sensitive(False) self.tree.get_object('cut_button').set_sensitive(False) self.tree.get_object('copy_button').set_sensitive(False) can_paste = isinstance(menu, CMenu.TreeDirectory) and self.cut_copy_buffer is not None self.tree.get_object('edit_paste').set_sensitive(can_paste) self.tree.get_object('paste_button').set_sensitive(can_paste) index = menus.get_path(iter).get_indices()[menus.get_path(iter).get_depth() - 1] parent_iter = menus.iter_parent(iter) count = menus.iter_n_children(parent_iter) can_go_up = index > 0 and isinstance(menu, CMenu.TreeDirectory) can_go_down = index < count - 1 and isinstance(menu, CMenu.TreeDirectory) self.last_tree = "menu_tree" def on_item_tree_show_toggled(self, cell, path): item = self.item_store[path][3] if isinstance(item, CMenu.TreeSeparator): return if self.item_store[path][0]: self.editor.setVisible(item, False) else: self.editor.setVisible(item, True) self.item_store[path][0] = not self.item_store[path][0] def on_item_tree_cursor_changed(self, treeview): selection = treeview.get_selection() if selection is None: return items, iter = selection.get_selected() if iter is None: return item = items[iter][3] self.tree.get_object('edit_delete').set_sensitive(True) self.tree.get_object('delete_button').set_sensitive(True) can_edit = not isinstance(item, CMenu.TreeSeparator) self.tree.get_object('edit_properties').set_sensitive(can_edit) self.tree.get_object('properties_button').set_sensitive(can_edit) can_cut_copy = not isinstance(item, CMenu.TreeDirectory) self.tree.get_object('cut_button').set_sensitive(can_cut_copy) self.tree.get_object('copy_button').set_sensitive(can_cut_copy) self.tree.get_object('edit_cut').set_sensitive(can_cut_copy) self.tree.get_object('edit_copy').set_sensitive(can_cut_copy) can_paste = isinstance(item, CMenu.TreeDirectory) and self.cut_copy_buffer is not None self.tree.get_object('edit_paste').set_sensitive(can_paste) self.tree.get_object('paste_button').set_sensitive(can_paste) index = items.get_path(iter).get_indices()[0] can_go_up = index > 0 and isinstance(item, CMenu.TreeDirectory) can_go_down = index < len(items) - 1 and isinstance(item, CMenu.TreeDirectory) self.last_tree = "item_tree" def on_item_tree_row_activated(self, treeview, path, column): self.on_edit_properties_activate(None) def on_item_tree_popup_menu(self, item_tree, event=None): model, iter = item_tree.get_selection().get_selected() if event: #don't show if it's not the right mouse button if event.button != 3: return button = event.button event_time = event.time info = item_tree.get_path_at_pos(int(event.x), int(event.y)) if info is not None: path, col, cellx, celly = info item_tree.grab_focus() item_tree.set_cursor(path, col, 0) else: path = model.get_path(iter) button = 0 event_time = 0 item_tree.grab_focus() item_tree.set_cursor(path, item_tree.get_columns()[0], 0) popup = self.tree.get_object('edit_menu') popup.popup(None, None, None, None, button, event_time) #without this shift-f10 won't work return True def on_menu_tree_popup_menu(self, menu_tree, event=None): model, iter = menu_tree.get_selection().get_selected() if event: #don't show if it's not the right mouse button if event.button != 3: return button = event.button event_time = event.time info = menu_tree.get_path_at_pos(int(event.x), int(event.y)) if info is not None: path, col, cellx, celly = info menu_tree.grab_focus() menu_tree.set_cursor(path, col, 0) else: path = model.get_path(iter) button = 0 event_time = 0 menu_tree.grab_focus() menu_tree.set_cursor(path, menu_tree.get_columns()[0], 0) popup = self.tree.get_object('edit_menu') popup.popup(None, None, None, None, button, event_time) #without this shift-f10 won't work return True def on_item_tree_key_press_event(self, item_tree, event): if event.keyval == Gdk.KEY_Delete: self.on_edit_delete_activate(item_tree) def on_restore_button_clicked(self, button): self.editor.restoreToSystem() def on_close_button_clicked(self, button): self.quit() def on_properties_button_clicked(self, button): self.on_edit_properties_activate(None) def on_delete_button_clicked(self, button): self.on_edit_delete_activate(None) def on_cut_button_clicked(self, button): self.on_edit_cut_activate(None) def on_copy_button_clicked(self, button): self.on_edit_copy_activate(None) def on_paste_button_clicked(self, button): self.on_edit_paste_activate(None) def quit(self): Gtk.main_quit() Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/util.py0000664000175000017500000001431512625666430025175 0ustar fabiofabio# -*- coding: utf-8 -*- # Alacarte Menu Editor - Simple fd.o Compliant Menu Editor # Copyright (C) 2006 Travis Watkins # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA import os import xml.dom.minidom import uuid from collections import Sequence from gi.repository import Gtk, GdkPixbuf, CMenu, GLib, Gdk DESKTOP_GROUP = GLib.KEY_FILE_DESKTOP_GROUP KEY_FILE_FLAGS = GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS def fillKeyFile(keyfile, items): for key, item in items.iteritems(): if item is None: continue if isinstance(item, bool): keyfile.set_boolean(DESKTOP_GROUP, key, item) elif isinstance(item, basestring): keyfile.set_string(DESKTOP_GROUP, key, item) elif isinstance(item, Sequence): keyfile.set_string_list(DESKTOP_GROUP, key, item) def getNameFromKeyFile(keyfile): return keyfile.get_string(DESKTOP_GROUP, "Name") def getUniqueFileId(name, extension): while 1: filename = name + '-' + str(uuid.uuid1()) + extension if extension == '.desktop': path = getUserItemPath() if not os.path.isfile(os.path.join(path, filename)) and not getItemPath(filename): break elif extension == '.directory': path = getUserDirectoryPath() if not os.path.isfile(os.path.join(path, filename)) and not getDirectoryPath(filename): break return filename def getUniqueRedoFile(filepath): while 1: new_filepath = filepath + '.redo-' + str(uuid.uuid1()) if not os.path.isfile(new_filepath): break return new_filepath def getUniqueUndoFile(filepath): filename, extension = os.path.split(filepath)[1].rsplit('.', 1) while 1: if extension == 'desktop': path = getUserItemPath() elif extension == 'directory': path = getUserDirectoryPath() elif extension == 'menu': path = getUserMenuPath() new_filepath = os.path.join(path, filename + '.' + extension + '.undo-' + str(uuid.uuid1())) if not os.path.isfile(new_filepath): break return new_filepath def getItemPath(file_id): for path in GLib.get_system_data_dirs(): file_path = os.path.join(path, 'applications', file_id) if os.path.isfile(file_path): return file_path return None def getUserItemPath(): item_dir = os.path.join(GLib.get_user_data_dir(), 'applications') if not os.path.isdir(item_dir): os.makedirs(item_dir) return item_dir def getDirectoryPath(file_id): for path in GLib.get_system_data_dirs(): file_path = os.path.join(path, 'desktop-directories', file_id) if os.path.isfile(file_path): return file_path return None def getUserDirectoryPath(): menu_dir = os.path.join(GLib.get_user_data_dir(), 'desktop-directories') if not os.path.isdir(menu_dir): os.makedirs(menu_dir) return menu_dir def getUserMenuPath(): menu_dir = os.path.join(GLib.get_user_config_dir(), 'menus') if not os.path.isdir(menu_dir): os.makedirs(menu_dir) return menu_dir def getSystemMenuPath(file_id): for path in GLib.get_system_config_dirs(): file_path = os.path.join(path, 'menus', file_id) if os.path.isfile(file_path): return file_path return None def getUserMenuXml(tree): system_file = getSystemMenuPath(os.path.basename(tree.get_canonical_menu_path())) name = tree.get_root_directory().get_menu_id() menu_xml = "\n" menu_xml += "\n " + name + "\n " menu_xml += "" + system_file + "\n\n" return menu_xml class SurfaceWrapper: def __init__(self, surface): self.surface = surface def getIcon(item, widget): wrapper = SurfaceWrapper(None) pixbuf = None if item is None: return wrapper if isinstance(item, CMenu.TreeDirectory): gicon = item.get_icon() elif isinstance(item, CMenu.TreeEntry): app_info = item.get_app_info() gicon = app_info.get_icon() else: return wrapper if gicon is None: return wrapper icon_theme = Gtk.IconTheme.get_default() size = 24 * widget.get_scale_factor() info = icon_theme.lookup_by_gicon(gicon, size, 0) if info is None: return wrapper try: pixbuf = info.load_icon() except GLib.GError: return wrapper if pixbuf is None: return wrapper if pixbuf.get_width() != size or pixbuf.get_height() != size: pixbuf = pixbuf.scale_simple(size, size, GdkPixbuf.InterpType.HYPER) wrapper.surface = Gdk.cairo_surface_create_from_pixbuf (pixbuf, widget.get_scale_factor(), widget.get_window()) return wrapper def removeWhitespaceNodes(node): remove_list = [] for child in node.childNodes: if child.nodeType == xml.dom.minidom.Node.TEXT_NODE: child.data = child.data.strip() if not child.data.strip(): remove_list.append(child) elif child.hasChildNodes(): removeWhitespaceNodes(child) for node in remove_list: node.parentNode.removeChild(node) def menuSortKey(node): prefCats = ["administration", "preferences"] key = node.get_menu_id().lower() name = node.get_name().lower() if key in prefCats: name = "zzzz" + name # Hack for prefCats to be sorted at the end return name Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/MenuEditor.py0000664000175000017500000006125612625666430026301 0ustar fabiofabio# -*- coding: utf-8 -*- # Alacarte Menu Editor - Simple fd.o Compliant Menu Editor # Copyright (C) 2006 Travis Watkins, Heinrich Wendel # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA import os import xml.dom.minidom import xml.parsers.expat from gi.repository import CMenu, GLib from cme import util class MenuEditor(object): def __init__(self, name='cinnamon-applications.menu'): self.name = name self.tree = CMenu.Tree.new(name, CMenu.TreeFlags.SHOW_EMPTY|CMenu.TreeFlags.INCLUDE_EXCLUDED|CMenu.TreeFlags.INCLUDE_NODISPLAY|CMenu.TreeFlags.SHOW_ALL_SEPARATORS|CMenu.TreeFlags.SORT_DISPLAY_NAME) self.tree.connect('changed', self.menuChanged) self.load() self.path = os.path.join(util.getUserMenuPath(), self.tree.props.menu_basename) self.loadDOM() def loadDOM(self): try: self.dom = xml.dom.minidom.parse(self.path) except (IOError, xml.parsers.expat.ExpatError), e: self.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.tree)) util.removeWhitespaceNodes(self.dom) def load(self): if not self.tree.load_sync(): raise ValueError("can not load menu tree %r" % (self.name,)) def menuChanged(self, *a): self.load() def save(self): fd = open(self.path, 'w') fd.write(self.dom.toprettyxml()) fd.close() def restoreToSystem(self): self.restoreTree(self.tree.get_root_directory()) path = os.path.join(util.getUserMenuPath(), os.path.basename(self.tree.get_canonical_menu_path())) try: os.unlink(path) except OSError: pass self.loadDOM() def restoreTree(self, menu): item_iter = menu.iter() item_type = item_iter.next() while item_type != CMenu.TreeItemType.INVALID: if item_type == CMenu.TreeItemType.DIRECTORY: item = item_iter.get_directory() self.restoreTree(item) elif item_type == CMenu.TreeItemType.ENTRY: item = item_iter.get_entry() self.restoreItem(item) item_type = item_iter.next() self.restoreMenu(menu) def restoreItem(self, item): if not self.canRevert(item): return try: os.remove(item.get_desktop_file_path()) except OSError: pass self.save() def restoreMenu(self, menu): if not self.canRevert(menu): return #wtf happened here? oh well, just bail if not menu.get_desktop_file_path(): return file_id = os.path.split(menu.get_desktop_file_path())[1] path = os.path.join(util.getUserDirectoryPath(), file_id) try: os.remove(path) except OSError: pass self.save() def getMenus(self, parent): if parent is None: yield (self.tree.get_root_directory(), True) return item_iter = parent.iter() item_type = item_iter.next() items = []; while item_type != CMenu.TreeItemType.INVALID: if item_type == CMenu.TreeItemType.DIRECTORY: item = item_iter.get_directory() items.append(item) item_type = item_iter.next() items.sort(key=util.menuSortKey) for item in items: yield (item, self.isVisible(item)) def getContents(self, item): contents = [] item_iter = item.iter() item_type = item_iter.next() while item_type != CMenu.TreeItemType.INVALID: item = None if item_type == CMenu.TreeItemType.DIRECTORY: item = item_iter.get_directory() elif item_type == CMenu.TreeItemType.ENTRY: item = item_iter.get_entry() elif item_type == CMenu.TreeItemType.HEADER: item = item_iter.get_header() elif item_type == CMenu.TreeItemType.ALIAS: item = item_iter.get_alias() elif item_type == CMenu.TreeItemType.SEPARATOR: item = item_iter.get_separator() if item: contents.append(item) item_type = item_iter.next() return contents def getItems(self, menu): item_iter = menu.iter() item_type = item_iter.next() while item_type != CMenu.TreeItemType.INVALID: item = None if item_type == CMenu.TreeItemType.ENTRY: item = item_iter.get_entry() elif item_type == CMenu.TreeItemType.DIRECTORY: item = item_iter.get_directory() elif item_type == CMenu.TreeItemType.HEADER: item = item_iter.get_header() elif item_type == CMenu.TreeItemType.ALIAS: item = item_iter.get_alias() elif item_type == CMenu.TreeItemType.SEPARATOR: item = item_iter.get_separator() yield (item, self.isVisible(item)) item_type = item_iter.next() def canRevert(self, item): if isinstance(item, CMenu.TreeEntry): if util.getItemPath(item.get_desktop_file_id()) is not None: path = util.getUserItemPath() if os.path.isfile(os.path.join(path, item.get_desktop_file_id())): return True elif isinstance(item, CMenu.TreeDirectory): if item.get_desktop_file_path(): file_id = os.path.split(item.get_desktop_file_path())[1] else: file_id = item.get_menu_id() + '.directory' if util.getDirectoryPath(file_id) is not None: path = util.getUserDirectoryPath() if os.path.isfile(os.path.join(path, file_id)): return True return False def setVisible(self, item, visible): dom = self.dom if isinstance(item, CMenu.TreeEntry): menu_xml = self.getXmlMenu(self.getPath(item.get_parent()), dom.documentElement, dom) if visible: self.addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Include') self.writeItem(item, NoDisplay=False) else: self.addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Exclude') self.addXmlTextElement(menu_xml, 'AppDir', util.getUserItemPath(), dom) elif isinstance(item, CMenu.TreeDirectory): item_iter = item.iter() first_child_type = item_iter.next() #don't mess with it if it's empty if first_child_type == CMenu.TreeItemType.INVALID: return menu_xml = self.getXmlMenu(self.getPath(item), dom.documentElement, dom) for node in self.getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): node.parentNode.removeChild(node) self.writeMenu(item, NoDisplay=not visible) self.addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) self.save() def createItem(self, parent, before, after, **kwargs): file_id = self.writeItem(None, **kwargs) self.insertExternalItem(file_id, parent.get_menu_id(), before, after) def insertExternalItem(self, file_id, parent_id, before=None, after=None): parent = self.findMenu(parent_id) dom = self.dom self.addItem(parent, file_id, dom) self.positionItem(parent, ('Item', file_id), before, after) self.save() def insertExternalMenu(self, file_id, parent_id, before=None, after=None): menu_id = file_id.rsplit('.', 1)[0] parent = self.findMenu(parent_id) dom = self.dom self.addXmlDefaultLayout(self.getXmlMenu(self.getPath(parent), dom.documentElement, dom) , dom) menu_xml = self.getXmlMenu(self.getPath(parent) + [menu_id], dom.documentElement, dom) self.addXmlTextElement(menu_xml, 'Directory', file_id, dom) self.positionItem(parent, ('Menu', menu_id), before, after) self.save() def editItem(self, item, icon, name, comment, command, use_term, parent=None, final=True): #if nothing changed don't make a user copy app_info = item.get_app_info() if icon == app_info.get_icon() and name == app_info.get_display_name() and comment == item.get_comment() and command == item.get_exec() and use_term == item.get_launch_in_terminal(): return #hack, item.get_parent() seems to fail a lot if not parent: parent = item.get_parent() self.writeItem(item, Icon=icon, Name=name, Comment=comment, Exec=command, Terminal=use_term) if final: dom = self.dom menu_xml = self.getXmlMenu(self.getPath(parent), dom.documentElement, dom) self.addXmlTextElement(menu_xml, 'AppDir', util.getUserItemPath(), dom) self.save() def editMenu(self, menu, icon, name, comment, final=True): #if nothing changed don't make a user copy if icon == menu.get_icon() and name == menu.get_name() and comment == menu.get_comment(): return #we don't use this, we just need to make sure the exists #otherwise changes won't show up dom = self.dom menu_xml = self.getXmlMenu(self.getPath(menu), dom.documentElement, dom) self.writeMenu(menu, Icon=icon, Name=name, Comment=comment) if final: self.addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) self.save() def copyItem(self, item): dom = self.dom file_path = item.get_desktop_file_path() copy_buffer = GLib.KeyFile() copy_buffer.load_from_file(file_path, util.KEY_FILE_FLAGS) return (copy_buffer, None) def cutItem(self, item): copy_buffer, file_id = self.copyItem(item) file_id = self.deleteItem(item) return (copy_buffer, file_id) def pasteItem(self, cut_copy_buffer, menu, file_id = None): try: path = self.getPath(menu) util.fillKeyFile(cut_copy_buffer, dict(Hidden=False, NoDisplay=False)) name = util.getNameFromKeyFile(cut_copy_buffer) if file_id is None: file_id = util.getUniqueFileId(name.replace(os.sep, '-'), '.desktop') out_path = os.path.join(util.getUserItemPath(), file_id) contents, length = cut_copy_buffer.to_data() f = open(out_path, 'w') f.write(contents) f.close() menu_xml = self.getXmlMenu(path, self.dom.documentElement, self.dom) self.addXmlFilename(menu_xml, self.dom, file_id, 'Include') self.addXmlTextElement(menu_xml, 'AppDir', util.getUserItemPath(), self.dom) self.save() return True except: return False def deleteItem(self, item): file_id = self.writeItem(item, Hidden=True) item_xml = self.getXmlMenu(self.getPath(item.get_parent()), self.dom.documentElement, self.dom) self.removeXmlFilename(item_xml, self.dom, file_id) self.save() return file_id def deleteMenu(self, menu): dom = self.dom menu_xml = self.getXmlMenu(self.getPath(menu), dom.documentElement, dom) self.addDeleted(menu_xml, dom) self.save() def deleteSeparator(self, item): parent = item.get_parent() contents = self.getContents(parent) contents.remove(item) layout = self.createLayout(contents) dom = self.dom menu_xml = self.getXmlMenu(self.getPath(parent), dom.documentElement, dom) self.addXmlLayout(menu_xml, layout, dom) self.save() def findMenu(self, menu_id, parent=None): if parent is None: parent = self.tree.get_root_directory() if menu_id == parent.get_menu_id(): return parent item_iter = parent.iter() item_type = item_iter.next() while item_type != CMenu.TreeItemType.INVALID: if item_type == CMenu.TreeItemType.DIRECTORY: item = item_iter.get_directory() if item.get_menu_id() == menu_id: return item menu = self.findMenu(menu_id, item) if menu is not None: return menu item_type = item_iter.next() def isVisible(self, item): if isinstance(item, CMenu.TreeEntry): app_info = item.get_app_info() return not (item.get_is_excluded() or app_info.get_nodisplay()) elif isinstance(item, CMenu.TreeDirectory): return not item.get_is_nodisplay() return True def getPath(self, menu): names = [] current = menu while current is not None: try: names.append(current.get_menu_id()) except: names.append(current.get_desktop_file_id()) current = current.get_parent() # XXX - don't append root menu name, alacarte doesn't # expect it. look into this more. names.pop(-1) return names[::-1] def getXmlMenuPart(self, element, name): for node in self.getXmlNodesByName('Menu', element): for child in self.getXmlNodesByName('Name', node): if child.childNodes[0].nodeValue == name: return node return None def getXmlMenu(self, path, element, dom): for name in path: found = self.getXmlMenuPart(element, name) if found is not None: element = found else: element = self.addXmlMenuElement(element, name, dom) return element def addXmlMenuElement(self, element, name, dom): node = dom.createElement('Menu') self.addXmlTextElement(node, 'Name', name, dom) return element.appendChild(node) def addXmlTextElement(self, element, name, text, dom): for temp in element.childNodes: if temp.nodeName == name: if temp.childNodes[0].nodeValue == text: return node = dom.createElement(name) text = dom.createTextNode(text) node.appendChild(text) return element.appendChild(node) def addXmlFilename(self, element, dom, filename, type = 'Include'): # remove old filenames for node in self.getXmlNodesByName(['Include', 'Exclude'], element): if node.childNodes[0].nodeName == 'Filename' and node.childNodes[0].childNodes[0].nodeValue == filename: element.removeChild(node) # add new filename node = dom.createElement(type) node.appendChild(self.addXmlTextElement(node, 'Filename', filename, dom)) return element.appendChild(node) def removeXmlFilename(self, element, dom, filename): for node in self.getXmlNodesByName(['Include'], element): if node.childNodes[0].nodeName == 'Filename' and node.childNodes[0].childNodes[0].nodeValue == filename: element.removeChild(node) def addDeleted(self, element, dom): node = dom.createElement('Deleted') return element.appendChild(node) def makeKeyFile(self, file_path, kwargs): if 'KeyFile' in kwargs: return kwargs['KeyFile'] keyfile = GLib.KeyFile() if file_path is not None: keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) util.fillKeyFile(keyfile, kwargs) return keyfile def writeItem(self, item, **kwargs): if item is not None: file_path = item.get_desktop_file_path() else: file_path = None keyfile = self.makeKeyFile(file_path, kwargs) if item is not None: file_id = item.get_desktop_file_id() else: file_id = util.getUniqueFileId(keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, 'Name'), '.desktop') contents, length = keyfile.to_data() f = open(os.path.join(util.getUserItemPath(), file_id), 'w') f.write(contents) f.close() return file_id def writeMenu(self, menu, **kwargs): if menu is not None: file_id = os.path.split(menu.get_desktop_file_path())[1] file_path = menu.get_desktop_file_path() keyfile = GLib.KeyFile() keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) elif menu is None and 'Name' not in kwargs: raise Exception('New menus need a name') else: file_id = util.getUniqueFileId(kwargs['Name'], '.directory') keyfile = GLib.KeyFile() util.fillKeyFile(keyfile, kwargs) contents, length = keyfile.to_data() f = open(os.path.join(util.getUserDirectoryPath(), file_id), 'w') f.write(contents) f.close() return file_id def getXmlNodesByName(self, name, element): for child in element.childNodes: if child.nodeType == xml.dom.Node.ELEMENT_NODE: if isinstance(name, str) and child.nodeName == name: yield child elif isinstance(name, list) or isinstance(name, tuple): if child.nodeName in name: yield child def addXmlMove(self, element, old, new, dom): if not self.undoMoves(element, old, new, dom): node = dom.createElement('Move') node.appendChild(self.addXmlTextElement(node, 'Old', old, dom)) node.appendChild(self.addXmlTextElement(node, 'New', new, dom)) #are parsed in reverse order, need to put at the beginning return element.insertBefore(node, element.firstChild) def addXmlLayout(self, element, layout, dom): # remove old layout for node in self.getXmlNodesByName('Layout', element): element.removeChild(node) # add new layout node = dom.createElement('Layout') for order in layout: if order[0] == 'Separator': child = dom.createElement('Separator') node.appendChild(child) elif order[0] == 'Filename': child = self.addXmlTextElement(node, 'Filename', order[1], dom) elif order[0] == 'Menuname': child = self.addXmlTextElement(node, 'Menuname', order[1], dom) elif order[0] == 'Merge': child = dom.createElement('Merge') child.setAttribute('type', order[1]) node.appendChild(child) return element.appendChild(node) def addXmlDefaultLayout(self, element, dom): # remove old default layout for node in self.getXmlNodesByName('DefaultLayout', element): element.removeChild(node) # add new layout node = dom.createElement('DefaultLayout') node.setAttribute('inline', 'false') return element.appendChild(node) def createLayout(self, items): layout = [] layout.append(('Merge', 'menus')) for item in items: if isinstance(item, CMenu.TreeDirectory): layout.append(('Menuname', item.get_menu_id())) elif isinstance(item, CMenu.TreeEntry): layout.append(('Filename', item.get_desktop_file_id())) elif isinstance(item, CMenu.TreeSeparator): layout.append(('Separator',)) else: layout.append(item) layout.append(('Merge', 'files')) return layout def addItem(self, parent, file_id, dom): xml_parent = self.getXmlMenu(self.getPath(parent), dom.documentElement, dom) self.addXmlFilename(xml_parent, dom, file_id, 'Include') def moveItem(self, parent, item, before=None, after=None): self.positionItem(parent, item, before=before, after=after) self.save() def getIndex(self, item, contents): index = -1 if isinstance(item, CMenu.TreeDirectory): for i in range(len(contents)): if type(item) is not type(contents[i]): continue if item.get_menu_id() == contents[i].get_menu_id(): index = i return index elif isinstance(item, CMenu.TreeEntry): for i in range(len(contents)): if type(item) is not type(contents[i]): continue if item.get_desktop_file_id() == contents[i].get_desktop_file_id(): index = i return index return index def positionItem(self, parent, item, before=None, after=None): contents = self.getContents(parent) index = -1 if after: index = self.getIndex(after, contents) + 1 # index = contents.index(after) + 1 elif before: index = self.getIndex(before, contents) # index = contents.index(before) else: # append the item to the list index = len(contents) #if this is a move to a new parent you can't remove the item item_index = self.getIndex(item, contents) if item_index > -1: # decrease the destination index, if we shorten the list if (before and (item_index < index)) \ or (after and (item_index < index - 1)): index -= 1 contents.remove(contents[item_index]) contents.insert(index, item) layout = self.createLayout(contents) dom = self.dom menu_xml = self.getXmlMenu(self.getPath(parent), dom.documentElement, dom) self.addXmlLayout(menu_xml, layout, dom) def undoMoves(self, element, old, new, dom): nodes = [] matches = [] original_old = old final_old = old #get all elements for node in self.getXmlNodesByName(['Move'], element): nodes.insert(0, node) #if the matches our old parent we've found a stage to undo for node in nodes: xml_old = node.getElementsByTagName('Old')[0] xml_new = node.getElementsByTagName('New')[0] if xml_new.childNodes[0].nodeValue == old: matches.append(node) #we should end up with this path when completed final_old = xml_old.childNodes[0].nodeValue #undoing s for node in matches: element.removeChild(node) if len(matches) > 0: for node in nodes: xml_old = node.getElementsByTagName('Old')[0] xml_new = node.getElementsByTagName('New')[0] path = os.path.split(xml_new.childNodes[0].nodeValue) if path[0] == original_old: element.removeChild(node) for node in dom.getElementsByTagName('Menu'): name_node = node.getElementsByTagName('Name')[0] name = name_node.childNodes[0].nodeValue if name == os.path.split(new)[1]: #copy app and dir directory info from old root_path = dom.getElementsByTagName('Menu')[0].getElementsByTagName('Name')[0].childNodes[0].nodeValue xml_menu = self.getXmlMenu(root_path + '/' + new, dom.documentElement, dom) for app_dir in node.getElementsByTagName('AppDir'): xml_menu.appendChild(app_dir) for dir_dir in node.getElementsByTagName('DirectoryDir'): xml_menu.appendChild(dir_dir) parent = node.parentNode parent.removeChild(node) node = dom.createElement('Move') node.appendChild(self.addXmlTextElement(node, 'Old', xml_old.childNodes[0].nodeValue, dom)) node.appendChild(self.addXmlTextElement(node, 'New', os.path.join(new, path[1]), dom)) element.appendChild(node) if final_old == new: return True node = dom.createElement('Move') node.appendChild(self.addXmlTextElement(node, 'Old', final_old, dom)) node.appendChild(self.addXmlTextElement(node, 'New', new, dom)) return element.appendChild(node) Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/__init__.py0000664000175000017500000000000012625666430025741 0ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-menu-editor/cme/config.py0000664000175000017500000000033412625666430025461 0ustar fabiofabioprefix="/usr" datadir="/usr/share" localedir=datadir+"/locale" libdir="/usr/share/cinnamon" libexecdir="/usr/share/cinnamon/cinnamon-menu-editor" PACKAGE="cinnamon-menu-editor" VERSION="1.6.1" GETTEXT_PACKAGE="cinnamon" Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/0000775000175000017500000000000012625666430023417 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/page_memory.py0000664000175000017500000000420312625666430026274 0ustar fabiofabiofrom pageutils import * from gi.repository import Gio, Gtk, GObject, Gdk, Pango, GLib class MemoryView(BaseListView): def __init__(self): store = Gtk.ListStore(str, int) BaseListView.__init__(self, store) self.createTextColumn(0, "Name") self.createTextColumn(1, "Size (bytes)") column = self.createTextColumn(1, "Size (readable)") column.set_cell_data_func(self.rendererText, self.cellDataFuncSize) self.getUpdates() lookingGlassProxy.addStatusChangeCallback(self.onStatusChange) def cellDataFuncSize(self, column, cell, model, iter, data=None): value = model.get_value(iter, 1) if(value < 1000): cell.set_property("text", "%d B" % value) elif(value < 1000000): cell.set_property("text", "%.2f KB" % (value/1024.0)) elif(value < 1000000000): cell.set_property("text", "%.2f MB" % (value/1024.0/1024.0)) def onStatusChange(self, online): if online: self.getUpdates() def getUpdates(self, igno=None): self.store.clear() success, time_last_gc, data = lookingGlassProxy.GetMemoryInfo() if success: self.secondsLabel.set_text("%d" % time_last_gc) for key in data.keys(): self.store.append([key, data[key]]) def onFullGc(self, widget): lookingGlassProxy.FullGc() self.getUpdates() class ModulePage(WindowAndActionBars): def __init__(self, parent): self.view = MemoryView() WindowAndActionBars.__init__(self, self.view) self.parent = parent refresh = ImageButton("view-refresh") refresh.set_tooltip_text("Refresh") refresh.connect("clicked", self.view.getUpdates) self.addToLeftBar(refresh, 1) fullGc = ImageButton("user-trash-full") fullGc.set_tooltip_text("Full Garbage Collection") fullGc.connect ('clicked', self.view.onFullGc) self.addToLeftBar(fullGc, 1) self.addToBottomBar(Gtk.Label("Time since last GC:"), 2) self.view.secondsLabel = Gtk.Label("") self.addToBottomBar(self.view.secondsLabel, 2) Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/page_windows.py0000664000175000017500000000701012625666430026455 0ustar fabiofabiofrom pageutils import * from gi.repository import Gio, Gtk, GObject, Gdk, Pango, GLib class ModulePage(BaseListView): def __init__(self, parent): store = Gtk.ListStore(int, str, str, str) BaseListView.__init__(self, store) self.parent = parent column = self.createTextColumn(0, "ID") column.set_cell_data_func(self.rendererText, self.cellDataFuncID) self.createTextColumn(1, "Title") self.createTextColumn(2, "WMClass") self.createTextColumn(3, "Application") self.getUpdates() lookingGlassProxy.connect("WindowListUpdate", self.getUpdates) lookingGlassProxy.addStatusChangeCallback(self.onStatusChange) self.treeView.connect("row-activated", self.onRowActivated) self.treeView.connect("button-press-event", self.onButtonPress) # Popup menu self.popup = Gtk.Menu() inspectWindow = Gtk.MenuItem('Inspect Window') label = inspectWindow.get_children()[0] label.set_markup("" + inspectWindow.get_label() + "") inspectWindow.connect("activate", self.onInspectWindow) self.popup.append(inspectWindow) self.inspectApp = Gtk.MenuItem('Inspect Application') self.inspectApp.connect("activate", self.onInspectApplication) self.popup.append(self.inspectApp) self.popup.show_all() def cellDataFuncID(self, column, cell, model, iter, data=None): value = model.get_value(iter, 0) cell.set_property("text", "w(%d) / a(%d)" % (value, value)) def onRowActivated(self, treeview, path, view_column): iter = self.store.get_iter(path) id = self.store.get_value(iter, 0) title = self.store.get_value(iter, 1) cinnamonLog.pages["inspect"].inspectElement("w(%d)" % id, "object", title, "") def onInspectWindow(self, menuItem): iter = self.store.get_iter(self.selectedPath) id = self.store.get_value(iter, 0) title = self.store.get_value(iter, 1) cinnamonLog.pages["inspect"].inspectElement("w(%d)" % id, "object", title, "") def onInspectApplication(self, menuItem): iter = self.store.get_iter(self.selectedPath) id = self.store.get_value(iter, 0) application = self.store.get_value(iter, 3) cinnamonLog.pages["inspect"].inspectElement("a(%d)" % id, "object", application, "") def onButtonPress(self, treeview, event): if event.button == 3: x = int(event.x) y = int(event.y) time = event.time pthinfo = treeview.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo self.selectedPath = path treeview.grab_focus() treeview.set_cursor( path, col, 0) iter = self.store.get_iter(self.selectedPath) app = self.store.get_value(iter, 3) self.inspectApp.set_sensitive(app != "") self.popup.popup( None, None, None, None, event.button, event.time) return True def onStatusChange(self, online): if online: self.getUpdates() def getUpdates(self): self.store.clear() success, data = lookingGlassProxy.GetLatestWindowList() if success: try: for item in data: self.store.append([int(item["id"]), item["title"], item["wmclass"], item["app"]]) except Exception as e: print e Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/lookingglass_proxy.py0000664000175000017500000000760512625666430027736 0ustar fabiofabioimport dbus from gi.repository import Gio, Gtk, GObject, Gdk, Pango, GLib LG_DBUS_NAME = "org.Cinnamon.LookingGlass" LG_DBUS_PATH = "/org/Cinnamon/LookingGlass" class LookingGlassProxy: def __init__(self): self._signals = [] self._statusChangeCallbacks = [] self._proxy = None Gio.bus_watch_name(Gio.BusType.SESSION, LG_DBUS_NAME, Gio.BusNameWatcherFlags.NONE, self._onConnect, self._onDisconnect) def addStatusChangeCallback(self, callback): self._statusChangeCallbacks.append(callback) def refreshStatus(self): if self._proxy != None: self._setStatus(True) else: self._setStatus(False) def getIsReady(self): return self._proxy != None def connect(self, name, callback): self._signals.append((name, callback)) def _onSignal(self, proxy, sender_name, signal_name, params): for name, callback in self._signals: if signal_name == name: callback(*params) def _setStatus(self, state): for callback in self._statusChangeCallbacks: callback(state) def _onConnect(self, connection, name, owner): if self._proxy: return self._initProxy() def _onDisconnect(self, connection, name): self._proxy = None self._setStatus(False) def _initProxy(self): try: self._proxy = Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.NONE, None, LG_DBUS_NAME, LG_DBUS_PATH, LG_DBUS_NAME, None, self._onProxyReady, None) except dbus.exceptions.DBusException as e: print(e) self._proxy = None def _onProxyReady(self, object, result, data=None): self._proxy = Gio.DBusProxy.new_for_bus_finish(result) self._proxy.connect("g-signal", self._onSignal) self._setStatus(True) # Proxy Methods: def Eval(self, code): if self._proxy: try: self._proxy.Eval('(s)', code) except: pass def GetResults(self): if self._proxy: try: return self._proxy.GetResults('()') except: pass return (False, "") def AddResult(self, code): if self._proxy: try: self._proxy.AddResult('(s)', code) except: pass def GetErrorStack(self): if self._proxy: try: return self._proxy.GetErrorStack('()') except: pass return (False, "") def GetMemoryInfo(self): if self._proxy: try: return self._proxy.GetMemoryInfo('()') except: pass return (False, 0, {}) def FullGc(self): if self._proxy: try: self._proxy.FullGc('()') except: pass def Inspect(self, code): if self._proxy: try: return self._proxy.Inspect('(s)', code) except: pass return (False, "") def GetLatestWindowList(self): if self._proxy: try: return self._proxy.GetLatestWindowList('()') except: pass return (False, "") def StartInspector(self): if self._proxy: try: self._proxy.StartInspector('()') except: pass def GetExtensionList(self): if self._proxy: try: return self._proxy.GetExtensionList('()') except: pass return (False, "") def ReloadExtension(self, uuid, xletType): if self._proxy: try: return self._proxy.ReloadExtension('(ss)', uuid, xletType) except: pass return (False, "") Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/page_log.py0000664000175000017500000001215012625666430025545 0ustar fabiofabioimport datetime from pageutils import * from gi.repository import Gio, Gtk, GObject, Gdk, Pango, GLib class LogEntry(): def __init__(self, category, time, message): self.category = category self.time = int(time) self.timestr = datetime.datetime.fromtimestamp(self.time).strftime("%Y-%m-%dT%H:%M:%SZ") self.message = message self.formattedText = "%s t=%s %s\n" % (category, self.timestr, message) class LogView(Gtk.ScrolledWindow): def __init__(self): Gtk.ScrolledWindow.__init__(self) self.set_shadow_type(Gtk.ShadowType.ETCHED_IN) self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.textview = Gtk.TextView() self.textview.set_editable(False) self.add(self.textview) self.textbuffer = self.textview.get_buffer() self.log = [] self.addedMessages = 0 self.firstMessageTime = None self.enabledTypes = {'info': True, 'warning': True, 'error': True, 'trace': False } self.typeTags = { 'info': self.textbuffer.create_tag("info", foreground="#1a6f18", invisible=self.enabledTypes["info"] != True, invisible_set=True), 'warning': self.textbuffer.create_tag("warning", foreground="#c8bf33", invisible=self.enabledTypes["warning"] != True, invisible_set=True), 'error': self.textbuffer.create_tag("error", foreground="#9f1313", invisible=self.enabledTypes["error"] != True, invisible_set=True), 'trace': self.textbuffer.create_tag("trace", foreground="#18186f", invisible=self.enabledTypes["trace"] != True, invisible_set=True) } #todo: load all enabled types from gsettings #self.enabledTypes = {'info': False, 'warning': False, 'error': False, 'trace': False } #for key in data: # self.enabledTypes[key] = True self.getUpdates() lookingGlassProxy.connect("LogUpdate", self.getUpdates) lookingGlassProxy.addStatusChangeCallback(self.onStatusChange) def append(self, category, time, message): entry = LogEntry(category, time, message) self.log.append(entry) return entry def onButtonToggled(self, button, data): self.textview.hide() active = button.get_active() self.enabledTypes[data] = active self.typeTags[data].props.invisible = active != True self.textbuffer.set_modified(True) #print self.textview.get_preferred_height() adj = self.get_vadjustment() #adj.set_upper(self.textview.get_allocated_height()) self.textview.show() def onStatusChange(self, online): iter = self.textbuffer.get_end_iter() if online: entry = self.append("info", 0, "================ DBus connection established ===============") else: entry = self.append("warning", 0, "================ DBus connection lost ===============") self.textbuffer.insert_with_tags(iter, entry.formattedText, self.typeTags[entry.category]) self.getUpdates(True) def getUpdates(self, reread = False): success, data = lookingGlassProxy.GetErrorStack() if success: try: dataSize = len(data) if dataSize > 0: # If this is a completely new log, start reading at the beginning firstMessageTime = data[0]["timestamp"] if self.addedMessages > dataSize or self.firstMessageTime != firstMessageTime or reread: self.firstMessageTime = firstMessageTime self.addedMessages = 0 if reread: start, end = self.textbuffer.get_bounds() self.textbuffer.delete(start, end) self.textview.hide() iter = self.textbuffer.get_end_iter() for item in data[self.addedMessages:]: entry = self.append(item["category"], float(item["timestamp"])*0.001, item["message"]) self.textbuffer.insert_with_tags(iter, entry.formattedText, self.typeTags[entry.category]) self.addedMessages += 1 self.textview.show() except Exception as e: print e class ModulePage(WindowAndActionBars): def __init__(self, parent): self.view = LogView() WindowAndActionBars.__init__(self, self.view) self.parent = parent; self.addToggleButton("info", "dialog-information", "Show/Hide Messages tagged as 'info'") self.addToggleButton("warning", "dialog-warning", "Show/Hide Messages tagged as 'warning'") self.addToggleButton("error", "dialog-error", "Show/Hide Messages tagged as 'error'") self.addToggleButton("trace", "dialog-question", "Show/Hide Messages tagged as 'trace'") def addToggleButton(self, logType, icon, tooltip): button = ImageToggleButton(icon) button.connect("toggled", self.view.onButtonToggled, logType) button.set_active(self.view.enabledTypes[logType]) button.set_tooltip_text(tooltip) self.addToLeftBar(button, 1) Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-looking-glass/cinnamon-looking-glass.py0000775000175000017500000005355512625666430030362 0ustar fabiofabio#!/usr/bin/env python2 # Todo: # - TextTag.invisible does not work nicely with scrollheight, find out why # - (Sometimes scrollbars think there is more or less to scroll than there actually is after showing/hiding entries in page_log.py) # - Add insert button to "simple types" inspect dialog ? is there actual use for these types inserted as results ? # - Load all enabled log categories and window height from gsettings # - Make CommandLine entry & history work more like a normal terminal # - When navigating through history and modifying a line # - When pressing ctrl + r, search history # - auto-completion ? import sys import os import pyinotify import gi gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk, GObject, Gdk, Pango, GLib import dbus, dbus.service, dbus.glib from pageutils import * from lookingglass_proxy import LookingGlassProxy from dbus.mainloop.glib import DBusGMainLoop import signal signal.signal(signal.SIGINT, signal.SIG_DFL) MELANGE_DBUS_NAME = "org.Cinnamon.Melange" MELANGE_DBUS_PATH = "/org/Cinnamon/Melange" class MenuButton(Gtk.Button): def __init__(self, text): Gtk.Button.__init__(self, text) self.connect("clicked", self.onClicked) def set_popup(self, menu): self.menu = menu def onClicked(self, widget): x, y, w, h = self.getScreenCoordinates() self.menu.popup(None, None, lambda menu, data: (x, y+h, True), None, 1, 0) def getScreenCoordinates(self): parent = self.get_parent_window() x, y = parent.get_root_origin() w = parent.get_width() h = parent.get_height() extents = parent.get_frame_extents() allocation = self.get_allocation() return (x + (extents.width-w)/2 + allocation.x, y + (extents.height-h)-(extents.width-w)/2 + allocation.y, allocation.width, allocation.height) class ResizeGrip(Gtk.Widget): def __init__(self, parent): Gtk.Widget.__init__(self) self.parentWindow = parent def do_realize(self): self.set_realized(True) allocation = self.get_allocation() attr = Gdk.WindowAttr() attr.window_type = Gdk.WindowType.CHILD attr.wclass = Gdk.WindowWindowClass.INPUT_OUTPUT attr.event_mask = self.get_events() | Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK attr.x = 0 attr.y = 0 attr.width = allocation.width attr.height = allocation.height mask = Gdk.WindowAttributesType.X | Gdk.WindowAttributesType.Y window = Gdk.Window.new(self.get_parent_window(), attr, mask) self.set_window(window) window.set_user_data(self) self.style = self.get_style() self.style.set_background(window, Gtk.StateFlags.NORMAL) self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.BOTTOM_SIDE)) self.connect("draw", self.onDraw) def do_unrealize(self): self.get_window().destroy() def do_size_request(self, requisition): requisition.height = 4 requisition.width = -1 def do_get_preferred_width(self): req = Gtk.Requisition() self.do_size_request(req) return (req.width, req.width) def do_get_preferred_height(self): req = Gtk.Requisition() self.do_size_request(req) return (req.height, req.height) def do_size_allocate(self, allocation): if self.get_realized(): self.get_window().move_resize(allocation.x, allocation.y, allocation.width, allocation.height) self.queue_draw() def do_button_press_event(self, event): self.parentWindow.begin_resize_drag(Gdk.WindowEdge.SOUTH, event.button, int(event.x_root), int(event.y_root), event.time) return True def onDraw(self, widget, ctx): width = self.get_window().get_width() # Draw a line using the current theme cr = self.get_window().cairo_create() self.style.do_draw_hline(self.style, cr, Gtk.StateType.NORMAL, self, "", 1, width-2, 1) class CommandLine(Gtk.Entry): def __init__(self): Gtk.Entry.__init__(self) self.settings = Gio.Settings.new("org.cinnamon") self.history = self.settings.get_strv("looking-glass-history") self.historyPosition = -1 self.lastText = "" self.connect('key-press-event', self.onKeyPress) self.connect("populate-popup", self.populatePopup) def populatePopup(self, view, menu): menu.append(Gtk.SeparatorMenuItem()) clear = Gtk.MenuItem("Clear History") clear.connect('activate', self.historyClear) menu.append(clear) menu.show_all() return False def onKeyPress(self, widget, event): if event.keyval == Gdk.KEY_Up: self.historyPrev() return True if event.keyval == Gdk.KEY_Down: self.historyNext() return True if event.keyval == Gdk.KEY_Return: self.execute() return True def historyClear(self, menuItem): self.history = [] self.historyPosition = -1 self.lastText = "" self.settings.set_strv("looking-glass-history", self.history) def historyPrev(self): num = len(self.history) if self.historyPosition == 0 or num == 0: return if self.historyPosition == -1: self.historyPosition = num - 1 self.lastText = self.get_text() else: self.historyPosition -= 1 self.set_text(self.history[self.historyPosition]) self.select_region(-1, -1) def historyNext(self): if self.historyPosition == -1: return num = len(self.history) if self.historyPosition == num-1: self.historyPosition = -1 self.set_text(self.lastText) else: self.historyPosition += 1 self.set_text(self.history[self.historyPosition]) self.select_region(-1, -1) def execute(self): self.historyPosition = -1 command = self.get_text() if command != "": num = len(self.history) if num == 0 or self.history[num-1] != command: self.history.append(command) self.set_text("") self.settings.set_strv("looking-glass-history", self.history) lookingGlassProxy.Eval(command) def doCrash(self): lookingGlassProxy.Eval("global.segfault()") class NewLogDialog(Gtk.Dialog): def __init__(self, parent): Gtk.Dialog.__init__(self, "Add a new file watcher", parent, 0, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)) self.set_default_size(150, 100) label = Gtk.Label("") label.set_markup("Add File Watch:\n\nPlease select a file to watch and a name for the tab\n") box = self.get_content_area() box.add(label) self.store = Gtk.ListStore(str, str) self.store.append(["glass.log", "~/.cinnamon/glass.log"]) self.store.append(["custom", "",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings-spice-progress.ui0000664000175000017500000001004312625666430031446 0ustar fabiofabio False start start 6 False preferences-other normal True True False 300 110 False start start vertical 6 False spread gtk-cancel True True True True False True 2 0 False True end 0 True False vertical 6 True False Refreshing applet index... False True 0 True False True True True 4 1 False True 1 btnProgressAbort Cinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.ui0000664000175000017500000002434612625666430026516 0ustar fabiofabio True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK System Settings center 800 600 preferences-system True False vertical True False icons True False center True False True False 1 True True start 25 False gtk-find gtk-clear Search... False True end side_view side_view 2 True False 1 True True True False True False go-previous-symbolic False False 0 True center True True True 1 content_box content_box 1 True False False True 0 True False 125 True True never True False True natural none True False start 10 vertical Hello World side_view_page 1 True True True True False True none True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 6 3 content_box_page 2 True True 1 Cinnamon-2.8.6/files/usr/share/cinnamon/bumpmaps/0000775000175000017500000000000012625666430020672 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/bumpmaps/fish.png0000664000175000017500000000047412625666430022336 0ustar fabiofabioPNG  IHDRFȋ+IDATx S݉b@?Ћͼ'P3-##0=r"sh7JF+ 0i-%ȘRԷN."U8*0**8*~]$xҁt+ t+e2RY^W`dOP(i,k|T]ag@əa؏wv{|쏸6 ]2a-ШݽhGmY6DX9]geQFm|qV"L{Mq6 ',P)vaEVIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/bumpmaps/birdnfish.png0000664000175000017500000000705212625666430023354 0ustar fabiofabioPNG  IHDRWK IDATx^\Ɏ$Q;*@i*`%q&e^d7xNeQJ$pPp!4U]ԭ;*WԕXI%*av"Q#Q^$cQxw [pFT(*r^>BTUZd{ ؿ0ݴjDC<4 PssC 5|g;Dd*j/KyWP/ bL1- fn\s,#@9)'#^`".{pr6`$j/*ϟ&#˞r>y(T(4<>}᭺l'޽|Rq QE?R;>BsʇgVga7K[$ήppk 8\rƃEߓr;y z‡*vfשi*jD763(L/?O&@6boX]r ;*~)*ɮz|n2wX[oFA3~(aN6]C7W5qy eY§^r`d  C*f7S9Ht2fY96fS;Zr$^]W?tBRl=; w؊ێaMb3ss5DsG{6d zū'LSE:%wA ($~+#{'j? SWO  !hH?;< WʹE\H]Ԁ5"͘+P>ǀ\RA />D" .J562`*Q&iH'\2#'7pܺg%5w9W4" `g6T#+|VɌ^o`"ihkv2#P@;p_fnx7*"\?Ȟ8aѫn飕l*VHt0<&sILLs^)B#9 $vӝyGf:_& } ,X/8 ,7<DI[[B -?XZe֧y+$CyR z77^/19wZ+!i=D10T iHdV 'hdM#_{2 "'M27=d.I/!8P+`G@``D֝{I, XEmȫ'm4ڹ6(4tfiO-ҋ`_!пMD \gUnԶF˕A%98@}NmaDCIzU"٤] G/?ջV@&I+̚(a w 9d׏N^x=F3M| [hpTHڙ{WK!SIO@ug2ss+* ’S5x!!8!7rt:Őv_", (qea#FԐЂt?V6AU0=0"uj(tJK|Dy/u =ѭFnCsԪ;_Z{X2Զ^~݉GՅ\c3KCmx~s#_Od쁒ac]E[X@5>/9V9̏D Y:|$e?Xj=bC2w|*ݑgÉ''z2R5L< ${Ƭd.tԺ(Z.@84 {$LmZ3&=!8BB-'9<_\48^ ɪ M@~-i.l@-a AM. 8h́sAO٣ jU^AR (ٞ.^(  j^c۵s/"MhICE'mƝ //)躯܃ J@IWr7 Dɪ_Q2~@VLy7aA0*;(/TЏ+'ln59=Pk z]9Q}mk -i?oAtvmÎnm5,ig^\^5Sh@]͞n]u4@dr(FL @(G 3doBև]VHWbJi> kIwmI;1v2W"7h2ЗL:m:,'}PN8cse7OA6IZwz/smAy=RyHܺЊ~3.)Uif_`%26SSgdXl J1ʩà\81X|{96aCA)F d= D^5ԕ|IX=.";XJcTgyjGmPr$u4> @!bG V#=rZh!JYU}"b:@z L)bE3L݅ 9q2p}$+(uIݣQ~T hj^,A̩F)3WiPMihtk #zR;#Wkޝ>`hRy⦸οH5hk92*I{ $P3E;,Q*D0h#Aҋ'x D5h'*%w$vޥphVU9CʼfRBHMjl\%b(c4ԴCr͡UBebF-FvAXNWOOr:DYdFuv>AܧRMuxkIN(W]| +Qt$=b& dH^7t=Y7̚|4 F⅙$E5OɦvsaF@oL& å.4F'\!Kqc0*}7!(92uP.vBPDCZٹTY#R-ȉ!@) tTIQ0쮲uvJ M8>V^+f*vRtQ9*I=蝂 XuBp.dKAʕ1Bt:>\of']ߋL3aJՀXǥԇJC!3ɹ}kН]d_nn ]V+V>|Ⲵ]3FE} z@ (G&ePtwM◦i)XItou"7 $IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/bumpmaps/brick.png0000664000175000017500000000025412625666430022473 0ustar fabiofabioPNG  IHDRF(sIDATxco?gA&A X~Rˠ_t@H~I,&#kA,4B2' EI#Ĥ5f"2-JC]YoY zIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/bumpmaps/frost.png0000664000175000017500000003677612625666430022560 0ustar fabiofabioPNG  IHDRdd=IDATxWrHQw۶KB&95#}e9Ε0(8wD?: "-s:•ܐR+򓵣 ]<xKx*Dldи؍b'MȨsD'NEH8HnɁ1"L*Z!G̅hp+͓ Lh<-R"> "E~%.;.M]/ɭ3c*bD뉁h avbg+S7r_s%)F6Q鱙ly'tRUy]p1"s4fKbp!ڹ/Bs~P ycxe4n_4.Ph7P5. [xRȕ!p/5!-y]i;Z G De=R. y%-Y7FYbHd&8|`]p#Nn[cwXG-6;񊐬CIΚH%.¦QXP,qK|@IhIˌLGLI (ŶktK<&Z)yG&$b3v\'1?#RqXB1 Ŧxj$lFt$\~;WjUp^S㐓n7%5B$EƧ$H̊:5*Oq2%\H =}Cyք*x LBb9%4Jc^tϽGje-|3"2P.8cg@ZE"^/1[ m#jF*RfdЎ|77wbh'J! Fa|XV"reOdIєlP|­TMm ${+bp$";b gDI|[>gd(@/fɉ"5-o$VLb/ă]GB>H+-cqTyӊP6P4yZVB;G@Y*`Un8J 8,'0/EA욳='@xIoRTΤuR&$&QBդJ8VΉay3{+ԍf qa^qX{FqR {8G/qRcLϑfޑ<&='oR0?=!ICvb҂_w#L'\GJL~4[4&b7b*Y"L#Á 䕶3ߤgG&)%0̄gɁPz͕0+GBg C,x#Ol9BCwnFe̎]924r:rx1_% X{&;GJq3rdy8WPzi\C,+n6>t~+fͯL k,)ɫGLI  tk?+s <-)cJ\o""_ׁҨmfOZ'TČ c("b FB =k$V1f#>ʣVI?zD;R|;6I]/L<HWuaN;`fE5̕džSl?04c+O<Г!s|c/,+]%X".)a;x-s E^dn2xOQRy$4ߟ,+۝tv GhIHLfBD9-}!5;aG0H-{lLm b-ƷՒaiR|(dm?i MV 0\isW`XBB?%V> sF5-0Չ@$)VeXqbp$wiI_ٟ_%K) $|n ?v*|nw΅ W؃g -7c5&BqlTIU.lx'y!Jfxug3Uӟp?0NLjlwg|{fS.' g3Mrl!0AKF n(覧Z>b&`#]vę+]Kцj!OC,t`8}jfQ4vBa;neL _.ѯSx[d:WZmT$ %^>Db+׆z{dDh7+; PWr+)6E|ǧb F$En3( Āϼ#J[N3lNV m4H`)ocIܿot`xnD8^|&]ԑ4M{X3s%K@meU$>)c$Xzӝ2/<0x'wwR&>YVHp߅|[ߕTx쌧0RC, &ir//<Viz;F"ȳ8~3l&916# M k0c31W<Zb.8x̞l1Ԍ 3q7Tģ'Fkca%7i(®Rao̞S&[v``&j2_Hku&)$(BF}Pf;[ʋ,ׅ8V*q"GYGIHN%=!j)E>Pcr|L]p3DkHEv\NDxf)%|%#DNq4|P0V+%ʑІwf:s3U~B G&Bւ1ްQ5q4ғ`9*5QS"~*3/y l/Y]~/B<I'Hhlָ;RT֙52>[[q+q_Xo/v];!uƏ ;ɤ_B%W~\ıaxC4!|*e85&lWc^,IgQ"aKCp[LN|̌q+6%8ȲƕsI{WΚ-af[yGU_oc,~Mlf"_ i两K6eJ|,ʇ=|f[g F%CJt G{+.=ffL1ON$^tX I9P9'aJ*8Xٳ)`10FY&OaV dWL#Y *w ̊qgYxvؿl+IvK>W>ĸ fF7/Нh$OAZ6ȪKDG,41#KFU쵝6R"p?boXeX#^+,{wc 펋1 םr 9#["̂ '4Jի%3njA˒1DjZ` FT-2+ đb"s}`;zA2_ň$̲yRuAW oYEtq ,|bN^Z / R cR߆k)| 45vB,$\>&GDZ*C%uH+5nt|I`^1Fִ",,wBr7Oj'^"J|)z,  D̍ŜU] 쵤N0V"'ĦXWr7d.eMbFfOo12(%=djޓ7d-Otf;bH;ޣw\&%p9(y-N.~Cc),O?% iI#2)B&Wۛ.q,MikQ$3kI_v$]̈p_zi7-fLؓ (3Olv#gve^cKUS:%'J7L"Y<Ó"SoI2wRgBϠZzΆx\Wm8'~`+jV̜(_3yĝ<~0G]gv Vwyaxinw;SUg]vG:X?LG'GDneX͸p1OZa fĿ(d؂芤CPjn1=RHivS@AN^\|H~cMtF1F2KvF3nh֋g'2vRʯ ^Wi9ܶ }2UZ2]!i[VޓtZaz$_Cn'Xq@tZ!6'aqɑ'eޤseda26мFonsͤȶb#o\ugw,or`JGwc)Dij%t4zI U3&0g%kv9]Tt.Gns'n^ڋIXEnɐL$~W_)QgO'AY /|V=Q2_.gqWC _0(yp3l?)'m4 ֓_v m;ҕ:X;n'͆Nd |'LqƷyS ["7yz1;])L6rnΔB{`Aׄ9qXV7R2 LF uf M.%r/⾳0YK1oLVM[EVNדtlB9-w“a 6Ћy{ktҝT%;q_!p/l51+ɳN3Yn zlkd|Lq) Kۋb6r &% >I, $µ'ˍTwdV#E6!sx' pUBOlRPH[pfD/4A8mx Ƴk/@dD,v\뷉C@aB$b ,c bV^;;a!tS`ocHA?i$4L/&ыY.QZV\.M22g2&]ư$ƙt,*GfY^$HXXwJӉ-l`I"n={F s b\I.N|ΔM%ϐg鐑q`:Ș9 so&NjG޾-m'G*MޘVKD<9zNQx-|Y^LU 2 g;n]o \t68EBa1\fSdNV*Iu"LcbĹu8˜X_uXF>UXMf2:^Tga,‰Lxml+Ơ!dDvVR|ܳ%$18r; zhM?"v㈈Y$/%qI۽t|}v ot|vbq$FC'bx6iTԥ=lEMޟ iv1Zxi{ş6 *"OQS'b;?aa;7T[oxqIt_s a,m$M&jf^`StS&Ϲ }&J#`SMrYP:fXI=A.%P(gz !2/en&a#1Osv01=U-_n 'S~~q̯ zZ5qps! n%˜"םr/Ȗ;.GfToOϷDגJ8\dIYFn34c&{#.S䴌./KE+~D3ԕ+ptuN=˃im*Hwd>%#FwUQɇyqָ]_)ox+eza5͂X XyԸqY ƪ% /AE&YIg;OJx˪>ʹ-B?fÿT2f欀/f8Vvrrs/. AlL0(ze5e(֕$eXs-#1+8-LAȍ06/2* }av>|1zONH D".qe%u0%J>U(]?U7g1G`//G?1EkAV*LҐ"OSx =/DKChh o,#)!=k-h6DC˰*0l<ɦV|.'C6Rd-wI=_3bE~a241d@m?9pCǡۣ%Jo¤֏nsp>V8K_{ZFjȲ+[x__ Mb_ _X޳RQ͆? ;EnoI1mlrC SS> +w*RΘڱ?5:O+(f[ FV5eqľfz+(oOr>2~l]#Ҩ}`<1X^r\6};hj8et2my}v Cb>҈%T} |&03%bwƑ1\;K۟dFEU:U2EW!IxT El{9QZ.8E7 ;3"tQ6 k`2,`^^ ]j3ĭ;Uaմ`z$|Ué 2ز2ETG :w o\̹r> ޙVxg*Y Bs!k[Q0pZwF/H8HRQwO:^<鸵VCxP00 MFO[MkX;åzm CD7~!dԖYW9dlr[*aS'ҵ QI3}Ի|$M'$OYuWm6INnZ'Nwִ 5ҿ$m"ȢwXzr0⼆i'9b9V#:\& {1[igp\*:GM63,ZU-̒Ojvb 'e[֕HZ{Xt/nfpHF.8cG4v.9I)CTܰI[6G+T~껈LIt~bOTϼVݱTA{^>=/ KcVΉZsܱs6 jRc&g]{[LNcK^,0QGzN?0ujL*,Q2v®ed[撯Hs sFKy/pC]E+L/`s.s0οs3MN"4ohdsMrw]xL؁bpՆMevšF[H+gi"mEq<׍,2ų,̙)`UF$*̜qEt}8\1YLٛŷ3W($&kY?:>̺{CYoEkifԫ:Ϭ;B)hExމq HwTZ8ZщaS~gd0I+ SWRk`N*>'G ?6\5[0 7*0F`t;I(ځTjXa7 Jgܷ'_C"9~H0%NwJ#3"`~2ZoxL`-]ja&|\#kuYȨQLus$i3H aH/NٝQ8l !KW(I8Yb &}tzdmR$%om*ڿܛЩEL[NoE{uϯcƠ|#+ ׹bd z!}ErT ph$)_|`>jyf;qoS:V3S]pjx,I&X1|YQC< ]&F,l7w5<>t?B~cz ӧ3cYǿp A:P47+իecAX97s(03}>U$`fq#M [6`t*=1V,~ҍRy8Ɩ+x!ܣg{7piz5c_$[cI7#o0<%hIN$hu[RN6Y*!Nck8/^KB%(w U'SKkL~iYIOTu:]UOĚ(qE<;0 OVw"m*5/Suih=àw9uЖVvo08a#m?Awǔ5o5A uϣ;NEihwPRؤwp=Fܡf3ujK ԯZ(IL*n|׶~f5Vx tJkwOzXn-YS+=53W%)Bcjܼ<:hӔ|y.lOQ9r#[2~Fm/#dWJ$2:ywl1>47}"$[Yl2əDjre`}beJ-L);~(‡&9pNK 4;7kUyOݱIxӦ=eLǃau qdh">iopNPܝ`'6zo ϊi*i•tJgm.r̪ qmQ&n#Y#NmfQn{c#*Bˌ5Ql˙ɗf⾒k'\}"<^>,}-S/{=^q;InhZe г"E~u/:uO}\M-j)LuOdy(Xk?-楈_ Vt$Blߙ,]-6q(qCsBF nT3/s3Gu+4^/[fbkA;G)|hZfEi&/Q2ATwAJ]Mrx˴92P'5wDjfE|auh/YCezKF^zXw9N4 LO*t=D,:ۭجn/>OaC2rv6=ՠ%1OI=V38q[!hDG$/}ȁ}d=Q^ Y =%~d%}ŭIΞ$cVLr*||G=O1NM=4Qt'D{jUXWѺ?*Jő4rbLuh#ɍY暨vVTFё: Lz}n3IHJ?I#wM7meX˂o}OM\ꑭ>P;5:My0`3?>^`j_.α0{ܜ&LevѬ% {4*gI{OƷ 3#UL sz.d*|tKMn\ҭ^IȌHi R "vrj2~0 OJ%FCZcMdN?:~k^'ކEdAYΜ8UgK8t }`s@9QrF.na)U;gE+Ad(ā .%X^ZDy4X0k V:\<H&˪i[ ξNĞ$&,lw>UK_holM""wbIN|W8?4Y"dk`d u1 aH6 D)]JUdZL#O-Z$T\7Q)B' Xb蒺.&X//E+ʗH--ѳNx}VE:]oNvZO +Qo5W]N8Fc2:3Y+`WIƗ'k9/zdoP΀~ܶ3n8a0.JI;ETW}YyҳjC~OIoj)WU9)~FUWxh{R$Rf:g,#܄b.yIukW%w~[M4^5sx?upE/AIENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/0000775000175000017500000000000012625666430020127 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon/faces/user-generic.png0000664000175000017500000000632512625666430023233 0ustar fabiofabioPNG  IHDR``F tEXtSoftwareAdobe ImageReadyqe<PLTEefiprvruyy{npt{}~lnry{|~ppplll:::444555666===BBB???777999@@@DDDuuukkkrrrwwwTTTFFFVVV]]]fffzzzXXXddd|||___žhhhRRR;;;<<>>bbbEEE[[[QQQ~~~xxx}}}yyyYYYCCC{{{```tuvlmn}stu_`ahilmoswzbdfmnoUUU[\]jlpuw|\\\~ooommm333nnn*! kIDATxYy@"UQ鑶413m U6M2I$` .z , ,)+Ř۴9m6iz]s{7}73 s>~~,t"E =j!,b10J9]ijT5@f1=UؔɨIO1 JJJh, T3p6%ԉS.fz7Z'`OhE%UкVֶ:DG4[fjNvuͺuOքΘS\ 4 +;>8%C=X  %$ae3NLdEi|L{'U`w7VWoH moE+JT*PnZ5i@|H"#H΀oG@ +]O+_ CR iDnlYUR|/=/.9yӂhm ElP[D(yB"dHNen15 රсmd|a6 x^Oܤ*zE_ '!*{^Fg<2 t $t>Xrp}_AvT|H*0y=4J/hUʏP}e^Gb#B,w}يㅀrm9᫷Xo/ T%G ?׆k:#c2?lF w2ST섏OjO"Rnvȵ jQGGDʏ'&XM%IF l sv#@&2% PB ߀sd[ @Ә3߆|*g3?$w[yBFJEpPsnzP9E,E"L0Anjo&cT=3_@NtXÉ~;ӥQ yx%@/}tH#@*O?tG=w̡NLXy" fUx~2Eȳʰ%$hkk#2FLMzyN: PUl31d@)ԱKP|NCOz2#I` ¹u[E[$ '}Fq$Fy$nS]ʛh#E9EuuP,@?"o^`l5kD C#xNZ&wa$p{oMH}hy('y&\+1KP^pᓹ'wX1+ŁkGĩr`VoBw fr\(V7৬|cSG@s7l_zXRPMc±Ӭv4mhX#kwJΫn}O ***ZXhiaz ?'N 0h ׹i"M:p;^_Ixh ׂ%Kx*l~j8%Wop2#k8C^Wk<d}*`p0e8}2wBbUΞ|%&Pn"VR9;UmFZںjɫrєc=2ҔjeS޸-%y|3x3@Tp:-Ù&&,Xv uHq0 ioȷ u>1gPXXX2)Ewn45{=HZƦMg=\ق>ooK 6?_rށA p'E:H*+vwr=nMf ^W~o|=nS4mIaisTwG>d 5~II7F7"㇢6@o'ű?*.x v2U(N4 I_s2IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/6_butterfly.png0000664000175000017500000004142312625666430023106 0ustar fabiofabioPNG  IHDR``w8bKGD pHYs  tIME r IDATxyfgU{gCUJ%$ !ab2vh+Њ™'D=}šB$@BƪTo}L H置w_k~^^u}ݾn__ė;Kw{ϝyemqV D@&e$$&*ǷM7?!?ON7Ⱦ7ҍpX 8<2N8/iTcҸzwq#@D޳7_O͓[?7Qh yG !BTDH}V2wh_{䒩ڳrY2/|˛|٥nͳDž[( s4"X-0V`b 75O9c<~%&V6^'J R &+$$v$KjWk=#{sX]ǿ%ȧ_ oBK7_|gwT\_Zh:wr+: H@~1HBJx/"_b ϕރ=H@Dqq l$]SS .HP>|Uۖzo{~G^qw5OU|dĉ|  [RZJ0* p7!`¶I鄣"ŗ!j iQ#j5rb!a"[;r>(9o9zkW?}yph}Ai,N7Q"{`Ax dJA%֓$ѯ~paigњF#}+5n܇NiBZ\$ P 8P"^ {1'|-JR/;ؿ6sَwݮZ}SKf K/!~-v}3K/q?+3*=n3}JheH君8#N"Pf2 ę1^& #mAUᆛn|}8e_A\ V*yN!ZWZw# 2t0u:O~wݨ# BiשHM {5s=o'Y;z]ϿixSsvqcJ((.qV㐰UG[LU<B,:52>kR:5Qox3OM;O98Et p T8$M.p* Wdp*ĥƱo~#.X?Ϳ3okG4 o|2bI$6q*RViff 2~P6o5qcT^℣Vo&oq?ocZ:+"%JkGDBVہR:6jPNM .K/i8_NՑM<bl f Fڏ`oz|nN'A淿შb 1~,‰39CQC 41(B ={ ~RDU| ¶7H>rv'z%^J59¶%ڂjʸM/MmC*px?R#*Fnߋ 0Dݸ!sgўvr!\{/DŽ FD=/t"eQqVkV1;cI 5VDݜdxeϰ~~Ǯw zM;WW-Jl₋x؜P(\]ZMlBp$jV(PK˨NDEAnD8%D~_gyO,^8_&t(  >s/j\_Aok)G 3Fn,CFD٪#|K/rۿ_~0/x36F^XTW ")""dʜZ ail\ԈOGn߆w 򡣘=(( @SG/o{tOMsi?釉2C0 \v8romnȥH C丄8% X[%p]԰|6_Xlo ˆYVe؜"ZLħ~qGsQd!D@` ( {g˯O8_xVݟsO]fW6w}|K V6r㘅iEbv .nN_WC(K05hIQѪ*tL` w;{+9 Շ8J\LI65MQKqA9duq#a m!yaKPAk4)ag*>Yf`S)=s܅o~`]ݜVg769T b/U%hHpn1 gd*Ea/+A\#!.*50_ VQvov,͠MVr~ɯtB)~ ODkg%,70lpLWpW,*"Jt6׬p>U񶧓q|{?KW7o?ySoHV슝1HIcc8, ݀G&#L S 2>ϐKh3"Qf4.P^" +y)L\Ƶw0czvbD >2c_XQ6t,MtGOc8u#ɴ_}K%@Y,O5"}Ou;|Egv?=nŗn6iM=ş["3PF "KD+njIc1Gm={ys w},RΕ"5cS Q<]3 {1gXVKCDoogA6g/H]>z0zM$W q :IVmW~$3+P~dzoP:' b"읅U]e:D$u8ᰱ#b41AY r}D1$>I\5$f~+74oUzxj|~L5H5JCAm=Edx"*u0XGΜ+'0c "tk [eKY?g43_F7 s?w ! C˰Rb,&8%:q_8 ~cI 6 krp9Fx'gѽ ]r]'.rt#4_1LnzNv2kYŴZ*T} V iu+!C "x+1ōvjZkAo͡20犠> 9] ;?~Sw/|xٍ?`yK9? -.VxF,C!=pثd97Dn_{D A*@[PobwB՚%\Ď$KPZ>* IP;Lxn?qR*)L N!r7"!oNKc|ԃĝBױRؙi֜X>%q={-3xPToϕ'K ŎfeK *lhŞ_!Ȯލ/=n=5Y+*B1~zF\1? *.sU=727<TG.Rsb.Qp @W%K. c,W;T,b/ ڸpI3ybf mK+oz0\V տOE+n"w/U zB8p1BQw߇];g5i+fgrmpya}d=ȋ+' VRC'D }xlz Y Vc@:b* ɰ;S" Jk'lq QM '&߾%O7"QD~gIBu&M84 ,T>ݟ?k/֪j[l$p+ Ǝac rcžtuX &R W{8B D r⊧I{ D+gdž2i@K&sd;oV*J,$yT+g^ )RKxYqHHx'f[T`]\Z/ak]$3nD/#\!i/ LGo<7Kws A܆Z܎1Q  ʠgQR# (Q04(0ǘ"Vx)F ֆD:"D!& d'v2Gz.*@Z.\qWJckp^c?3SX{uR.j^i 3s%”NCoJZ yԶ AW@ .^8Ξ_[y+M!i/fܶ bt(4B Di#xXW _[V!k *Ibl)"U ^z7Q=q'1U|mx^t>no?[M᷿W|J?gS&8yZw?"/;;QkU)փR*Hv+zpE9v4X]exA3O!c,G~gC_rg_CR RdK&Z " JG8E& 6$ 6923aUZD X{h~7hfp7O}}3 :͸Uvz9?[ƫdK $"Ua{ Y)p_FCfq \6[p. $F ӸK)"eBaz!_uOͶ]OyJ̤DG>aEBp'pc$JT(ZBة"Achdgk7a +gA  rۦ~RE&*XA[fFq¹~E0k!^Mq "&gY-9Hop$ bllk+NT %c>ҧO\'׼fjc諒k7~DkAe8iB`0sXdQg:xN%sRDc|NHWndϽC$V^eɅ$P-;yn聇),Y|S6%r41BJ:(3LoLN *pK7#sw]_hNAN4f_FGʃ "/ z[k`KJ"-PFan0{cD[CC=TwYQJ#/%#,J@2G4|5z^zVx\36[lB=$4(cvBCIN:PرďF~7#.Fz4{{{yQdm4T}OC\Q#')4 Ԛ)d ]jI$E'MG-&e9 s4d QB]>aDկ爽C*3ԓL;|ަ|&TZaYYF8 >YjCeIRw 3#词hwNTV2@Q"##ARamn * 077_m* BG{m"#VFhED aQř0y&%0]CwxAӘ3:.T{oRdr8$Xy_-^be)`Q`~i,‹1!EMJ 2+m_t`<9茱wD² v[hGKE8Á/Vuמ(IW%`gΟ=v{[aB)%!XZ iW'o%CT[Jݛ(*:J1Lu, SMe!zuC!ϑ7gޅ8 aD "/a*a[|T}j6#ygxR/læ!^yhz=։67sQYPP kƂV*Z-cRPhߒ2</R!U(~'k_M^;“̘4HQGo3tka ;jybeUlj\I}٪JӈS!rk8G1g cU MMYSF)g0Z#REV!@]Ϭ]"Ƌ`xJvP1RL1t(+f+GZD6Y dT58h ,J%Y|_5 }߷%+0 e|),z"ۧӠA-L7t@l/K M Nmwμ_ IDATۄB6K+yI<>7{wJY/<8O,S*rsK^*t\#vp)hUH%:*I,#1A%"@ M8I\1f,B2P}¹s׿yW<~|o7k)XvЫm(s* Ԯ& 98 :QF'H nQ[SNaVI Z*PKC $ }6PzwX P0\Jָ-dMsD0 ( O9bЏ3pVÍ͖DqMPpB "G k19>E9LҠl'gf^~{V ]׭5wgzuDD,Vh([aOXZDȉ:.teMcb$nRJؠ6-&V8Avc :@#@4l2rʸU) 9v`Pzat/paDȤ|OEmD(QS3IۏW?ÇU-K 7m__2w4vm8RY80WjBT[~k!Vb䖎Bu|Dyf[ $ p%QgQZ`/I\H;!QJXPjG;>@w;8*Aƈ_h =e4h+K Mobz=J<vjgp?<ʛ]= C:44FiaX3$GV]8r%.Ie @`R#m=aEA<舘M8lPŔ9b{@xMbz}\ oB*4bNC ,5vx!%̠8aN 1?O*nrqLCE[SQL:vuԕӈ=MT q?* +QwK?Q 7rv=DmEW (QU N[aB/q5Y11YkPOB6#wLM6hk&'=Ny^dV FAN';/k]䨿5DmG"DcD!&gt*ÌZs/x8v :|+fAoݍz;bk51%.!j/ dYm%1thS.LVQ3h(l \$ĺI񞏢KOes1_'j,EL*( dx)N$īWNa,ƒ4YhD" \:”x鐹+1F0>*"n!BB^şzo hx^};ѳ_C D Nj:"ΞMLt~+aj )@V"dZi'"Y`zI^% t"Ԛf3^KIgr|nr"'Y[r & Ae$/DnB5eXKqI D9!2?mB{a50Yisw"#whXCƫ)!:ZM(-LgPc |v|rʸ:TQ #Z'4~x욝AWj⼯Ùao0L4SjM@Iߵýh^?BPjȵ6>RH#Jrkӝv`^%!1IG9.L#n' >jUi?{\ G ER5)p"*w; ;vƣrk{!%69GSWPmBԉ6E'ގ_hb:H4.# JL^2{J)i#Df1:mi#G&w,1ID"4c}@ "R@KgqawK[&UeñSGk&[.efnmrl"x)ƻ WI:q @4!fT 3&x"$O^6%SM1rDzrtWR9E6$׆8-4Q09G1\o_$zF#^FM$ [xhnS -(AƀDrqD }"zfz y߹֞sL]U4Ć>0?٠i`# 7-4ѕ*xDϞE^{eUԖVt \gjnY<쬊;ANGGԪb_ҨETU5*BHDGC}/3KئGUp :~Tx*@)Dlǧ[U<[%mXQ@1s̼G7_d]Db]lШCt!frbaF 6H! q*EZyO"y9}/y@GYșpzE*i;pd sMK.E *grpdP=D Ewc.MؽML.p"DG82c"D!& ЕQ!Ԉ$ܱ@R\k %rMEkrM'>5?1z;uvz!lmF:hol [!"@.ڸϏeOjԘ czJ8~15GăT|9x8΋0(=Ű`4㭡%]g(s;q"D稬,2Uе=)JP7B&^!C 26 6fLD-qE>s).03{rɓCBl @!)n*9xјq=gD&0{Q'ȅ9\=2nڈ\I9>w{?{1wͨG?EIwhzUY2߼&/58#Lā>MTمчps`6ZUv)Id~DD"<֖Q%_@f!>w!@bOjzCdp b&O`]⎧#&;nE<0pO=I 66@ @|aIf`U1|qȆJՀ)s J~WW=Db<ӷ A'%CF/z PIQ nr;DuJIL0HKPY3[+BuC߃+{">n!o8\^ <kAlVPއjzcd&B&I83&8h`sz{?:Z~ե;idpFGb"gqמ8v/w91.N^LcN% S]Oxރ~Qy[1p1g>EcC}B?]O;#DZZF B|n >Wָ_6E\s&9Ybp;Joq3=APm6FdZ|A4ز�桟jM A!\ 8E" 8$%"l!U$ $(f j n=wsC>T$u )֗:k{X+(١Zfi m}OhAg[cG+Q:VS1,&3贍iA:o|=^G#]C.> &)LV^И9m9o0b;Rë"?#\o(Zlj 2pؕȭBS<+8/p,`u³{ddx2a R8O: }dRC(W^{̋CEu qd!1D6vz"6p]87Ą%\%$6,22su]wynJP񷾂d5$ǯ pdu{ش{x}+1ƒ[t!jC1'CrS&[ Q…6p*T6F76-cdȵ431F v{_z?0Vw4n~>zk݆ġGym!u‹ac'|\:6zj9?RTYꢢ6'Ӛ <-rR>llq.,\(}M FMjGQ5Sʸ@> r*@ D ^쵱Y}K6Gɓ`#ll Piy_l}CP^?Ti!}4% n!W ~Jjֱ,ˈH!bȌ(ҜA#r ?U'}A\(xD _PIB`pxYayYz 3d6q S8bqa לDiNfq(UF Y>aOk;Z[2 w+`)lp :m(Y\# |$Xݤ& ޠXg `Q*G^ #J*qOosOJ^8-H͈~}F.N*TF1lk9;O^+B<Ts+Q$Zosl]|};)|1yb 3Q#K'Qh`)\U*le|J(޷oEG %7XDGP8˭JK\Z^'&h[|3 L1VhkD-T JN Dm(1NU 25yn01>V.4ץQӧ"TnbRh-#::*ס< Bvx<KXc\1m_1 >FI _c_lV B* >-롥,0RmGl,"Zv@zz q&Z KeV;L(I\2k=r7r&>Zӧg3RF Yn_…16i `| 7 [6}FO)J\T@@l"^t*6 YuS(?d |Rzv16/l6.lI#= E*?}ae]#{Qy22Cs5]؅-ҴM'. ɅE=_(Qh;0DO_ B;G5m^_WG箿uD㒉A)JL( Ny^qu%,νO?ΐrH"VpAւ"zvFфxf #eAm=r;v^5;[;YsFapdY`f`Ȭb(.NV=PC)8J\XB_xHQX)I͑^s7|dsH=r$CȐ u[XS^zpGжZ#Fd$%J–2`8w6&(#22·G=`[c급)1Al+tռ2Kبy tI^wAz/J4_EJT9#$Q1>ּsijc^Mh^M~HsŒIQ)w~Zۏ:b$N6{جള?[AE /`g;>\uoWpj8EQDXZԋPtf qdD$E/mOJDÈfp)o̕>Yw?׼1;ȴ:(%sXk;0\ GA(U !~:OM[F}'>i/z"/MAZ5m^: 1,ARUWyt¬#;;E⸙[RBRS ?hTE>W\·L:p3$#J]uo"z"R`AEQ\mcsޙKN~σ/'u |(pX2+!zIr1=t֠2/x!Ct6'5Ŧr B%G_9]|/gtG3S#U¨ cLPւ{KFؽqO sSj jU4' yLs <-c敻W_ogW]Q鐡b*uK| x>B pdxnHr3Elo#;k߹UH'4FjIMQeF||S]m6:75ڨU't!Q\""8x MyVCn6:?`f"s9vKZghTL6Yٌi:,CXF*ASڏl{`UzxG~B!xQ@Dz.^^ZNw̍[ŮG-- ILjiOP/ᱫ0fgs{8IvÇݫm<kokt(_p)X^}4}o?`kX }J5&KȌ!3\k5 A01Vv|r{}l^{ΙLrm wR@QN"RAaTg8!i5^{_|>~KnNOhLZȭE[68{SW}IDATd_}~׾/M\G<ֽmo 7!7gsE m0@ksJLMM^q}կ80}y:Ѧ(rLE` )-~ZBB_QC&Ǜ38\ů_y~yǗ׍R[1uhk1Mzaax_o|lCk]9Yl."i#(س͟;=oz+ak"7 AѲWal%$R |ߣRM~݉__o'תNI!k]ἔlSOq>sM4];{)&$%JLsp~n/g:=KA\zco_Z^̱N[@Q&Nퟘ{y/k7[zMV4GǾMqS]y2'^AcGkc}lVnLNM^<3ʏ~게_rozkcN6m(%;joz?/ .!=Өا[W\O>+~e#u>IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/0_cars.jpg0000664000175000017500000000767112625666430022013 0ustar fabiofabioJFIFHHCreated with GIMPC     C   `` @Q2?07 :"(N|V8rC @<:!pk9RFd\ftA.jXVzd"8PPL:2$)ώ|*$hdm+IP L%1ʉ`iF GF JP TSAa@ ^)& ᡑV&?&!" #r'"s; XFG9C׷ayŘ%گ3sjU5$sNa 3{9C/ IcVROK5rZfq+DѾ3RL>2MD'ye|탯 *ڌy1ȓW$*3Ӂ)*]k7";fNjyk%lU¡&j. bkpm=Fb*؅QRuel؈4CXc Eq]у]voky~D z8L.6Cg*z̈́=X8/RWI`ƫ((L"3\`!/Ě<>Zr1wMR5$1-L ut?i^LNe0U-KV)(i8d4 =zh0~['p?p?7!Q"1Aa#2bqB 03Rr??,1t'?vkfk'K+m8z +C cfIrjgI O6Jxڻ#Q9u#R=\㗿%]LUBʾhyx> m4{V$7vPcsZhzku/f D cZ45Q;nS!\HrvLtӆD5i^4!K뵩ɪz^n6fۘ^D9+ڑsȧ'ޠa{=џ{a_K,Sm-1NU)nhkh躪s^wochL8E͆Y?xsFGYp.msIm7*f=׏Xt&ؔG6yk! Z cFXZK[Kcouɩ Tr?H#wp@4$WQGwChRh-3RmVu̩"Deyz&ISZ P -;r?s Op9?5Z*=$罯188VHo.I>'Fƶ@;~T˾ۦԣoU,T.WTQV*ܹsNo\‹|ÞAG~cIG=nLd'$EPx,aQpT w!lUf/tpX{+njdQT&!1AQaq?!D8.2C'ǫ eg`! tĦ]Qd`H=]4Sʏpݚ 0䯅(VP=ϩ X~X ;kc8]e\MdX L2~<)gٙE6lJጐPb(+gǍaE$kq P]s#. ~M`1-{@ħT |j(g}`W,׋W%  UنS[3WE@#])ޠJ s̥s.e$5_phh[gH7&k@/fpRpwG 8}60Ǹc'MYML`Knߴ(-%杓b Bڟ  I I$A  AA$I I H@ p?p?"!1AQaq? \87U^2ydfwGnBz,a(YZq){pk3)00P\hȉ9.F<0iX\3s#jcqG-9˿F{T _"]WΈt@_#A7N"PFrSDP0{XSx|qS.J9x.=$;Ac;"p']6k$piaIk z0Ȩ Pv東C v/|yŕ.U5lDN sP]90Q0D盋+cZo€ix9>*CE*_]gN Xp.pS]DZT[#)V1{.5nEԐLm]ynM@].,Pnhb O4Y".Gl0$AS%%yp2@Z<}ej!ۮj5t"8rt$݉B!\mr•V*/ 2QY.Mb6*hjHyjCZR| %* O ui>&apiOr|xt![I퓝)?cN7fVGwhCϿltܛsCinnamon-2.8.6/files/usr/share/cinnamon/faces/7_fox.png0000664000175000017500000002224312625666430021662 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>#sIDATxy]GuUu}]j-k%˲l,f`c 6Ɛ! $fN&!8,$a3fm 7I}W^_RU}kYƒ%O>zwTsS%$&1ILb$&1ILb$&1ILb$x>yTC|WEIG)v]^{\őLZ Ʉ<)`3yN>C3Hvf+!]\`œ{r@s};NDJэ5: GX/~P tg/*a-+gáë\?:j3% J?{o!>qZϑbyBko`̷m!lsOb=sRy%u/gh.o\+j^('YO~X7_p‚īq{\W"+bGc)G^v M/$@CJ%e(+1}3^KIdsN. ׭Ū1Wg2ÏxsλQT%w3oURRX6Iown+c,ҕLBp#sE X 6>a`ޱp;񻶒]YRUXmeM泳1}*M!Gx7ӿkۖ2)_S810ZTeltk^ /&Lͷ2m"sOkgѡ?u.k_J!xR0W ͽQ/"kY?/e޴xKd,.qsRY"-&ou]ޝCESнrgtr9?KG$͢P(!11g1x*MΒOJas[a&Dz[#X5~vj{F fl1d:`c0Tcۃwӻ1jJdyft>VQi?o8aMMgq](;GuHB Kjr c:wm("R>|mv똺hdc 9QBn,_9S 80WFBQġ[q(l_"V"b{t_[ϱ724|`y( \%8;iV5:BVyhkZj>_Սk~hU.Yf9 ($WgP'< 3)ZXc^˙BSg!d~{ݱ1T7[kqtr,ZA"/1k3G6VHLϡE S"!K¥am7E\[;67007}cBg)˯sνd*h; s?GVL4aF^Bڬ$p=_!XcȰ o\2\ǥ}T.H)~m8Jixٻ>ɜsVR.9wD2Ik[;Ti8U {h:HD3)}R,B)!Zm^g}7Jֲgvvm߆wds\}9n`x܏S]ȰKپy#: y_5\s['=W;סKñU`8l\3'l-cv؂nAH+^N[ܡ \,E'kX0V՗KR[=K8T*~V?8BLy cAXcLh?׭5OonH[2 `ظaJ)GXlk%m 1ƳćƳ] X>((< %hXQEE舠RBIњm۶C$Tʱ7h+ts?v\l+{qnrKE1)#cU/cT]IKEDksƲt8DV< ڔ^,+V+iV>E$ 18T@ֽZT -cv,\HaR]3oƊ%|W1:Vj4pZ ȵt!<6B `6` jah+ŠoYā35N߽{.|}~.R޵+T]ZT Xc~--tvMXq]uq]|nX*166LyX"3YBih | ?G-8u=c;1Z  X0}Z ?Zۭ9y]eSlzޑl;}kzalJrF{wc\.]E<mdL ()\%WƳg & B) Q-1gf'GzjTa30D AXϚ7 y8ny75m&S]=ϯOM kiCD*%p<)RȚxR-М1EQ""~yXZA(L[PT*富FyK?'۾d?8tL@uqTLTxx4BQ_w zrgݼ~o7pb:tC.gRq]qo!$R*!ńp5nU6?ޜ𩇢mQ 0Q$T*mF)\p =i*Ϯ]=;z>Y.?zn%~JJ&jb Āئ-h4g&8EFS-J,X0 ntB:S(7c׿#icta-դ^jax $Bښ h;PĽ F>Z~eG>m`` 6Tԙs?KS vB /5{ a&x7c4DkM#Po;l.DRJa_ݔKWמP4|׿m[_}ĂjUctZ[ !!TR9TZ$)"`#f( Ȋi>J Z:ARȄ2)J)SBȔћE臾_ϓ&|Gc5 !lu(/Q4lF>yNHſL*J ŏc^kac1z kJyJqwwI"D:&NHi$؆ROkҨqJSG~riȯ0.B>-WюqRR9-RN)4͜(;7=1&SsѦolS_aDcO*V=Ml+ @؏~/x=ڎYs->W^ƌH$RA*HhN4TO($Fټv-z[P)XXvb>_gǼvS0ѿ{ˣmZ̧d)TM{ x+G[2k:w^r9CKh߱R@:.VT6k5g.^"TJ9 @̀7{3r|Ba? Ld2)綔>n\G~@Ww: D{)UB +ƚ: ׇ?3 ,Hs^O0xqѴUKs\GG?4tLm'J=ښ"t>b7uf # BIH)hzZ?W>? ;7?&^V/F9=;^m>#‰@FyQKypk2/qW*x^;VO)rv_Jj|cevm݅L˷#@9 Wĕq( ewwg-;Թg3a ,#=c_n]3xj0FkK$P9;tZ=ۘ7:R3á#^N>VOR=%scrf27EXfD(dr]gdh6̦g.Dٙ J_m||?H.BRr~O)gF{P4wmDkEaP, x)'I%>uϠא2G9[Cz3駽՘񍢈JDh_2mLZ;PYx5ѴPOA ӿ!SgP,9wrxSCfJ7jjCTZ?CwMayo2[7ΤsWqu"dq^p]uUFR.RJф: B ߯08O~Τ{xY(>H02m쁜'^ԯ~@k#D_]/qrQa'ĝ}~jD&.=O2+#}Z̧cb\E9Rœt6MWTJ >BZPZZ[Xl>SZ;B6ƔdrYש=K:ʔs/xp/N(Ku(clqW ZHUc}1UQj[gRe\W K;l꯼M ;t-=d&CGK{+Sٸn-18V?Kr:ڧ0},ڻ:Hq1DCk<23zvlv¼Z ={=n\k # 4:N3'yIq<[k1Z0JpUOpYʱ 0-N7 kHwu2kNQmCJRL)ň#CsǏw{fe,mώCXWb_PIgQ;yD~/MbțG{򏷄["uw( _싢n"Y[xJF"]. {jN~UJ"K6kTI( Y ґL>ɷqkEqpN"EJ8gjufVL#H(]fCCvӖ, {-sUB04B:oTA.H({&b/YH&A:+EbDtT7[1YaL;֭gT2bUfO.%A`e s(١v*wΜt<8DE_}6 $k>pBkxӝ]ւ⥳Ȧ 8iLB:aE̜3Fҙ \W \S@4-Ǻw3#z?9RQR E\?l l6Zsϣ_*( Y:Mk 0_' OxC\8%6Hk'i"nz<8);;l~݄6FVZ}3Ѳ)$ W/R,ujɣZVٻgo閼@opd\Mc4àVn{x{>Ҹ;YPc1DVvBb e-/P*[X"s_AQDƜJWU2Ve˫}}'g/[n/ȍVw_|W:[NL6@_m xab:LXA4Q+j'4JOΦ@ &41`q- @J9J\1&lb-ϮIn{~2e ]=s lA@\ص}N` ~Fkw(i.#J^nrԔ8i߈Jvlc j1Emњ j .$.s_sĶ!E!@! O^) ]A=T. F46N kpݻɇSEІZ@XFPӃ6*ۚ7n5eV} q;T*D&2 h!!#W/;R\d}! Em%Z%gLOFa(°h.I} zJGwlך/*6vbO7M{B4/Q5M+sN*Gє}M??Rk)J.phd6{G# @khs)U#ȦMZu15!/>ɊQvJ}}m$h u] N[Ul4X,bn>jFVoS*Gm-`,w*ˍCw߹Ԋ\cJԛ[a(<6\ %7UG !׏_߽XxZ轮_vH?B]fߓ#OyϚ/ !qTvEc0~spzm&r<+$Т<, ,1}&2PRDj2%F|EH.{n==c_Zۚ6j'_2Vә ֘@"a`/-6[eh=%9ƝҤAaW5䯬_t$Y)\8/kGj|rtD ;ݲצܿs<#2$#PnOg,GN7/S^wA*ޗ϶Nl_9\Rt#V 4XvtD >3߿?7Gwgt3q+vX6p喊fNP^9yR]mH0-S:m 0\*6z]7*E]Փ`cAn._N<@_zE'EXyoʒ DTepNv*L&@.k$GEaNJڟ=cd[EzhvͬT51 QM*Y+<%bpE3ZфUkҞGW f,[shA%y" C cضB/gRP͡z=ƍI (gye՜ܻ "& 2X5bT#t덤[*%2))ah0:T(Rd3iK>$4oA ⱃōY}t{8)/(Azvz- GmjƒtҒOZRh}T}HO| D-e]Y/pU=BsAƾnZK97:ދҵ[ -6R ¡QxUi:ԩѬֶ0P-U; 9%嶧<:2FbH ۵ OV=`fBlMJ8$qdTZN0mфyQPQRi0c9NI Z3L p/Ua8u0 & )IRJR2I(r52nk\젩ה s2;m\nFJ{QO I$<l ;T:CM't@Qcf&V,Y!{<2=`yPHf[Ci*1Ơ :V˄%ZJЀI2i6 r_aFNǼ7Ii4sNxN>5eJlI.N#ʵ[6X6g8l$#-2kݿP+rGk%vCewoteYe=P#ad{A7HYA*S 4X@(6idvL5gtQܔw^<94F4d⤇qݚ*FrK)5ƒ셮Q+v7%5QGu$`WX;VWiBHQIUB݁syղ4(`"1~e0[L-#5!Q I.7ıHY֭%XmD@Ս?"uU55ⴠ8Gҵa->- [ŹRF@)_7ۤ[mK!2g$Mq\8Q|@Pd5a3ɠz}ˋɔn)yynj!ʽ0TU9B7)$#M\/q˜(l=/$Wޡۥ^r:BO}*7DwZZG+`mbJ)*Ha\+$p>4 Tv[3҇.wZ-^d{E ?:EԎ ZOwjn4kw9'2Uhm6H@J8s?8sS:k.%<) zFa'+R]R-=nzN9 B% ^ܛ v(S EѬq6VS-NF%$:7~ɻDڰ+Q*L QV_mn6Gm_*ft@ ?֮3y*y~enX>hޓ=ǥ!U7%rxgfbѥbc*}1r NKc1j(>կe:f˧> 2n 4ԨA1>M<"mAqj)I'[x2TxR\% 6$ՎA-DWԡ\|JEZ A.uq6:o)Chn}|7fq&!707=\V2ƲRŸwq yd%3vmtQ3PxEkͺBɿJpɯ;ZŒ9NG5-qIB^*DW7Zsw76'I(MIHNN~#[֌Fy)Z6[79DO^2x5Cinnamon-2.8.6/files/usr/share/cinnamon/faces/5_fish.jpg0000664000175000017500000000623112625666430022010 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``"7!1A"Qaq2#B3Cbr-!1AQ"aq2 ?#+-;r0A9LA91_*J }[r^B9U6p4+=1cN]P#?aJ\W['4d׷x$P=+i[̆޵MKbk9Ky vЅ'ێ徸hsl]l8$)1T TI苆* lVU k(89hf3љ.OLR6;9BDWT֊GplRTr{'"⑀e:%D1NM?=5iJ g#)9|&Xj.2Mf  W;&uHQ&.TP޷QK)?R(Z)q9cݸEwKBu+֑)r(SU$<=6b%*aX$gzJXf;xHHYߋ poZ D~H*qL(+ʡWF .#4*y/@d5N 5_8)܊״WψIvK>uxHI#׾vB6Y$`kaĶZ GNATIǥ*P#8I2RH*JT@$|hlX~~53Gk[@ʾҲ-*qĐ{qWme{Ɛ9 UQLZȦ-)p7~ z۶W|'5x1WHЌ7ZEZ$-'irnB@9{RKmlRpGں+XQ)wt 9bܤokr֔d'j++󛓪a+Y'jH|-Gόfvj;mvp(,N'#*?jhte]B7ry]*UxZ=~1_GJVP(Q }sGdqN8aBjυjkJQɾU.|Qu+~ ]8TtPug7@;JG8Wn3;$ w| ^Sh++r!"|<(pcwymxv:~ǵda*Xs>PRBsxZ煺 'ݿ4l內Źԥ 2~q;z}˹<݅IJШn:TK29xY*ux2ܚ%xv]s϶)Rrʑp湛Ưe'V;YS\::\v3eۍ!@AOku G)Fq^\u .ڔ_JcLd3BͰxĨهQscO`ޱMg^_#1Mi=zOL9k[iYF0A鏊SnAlRҙwou'>iwhR`>҉fIk$Ijp2rN1MW`i ^MSkjBOm#<.Ad(#E0Y{~͋A֭ͼE_isj$&[!Pu>m<5K`)"]L{,!K ڷr}&#lc泴 g#b/9JSQح__k,WKCCr恍ͩ(R?`~58xn=h)s4iҳ\z*+:/yִu!xH gCNn[- 3 cĨaK9 |2@8HVU<@n24]QVφ3du-uo<8)G$ܚ.-vS9zg;Pn;.}=W \m6`2u!'>1K8rB79{Q>qڲOv2wo71GLfV[R**JNv ~9xFiN2^Z:CY*'xpzT' GV?rmEBUECSq!_{*%Ʒ,`j]xkK^ όE-"E&%~y$V$zڟe?̀H?'mP^8C):7s֣V/OOqJJ?UJ$qM隡Ӥg+ŝj-Y!\+SrʀTR:.)+/qG)Y?ޠXJtP9օV=h^CU]^߽|q(=|6[*zZ@9KR€Q{U}WJP"2sDm@q*HrsQ:  +)&!*;sr Vݨ\ҵ%1LRG `K.h1$UW;SNW[i0A{ P2ScjgQ FN(lD(rh.9RH>NN)j\OZ_OVdkݺ3IWӮ"6:Q0rMTg_cp'' aRWCinnamon-2.8.6/files/usr/share/cinnamon/faces/0_chess.jpg0000664000175000017500000000642212625666430022161 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``" 8!1Aa"Qq2Bb#R%!1"AQ2a ?Wƌ᥻pP/[ZEY0 \ '9ƂᆛOídTti^#+1f/ RD"ĭpۂZYhfCoG3Bǔc z`hMCe*QS!P$VRPt Sadsdu? w ʸU-q |p:?=`=9Q:̍t Χ>ru]:u'}fNd^mg WRаVt^-tX ۻ'lAa$rF b2Ky'$E6*r?=>xВVFbTuAP,dFo0;Bʜg_(:Ml?˦wI' +%yr 0g[ w^XYONKH[?FPMk) #*=V4H竳ee+ Fv/A1z5'~zZ(9攷OQzXkT1!` iױ*u1a$o{86[U%x 3NӺHR>9 4O ' l pW9]omYr@YƣfC>ħS{kP^5laíWaImP Ig@)#RG}gI(!x0Z0y9GM &l^G`8#?fOs-l#j*!Rŀ\N>A@1ݻg8WFt~gC{\nV;I<46p:h=bTbU9fA~GCdOTej2)Q+T˵j4QI;J)^9O\g PO/\| Uuδ7{OSN~.jF<: `mv\7'OEƈNS#J9}2pYK4O=x[ ܍QʥO&rA'S8B Ac{ͺj+]nJ@3Z2c+«cQZ)]JqmSfuqFq猬`]Q:"),ӻ|zp*HQᓡWЁΜtתnVSVgc3i45}vzDG Ȭ2uTZm-m3,$q@c$o=~wO7qWު$\v'<;vۭіP2~a]˵xs:۫Ox89MwwM1Ji93^PTbtC!U] B`{ꋾ6nֻaY"s̿TCaU.S-CqӴ ez~>tڭK%=LR%_i0)*G@:jfFJ^{ %Lt0 y96Nl]JB!QTt|1zǥ0@cc9ǀ3 dgנwtR~ʪ%ڝ9]1Fnb+ ys|ˏ\0 H$/1RN툪3R8bz$(PĘN9 J5e|ER~,1-L HG9X`NT*<*zG9﫞vM;s_ C [|z\6¯]$㎦lD0=hyD嫞":fh웆LfF'cRk'[ Q>y4q`;|pi[gH%T,%$ ЏVϾTHy`%=IwCWQ&'pcnhEt#] tVc$y|~lpN}{4UO8YdVvZl~x֫Th:8<.-9ֽ=Y=\|O1Ab Cinnamon-2.8.6/files/usr/share/cinnamon/faces/4_cinnamon.jpg0000664000175000017500000001363512625666430022666 0ustar fabiofabioJFIFHHCreated with GIMPC     C   `` SC2+tWj uз"#1_ fa_yҎšMW&銶Ճ^"╷_gGރ@6-yB}g9%a =vOgL ҭ Ez:c,9VvCuP״pgEºNu†ҠIw6@x.M/L9tUUwA؄g\o"۲^`8o*촁\[w8M5<6sS`#@qp9 }ЃZJj]5LԈ9E D[$k"V|JMXk'w!!$_$0,-@ڥWZ|&+*`cvCTIj)NE5[fh/!6~R`e%в>oΣ qAz RCݠPL?Ev"ڳp>vADei>xR09>/5o4$V"ܦWKڅڽY $x/e}F{[@o+7+kƨm4ҵ:/'HY(juPUbSFF+5"=(ҁo׊㙩=Vm4*대u*d;&J2L-svѫqM/g^{ix:F5c_kBk`0RX7Y>F# :TmLf+/ی+$B+ D<Š36tK~gJ-")1N9, ,U=KCn#RX, ƠiF)!1"A2#QqB?J1;YbQ&W~ 6F &y'8 D+o3-~dLb1#$N8e8Sօh^3f8R#}?kmbyuUbkk?0X 0X֎c2`ezG6h6[<]E(_Uԛs9jz{ 7:B>m>™}gzL>^*(e71+ЦEvZf2TnR1Ѧi=+iU*ncisN2q3SJ֕ Ć` KYZO@EM#ElR LF1wk~Z["}Lʑ)HK`X2[?%ZNJ>\0Cʘb"RC6?mQ[rz~'SIF,^d\5"2[ vwMjinS!=oR񔇉;߉b7ScQH6yz.8+F6KST]AT|8u"qA\sm̶@1 4jA^_/q_ p9dֱkٔG'[f;BR`p\OA1Z@X ttSCN5U ur"\iHIX/7[й:Sǩx99xLd{ [kl7h;+^ !zl{'^ņv$SfzCP:Z 9K#:5K)SQ{[^ 8VS̥5'ahj%\cpEr6~J}oܴ}I4y ~ kܶ?-zTOsg$!1AQaqѱ?!fTD$3_ ;R*FX3U]C!QmqAkq0=kx6˔cG4E)YK{cE PX s-nAwQuD45E$gُpl5J@JꥢQTǴEf^-Le%E!CXLS)(C^}CM38Sq8ϱn+:]"!bu&~2K.h* I0ea0\TC -עzCGgBk_S\:.4pPp\v|Gm ͋~k.IvlX 3&g qV#e!l>eմA^eۈ/χ?qtx| ׉=beyI%1-,;i:R|Eyn2C&}CPP# b:[Gr&n lk@]// nV2otPF0'O ŸtT'9G%scO9YYQ?@`{%!1AQaq?>"8bASt (E0NUA0Dv3_٘PpcB킈tˈzŊGr/3S!T 0 "(54`"d6<ΖYz7>)>Kt8!B_ؽ(ʯ[;gRR8K x:ԗ]bkT[ iC}U2U [ъ1' $f*Ųh:˷H NþWGX>z-\]mV|& wٙJ.D2x8|ofu_P)e2>! VK f7I_f: U_~e7WYg'fw ܥb_92zI&eG'6"ٟx0-uB@RvUU3w+V]iz.{Y=aDe?%!1AQaq?rN`b"ȮwA&/2pp('aCɿd24i,fIb ajCinnamon-2.8.6/files/usr/share/cinnamon/faces/5_puppy.jpg0000664000175000017500000000746312625666430022244 0ustar fabiofabioJFIFHHCreated with GIMPC     C   `` kϲlI8 Q.KYIkqjpG:jopv锹Hi ~E|(qSN~4tI媭8}Dc4bV衱S4-S X}xuEZ"/Ց1~N?W{qꮢ1\)`,e7F3~>:0r8>tNA['ALfZeb K,r ,Qw5պ\2A&+n{*p䡰a54 R2T(]Yͣ &o&0XAhfӅc_$<'!"#2$13ZǧAα!)baۅ3˷˛ArQ܃#z D| llzUbrnռw'xpKh}ZYCėwiŽ Rn)P mUaC5OϮ{/0G1>N TYǮG^3;N&x3Sa7۞GqL׊ܳ g&j=?3~MEϭ`V#c䋅;Wino?2ֽvζu5\Jh:.>gix>19=꧌gVD]\4!b(؆h FsfZꤻ_1X5dMr_ޠ7~WtvFзX(Y0f?"!1 A"2?e[AV^iq(hk9\,f(K,OL#2E pTukGG,~G/ۭ {"_ȵE?|zqlB6M 2Ed[&Jf$8ؒHdeɆ9;2xDF<>m3bYcC׊rG-^35RH;#:#$cdM\Z U cHo‘B!@P*!1"2QAqR 3Ba?oK,. Rf;p!VTJ,״k%nnC\KJ4}ʊwg *3͈):n!A 67M*6 /hNP0R`NG a9!'PtIʪyB; a 'tSPNd!vB0ANlPiPBEJ|tW ;NSsߋj]&փd=!6|]GI>d!PyJ¦ƍn3amZcZJKxѿZܮ#[ZyĮCb% B3C t(^_L\xCg̯R$6_P6^!1AQa q?N}a}-&}~X=.3KWdr^xL1,)%e] -l c?ɹ˟ ײT 6|):o䝲ܟ۸7D:ɱgM߄.Ⱦ˔ -9eߦ#_IM9ϏۘxٶLīܐlk :_Pߌd߂l" Q;Q܌<%~󌉐 w-o!1AQ ? ?$=bBi0ELJpl9&T!ُ[;DZ[~gJ%Vy A 2 ;r^!dө'&,ѐA^^x׈)i\Z̙`B%" O(oA^{ooC]J修4ED]PJ|D@yzwaM̝FDiu э nNFA|7uU}y¸ й}_1_&(c iDZi:-Jnd7"G{oD´5Tn7lb@V5g;zZ*QnVhQA4h%Jp!^PN:BfeK_IձS'2Q7[7[8hC\1܋ '[|R Yh(T} Խ9̴%7Q ٕ$^qzX|Q#vdյ pA/>t3-KΧZqvl9:nZh64hcPlB0TJy^f} o|71Ұ李(+Shi#ӈkQ(qrl}R oRe4Fkւ >C)~՞F!㜧??X8M^DӱӐ(Uc5F(A?DQe;hmWY |ED۷1:_'*cv)TB0br,&K gQ * ;j\m7` -_"`>Mm8x/Xg[ڑn iБk3@ߖyW2z>&C9>ƵPY!T8[\BQՊ|[ie;ciһ|W~M̹DzERc#Ji*c&6Ck݀P:"!O!A *=T&>xɝ\*66OdI3f,~ -;sxޭO,Xƌ3?FTI8ڒqF?k>l*: w4kĪg_dAl;[ӈr$n~e+<;j'#̜6=oNow_ⳝ 7l]4} 6댔Zab‚}I*=w Z(K0'd^ykPTGw! mNzv+9粏"+QH(1@E`!cV!pGd]ʚۘUlgָi7PX:W 1p/o$C%#)N.={XzUݑy/B|;Sg,ql 4/ D͵.8RZj?8U)sV9%:BzO`]۟3J;iY>]JӼ8Qd4d*&ԋ5uP%jԈs=5)37 e`\; f2FVy9[q:3g ߛ#Մ|JØ6KG\}kVJ_ pΟR^;pZR2ǟw1ognŋPuj^JVZF/ac-%g=1 =Jy.KάLYERfHD&% +hpO>M0vsyv7 kk O pTϾ ~IZ,>Hz(MmA]Ǎk[F<}Uk^(!Vؾ%Y|<"BvU0A>IWf@v֤ uGR扌>4-;7mOm@㪆xns?5|i**X n9ޣLRGcO6[e'hp/w)w8yE9D)e0Ad^тs ˡ' =e:J4 ("[ Ϥk+i^0m PY?rp`{55y8EB gSqDYq!6(=@Cs\>ƴ`\8p#4A)EJ6ՔX J ZƦ;R >˒> |㋿˔Ҷt44ƱD0L[C gՏޝHS#'O82ׄ\f*iP&{ RX }͉oy7EC8ftvۙwZ *\$IFRqI1shIbC}@# |8 WG/|?kA Ev<4!2ǻ6T:{Yq|ۙ#(mcZcoewOf%ũgoDi0*TyKCg>E F ҂46!*RE𳢍LkObLۍ eoғPԂ0g,.ј5z" o@ւ*Dm@!8IrȜf&q{F>u:=ۺqIN,wy5B/8lΥA" % C?(ܜ ւ :&.jQVG>Њ-PlHi|T\B$::Xm倶,3~u(k4ELђU2vFe:r+o Oĭ]WSq %ZXpsf- u%I\Mz+uFRMzƻL1u8qNp|#[Ru U1yM,`c`cS~v=O>yY ؝~i %=!#u6Ř,:2Cp؏Ѕ'na_Sh.S)8j5m2UDXX( USȐ&U8XUg=Q&.Wqx#) 'pZ!Mϡ4vo~"Ԃ (ēp5^I~&]9w>p1Zڔth>Gt=Qili#JRJR0IFGqKq\FD#xoEqPΙ4u9&bhB>o0JaD^:Qxٿn3^`ъ)MYy@Q`ͪ3R o@ed7Q g-J  ^&2&.JLuZ=nC؊ÒŌkhۚ7u6 gr&DQDZ!&SM=&$>EkCx*8(wK f`tGaMnpqOB:̏^AkS `#L %H}"i Ft[kQYiQQ*UzpӚz)xr]#T #h?ags-$ޡ ST FFh)s6PhQCg~0Was{݌IYKSjk829#2jz~ Uf]q> `,K|qD,Z[g-Fi X]Bz|ˌ]Lzb\Ky'E";&*Dy :|z{LYGهNI/ GʔJRH+)NܚRDa܄)>  -:F0ӱ~W=B.SBߺܳ. Mu)8xk7⊒~n&OӾ^|IL\\D(QȠӳ}6q2$b䁇u` SqN4(ţ9wSxoLP&$tJ/ [2 WX+4K?HS[{`EmE"7r :wtqsձf ߦ{EumƚK2X!d[H]&M]Q|PǦ)ރ:TQl _y] Z\´,<(ORTN 4N i"D*U!*HSpY=e2,C+1 ,J%ˢ"" yJ&:mcOUhy)3JBK#/?__-ko@M9\ bJ3ڿv9 ;_sQ[~:WyNBz%+rdg+kik+cYp'iR{P8 eLչ rRso]h@1Y^-ȐIْjOd ^{<-eּ<)'T׮f~2`Iƥp(8RAmI%}5i֬ܞM-^9'*8kIKY<i5|R*:g;\e:wlQ+oQ sf-Aw?kgRvM|PBc 2]De3Ž {׮aڎ]Aяbw>޺ʠ9ݳ~N9SLxLT;}ceDo'oιp]Ȏ( ueLihF1M[p[ݿt U>CwWq9Lzb4 :O/.qÍ Sʄ(:Tjc"($uT}xF"pX`dzLJӔZ38\Gu,*< gx5m1QPF[HR^xVNǴN8hv+Lѳ){$h`Dzu&cD €J^gNYMVgXN:6s)w$o]NFa;1e^:IDAT|w (Tg]FdeR눌!V1:rƅg^Jߎ ءnF[Ƙ1ISF-xBA>$kERb9x03j"* ,כ{nj_\pB9* D-Z<"oSᅤ*~UIM, Bii];7E)M\ k9WOl{Dcq9ĭsx!703lzNF=ʟ2GÉҴݢ?+G`綗iØQ%iMM s>hulJme^?&(tC#>^`ޭ8ki5(*wӳK̝t>~hܢ< Ǽbf9C(IjI\@Fmh :PZJ#H-p-b> RcwUh1ԯ|K$my]v^]O._4k~SAdg*UM+8kWR6s'OqFە1&2qD:$*뇌aܥ0n r:Ll2D7DDw I~騬ܑ֚4u(QV[3XVgFeN.dhB֗y)$\Ze{c_/>Iʉݘ(G{XLV Y>ڄJy&Ma-wONcpֆNx*edCT^D71%|BRsyL)nH҇ Uː !b]P++jλÃAㄐ$rE4yאu Qbtobrq!`uIws¼ɴ-<3Q&dyȧD R.8cҌcʺ'[l@Y*O:=*2P:yrϹ!$,8~=['oG24 ϬU R-5ϳ;eſQT66l$?y<'I-aŢC)+xN=8ZyOY\V:H3)PInV:}K=ͧ̄vw1x_`V@ڧa`m>",™ϘZ Be(TB|9k҂xMmRAZ)"XU][!e7漗FkDa|3(x32i8;~-z_s>JLL7=u#娕N^|[d˚'8,ZLd]G A QQnٙɚ82i2DsJHk2z`Y`./( *ni,!?z~W}w0w_whciz*y)ToSϽ)p po ^eV;{sh9fҵ\sĎ{ofh*zw_u) ! dc-C;BΔi"u@Ն3 so d8<`'gOeβ)L^DkdaB{6Cw߃^1,ݦѽ~m KLXsW_2S+GiDw3::Ne{?c!ڋ}<@Ηo`8w\AJP*q?y! 3>3п - qNxuY:qbmJ 1@attbyJʃ1ߣG,cWt5Kna}Zh'T.{n wA[= >z?I&NGtfPRp_XtY!# c[8%!IF)Hn5lő.L6n]Ď1al'k l>?y  H$Uյoއ0c㖜jn& o3)ΘOŸB;*mju1Rclz*?z tLb[. ـљ:,  k㏩)k5eV`uJ>;!!ܹӀ\%"a4kH2rхgGbߎx?OSn\p)#wx9f4%$VV?t՟4)]Sst p8ڑV˙tlPQIb‰^d}rWQp9:RuTBnĹ^).Fs &0hbXD..sl^xA'Wkd# z J; j A8W `էa+HQyUOtL:,J aG*T b3ҎL+ɄJ@!m堰+/, (XHQMS )M{VUFK.hQow1Կ;|]ݺi X1ݬus4>E0iJHO@9_lA Y1ѥ>yC*8܈hUI*s:pWғkbڝ>{{lT\Fh5H$ ޼dj5f=Z4^eKhCt>Jj4:i**ކubiS #F;V-2{ǎ]zQ}1)KKzt1po[P0yQ-D4) Y4NQ ix z#69$_'d"6x;#j ?} *=O_ Th]q(O( (J?sᆈ =$P)جȹ}":"MYP焔4Km!o>s~{k}ب+J]?Wy&y%Z)A|FzoV] L=MjDw!ZQBZg5C6R^_/_ƼS~u?*,*gzdxE cRrڣzQL1-(y /+('WT[y?ܿ18tzU2TĺZcfX8ͻ4m9-&RNxz sBbcSN:&}Hz䱻r[6>׎[J"*o$RiD9Lџu83ys\o7כ7u?.+< IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/3_lightning.jpg0000664000175000017500000000507512625666430023045 0ustar fabiofabioJFIF,,ExifMM*C  !"$"$C``"6!1AQ"aBq#$23brCD%!1AQ"a ?-GDBGcZ#M;aIOg~DG1α[eq 9] e'EK6aGyAШ]\Vqe}1@׊!ҕ $kq"@U-( 0t~E5lbL ·G30F[^'>5OLd`ay}G}L:5E[,XWNtt 0FJdB:}5D1eѢ" D-Ƌo rY[.DXU~P@ae>cNR#LNlI(!Ij6މeAN=5~tmm ymg:2uGb}(+7oXXwLI !'}SWQrDh6$*M.AQOZԵgznI sϡ<\N_oi=u<8Ǚ͌ކL$Xhs&^\fЭ U*]Jyد9mvTܘUɝ_GdE̲ >f+<܎GW EutsE;rFӹOzryuihJyCg!}tT΃j7Z'6ATA6npy<]=l~.&A#vq.KEsO(X4T!ﴓJz:URLUVǞƚPzz8 spqY9[T5&W2EX͍coe#o_W3}iY 4dI-U0A @ 9fƁov/lqIW?d`lMlO]Nue=>sx|R}usX1d Zյ!DMpHҞR y'8Ybk ]]5UK"U`*ۍTSy~=ѓ"BrC<egcU!Hx1$ETz  T X^KOwB\mU(:+!Rq>8F/aW&ذ]߭uE[:f'hnp,{ Ry;]L4y囐W>:^j"%,<\d)<@#qCW,"LZAIAsǸ[WcgQdv'Q S[7tqBZT64IEG!knMKJ-5 vU'$cZXE:jL-ݹ-'jz!DgMQ~TA6 u+SI^,+grA׶zCinnamon-2.8.6/files/usr/share/cinnamon/faces/7_penguin.png0000664000175000017500000001377112625666430022541 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>IDATx\wt՝n׋m6Ŧؐ,. @&@ȦHȦlfɞ]'!{6 6%$8؆`1VeYlYm=) I6=x9͛3~[w"("("("("("(R8H)q٥cڴcFy(rb1BdHc4-R^^}]] QqA j*x<QH$4LJ!D)@Vb !ARJ[9[ Bg-tr Xj5U{9x#i^/2Jit B@)SQ?8_xM;w퐿 K;މ`㺮\JYc PJ/N~ф, eelafl˖-uq{?y]vټH$eYRJ)clL?dzeYB芢+))j2ۺukD XN__~h!Dpx?>{mV .G>r6mڈI&]FBL&˽0!DgeAUߔ}Ѝ7m{Yޟ8 s"P~f С:,˂a0 iudct=i4`,˴_2yu@^9(n%TU- 2vܘ={6f74BJ}himEKK30TUza8-KGC,̸h&J~)DVttt LBӴľ}G?;_;ׯDze˾L&ltk1c vdH$8x/`&8g[aPU+[[nSLf &@$ž}x駰P]żP" Xߨwo9ւR+>UU,|)JKKw$ɷxA^G([ҔRBUUae0ԣ?~tc"ݻ߹] <|Ŋ~^8!Ƽ'[f7j߀igTM7ޔ#~,G<GsKs}p8|+/\Drtuuͭg]YYuFKedeiӦ-o+_z %5ÅcaiػoW^_?Ob Ƈ>׶0_dI~,:*-EQw9nVܽBEQθ4Ĉdʰ򺕠YfMiooo^Ȼx5U .-,)a"ԡ'u a\G^r=yaiobГ:RInA> df8 ޙ!  Nw\3kkkӤfRf ģ $I$IB "MH-PPU0RbCD,G"B?4MC[k+#fRpPuPUxD}O10fShl\z% 퇃p{|fٲef@?B t^=p@QD"bQD"TTT*5'Ι͎;W۶mUknGEe9$6 h1$IK.A`B_⿠/l_ ѸbTUVcy>Kq 0C~H8Wf\4 f>h,C0)SiTM0 SLEmm-V,xN~ m697Mi M5oEMD`w"Lqt'CQyGL@@ hIH>*ZOWRz8J`A,]a MSQQ^I Tv)eéSPVV5_5ho?;Lap Tf"< Iq4֔)XoUe RZR*f5sL;v@,Kۿeffנj (%;vnu׭Īe H4%P5v v 2C;q--ػo/87,--}g >` f ܖ9OA(B$Aゅ=x ӧM{ X|E0p)%J%}8ݍ-lA[[^m86<G'9g6lp?@vڅ vEcRʋy3f̀< ͎c==عk'LBOObV\ŋC@ C)^zr\3f yhՀ#G} J)v܁_= 2'dʚO8F0@~K/[siSBcvlܴ1H(-MP7. 2#Z] O{_=a~5AH)g =,Q]=--*czO s }, )eap(6ҖǏX(P<臱pA -ү\z0a.]K-DMVdɊ }!-ap7vهwuDQXe~q])X>)?(RQUي ㍝h ΛŦRBdrŒě>dFC),P9?]`A L q@.O J*'P?5M n w}v|x< g \C\܏8}?:eiP RTj!X_g]VPnoX*Rr9pߧ>z{n%Ag73rBPR H@{GV(XGt``+(]}sV#Jhi9 3Ӌ#GA B#gv" Lٕ1wmM5n}iY>{χTOc KJ3X$j/እ&hd Eӧ=\ 83TUz8uۅ.] YXbpqՕKQZcs>3Bs61VIgv,H&Oô(U8cs&*H .s[,#̙eY铧8;ٸ @)l9j'B·7sgv3'O`Mr\BU1Mɪ`BfQh2Yx?2 ags^ػU2m6 eeg #~oNR/x@ǂcFfAxNz>l=ڧlv{:PQjcc)(QatIxycAUT0J9%[J@G)%¡(ghmft M}Jr.1It_>@;n|('[Þ {% G@vX%rYa4 B I Ȇf1Z0Z;ڱ-?[:44d2 79=P"H HT NxEc-N__dRc=h(h#R!0u855v\4q ÀQ˲g|H!`Vn)%`x-e6> ]X[w> ]tWeY/ ±cǰg޷-g!Ķmq XLK-Ů}(S'"B 10;4oӕ @AWa<ԯ1?d"b*>x_a .TU'PJbq ah5H7Vt5}'qxP=`A~Ap<؋2V)BJIɓpizx:ԉuX}-T2Ӝ15?' P]9!2}A8B~?lvۨl`%Ilzq~ԯqFRC[v4D`VmT)8:Ӳn$R|y!IDATxy]u?wZHB%0&f ql07$ dsRSd*T<35%ƞc0$̾# Vnu;]햐Aj䝪[w_{~g-Ж-miK[Җ-miK[Җ-miiY8zyN @D͈x;% law0|u1 `1k,%#6$`# t:pS/ Dz<ʽt+7Z}W?M/ |r9%0N}l! (n-/Y+ߵ=Q^=ィOW+(* ::1UZ#h|Z|_a(nn~5X/RU&o$$H%j@XFM_Gt{' %#^7Vh3t qA\IjIO@pT fæn#]Ƚơysi=툧RI^jrY IڱBɅR Ro .`$MUiuSrYpT2ߌB{ǎ(7<=/ x??*B$^b]h͆%W\\_0 g Ԃ%TJcBi\}T^'oX*gֶ\GXWpYʲ#؄ UJ5pK̐S/܂7x)94Ɣ I;CJ@ϑhc ]$L05s}~ /105 g"Ġ4k.ӡCSpK# MI-&=A#H̎k= ƑaH8m +3gXR|C%kTd v0!"V |N+SI &U* 1hY%RQ>lӨ9_cN.뀯ϴ<^enYvYۼ탫8|h飔V +{Tڊc24 ҩ3 A|E]e a}O}|o뮿#q]0?KO)-a8.NY !Gj3Ur!wƘ(j; :rxtDRO(/"8zIK˭o}TuofO/|ǡg ~y1X uĴ^3K)%fDđ5=BSʮϗVJ׃{6CHe $9CkYV45])za?v: z;R|!w Z0PhtyOH}AI")~Teæ.K'ւ9Ze ew+?:>σ} y]^Fᣟ$$Fd9c1AvhMuRi) D><ׄr' yoZwnھ Lߠttncs(7B<ͨ]NxtK2 vD >`!aRO) ["5)f4PIHPR"DPJ**?cy2- vGf"V]u\zex2#HDZVqP ]UBԘ-OФCՎq6fN^ӻ ݴHR>F FZ,_(Z^#h s]OՃ?S񡻫J7UfƠ4iE~M:A1!Db$q9~ONash.  L{֭߀yxaO+зg&Vn<6dv0btGJtԢh+6VjR %/ ~Ep}I}W" 1zlTe"s W\uڎ/~+U\8:փtp Wi dj\구?gMu.Q?r`Kzl"()+8)p@VX$XTI2Q:j}tv'ZMJI$-|5s ~ PxSK`9wݹxup\#@׻q'Fn7TK(a}&wnDKXߡoE77T({2d+U+ITZ'ˀM-'##+6R0P1*NR/61E E㻨3o"ؔ1a<͢-l"zzpcL˜ہyet/5k~_fbv ӣ.j7QYe W|]M7Ih%V0R&UX tcV<34S%pcI I*]zzz1HI=^2{s1t~^gf΂ڄPq@61:!O-8> +jt:qm % O-k(yNyTčђO4B1,+ZMi)|GRiT5%.Em =b|~Eө 3p5Kx{ةԽwSo/?2F]~q:k6A`gKSw~+5PFxD,D(} )P03ՅysJU#մ_iFM|1LQU*hsӘg|tCF&_Mp=WO&f5]OTC7=| (Hǜ3hd|eLẐ@$1w27٘mQ`c("h4i6l2߾Zetd jPuKxp@Ȧ5!nf@ijb~jNZaUjqLA7οsnyQ; [jk%`!djA'MD0 >4CU4V:OQDqo G(]{\3J@n)v&''0㒠"fD$MKfcKFaȾ=y!=|TADiE; ` \֢0Hc" 6%"|;'h4yG82t8|չOoait .d9R 1ڑaFG!Fjr7}DDaȑÇxe nFoP}+Ct:iI^߳$tLPAD̹o7 Yߋώ!?_GfSzٖevI6"0a{\g-]%N`T`4?*o_xÃqDũ}@c8ر}xޫΒFNUZEL#xȴ?qC[$# q ( 9:ͣL~l>G,Wzv;B0HZ`3vaă#CDa@02?!oy&ђ=Ɨ(l9v*CXvb $daΝ<&aLMQbzlx{ɉo>v!` Z:3MI; "{ع%f$ ֢'v? Ҷ<$[}UbUbĩ7_"Yؽz 8ɹdtVD.Mva6mLV~P|GQ`]P:'[bK Z=i '`0oؤ8V8¬g -<Π3A,CC /,,,VY¹)҆yZ_Sa (DCoN7 ¨U:Gyiib-ZmxdJ@DB$ќPZUB9#Xm};55U",7FjeτeWHenWVnWD3Z,'ҭ`aAcd#l5-F%EӼi٪ %m ;Y8lDe #1R[ja#"7\9Ո^뫹ɓpM$[Q&BQ<'[jX' dyK1"FD\:uLimJ e“~*LOux:u=P$b xmyOy.j妿xe֬[͹z1pl>BVg՜sV/1.^G7K;P Ay{k}*D1q9j=OC&MM&CQ<#)n d?{cAo^п~U'!>.ZDž}$KZjx&6brEclOH)ܳ| Cq82;/-Rtxszhh\߹Q׌5Cҧ* )V0ghTk㭕f\[ψ8UՀfibԱ SLp(D N} .8jލU[>EU`zCw6O 86u þ!pUʲز%ץe1xxR-8.үJEt@*ֲ-rI+CqUV"@> qR`sΘX7ux)QdZdFu^{MF[(ઍ)|t\oJW=T9HPT8ƅHf iVP{MPٗ-]&liDiE W55j3U}On~ b,'2F>]5*aDd]H_$+meRU1JTH <-Ѩ^wUVPW9Hʺ$P/.02[9O?:q==e1QOLATNq9R:}m50#΄k]Ƴ[nZZPb9 *NI`Fp4VE%%$b(be=N03߮6 ? d%X*˒p3=Ν,+qab.'%K}isդHe [^k,@|46MU$W[l'TN(L>$Uhcpv\ljN*pT]ur#BسO.VkmAsO%vGUvǟ{O #t۹%mEƪi#$*Q:iwm5UkWjj)U[F:NF3@J۩!B?͝3V7.=4V|xK/;; rl1¢Cs>`GO]oX|尗4VPXpU3T)PHczpsg:Ѷ?MwqD5'@ϸ}8/Ê Ɓ%1So6rzo` mR4Ka$f@mΡ:u1[Qn4vIQO֤R:LR'UWP(%-Vyԏ jaQq|ŌU972:e\ ]>5UA="#3c`uyI>H0ǓJV;YٕI#麟-;n@=W:kQUXiꃱR鞸OmQK0 9rGM AS%a$&3\1UB,|09?9 |I $5aqMx$;scm]p|6V+$U6z%-Is\r*Q)8) OS#Q|/M +{ӊu@@1磅Ni8p1ƴB={zPy[bQ E;/KMgʩHbgI8܌gP5%wտ{ V .-,,"F9@' ?=Ci抔R { %՗>Q޶M f*y$TWg[:SS1!UGVc  ГYS[O;ŵJ֒o=${w ,w'鶲:r{LSʄNWh)JMɫ O I90]>FO-v8e=܄/zzhD9 ( @'uPI6\ER pyynV@+m=5R@)vjn4kU5l2=G_k׊!=<xb]p%d)ܰWFw  QuCsj**]M6=MM;3;Y;M[md2,nȊ`u`F{:;(OaGPxghpGG ӓ=A>xU1|qp=unOO!Ӈt]_\jcT`y;yXu'8δO-HVPO~zZ21؎R؄1ԽԖ#o9_F2I°Q>-qʊ* 9p1^u[FzL*wt:;/%ܿ")Tl7qEF*}4OY]G*JB2019h≳'*Z$WkG@ZtY4Bs|J7Cinnamon-2.8.6/files/usr/share/cinnamon/faces/6_grapes.jpg0000664000175000017500000000771512625666430022351 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``"7!1AQ"q2a#BR3b1!1AQ"2aq$3 ?gb)9::`:jzQWDL?O:pUvԒ˚WmsM<1?(Q9j/T-auvD_`p5wJ*xjBn5sD/4ܩ~wt Z[SaypɆ!X wk8eLX]O~UV;vefrTС N|{m2x.6ڙI8Lܒ;ct:;zIbI+#Ie۾i^)(+).`XA'}G󼈥Bp{3}iFN&<}n[cJWIL_)P v!2)G+$4cM=X$(K;/UҚӚX*1FqqGļq\K2:IK@apײ> qU;SfN~LrUURBI)+ĜfUX8*45s(F \eң(V2uZ'^C1qT6 !rdlN'SZ8h:DcFB ;xcZ`eɺg S닣x'<}76mSyܮҳl=KG \ZX9Wmo}E^743̭ z5~[i㇪l! g9#۷饿^@19.4]F.6߭-R²uFHǝ_YC DԐzlrp ;obT);6%Uv4r\#; >"E@@',flkuV&c(4 -׾xzw kpK*t^F߃%}[*R"cFp7 e딉q((jየA':ݣɅ$ҪԉTEH65}ulu3rIn_<\cHi'5.I]026 A=tw Y&"V>7_WXmsrE'I^\o}{iH|6`rw{tޠN|so 5VV 4#r UpIO)f$tI_3جJhVijbB9 s|w:k.&xR<|$:UpWܿ(㗓q7(c|.ye6.S6a؏`j)kG=TQU$,qq. ijuv|0%Ww?Աu5_xk0 ;+W%QL&2 Bsr|UQgQy^2 Ƨ$O)ꍐ2ݍxU+"W(rSmUx:,6d5=5T@7!X:3keޒLSE&Oaδ79vm du_9d!bUmKZUž{c$^e#y8S{5@xqG$Qf:\|Ϳ_X(,YjnuuT<'cASqpMtUH)ɱ }ƂMjj!_=an~ -4"D>pʓDڃ]dḷEGS(HK ޫO_l!'93]xsC'*++b<6T2WK#a?Dw\h-Ȋ"`"J_=jc Js9/t5$8jy'OmmKŵ4/Tt8 ,q;mfִmΌVz5%N;w;d 5Z7`9+sN(rӾگUL+CRplYb((i:̈ ߾x~0PqI-Js{+;o֢+lLăMwso}2Yqt:T dM'} +4%r4#q"j]5TjS`=74Gu q `BȝAnA$'U+).tbnL34tvA\{Gs-Ħd+ұW Zbb=gKZudq#_o ڊԪKޚKd7Lp@sN}QAhQ P#_oX*k wb7>PjjuaRP3|;Vt,r #sx7Sh,"jC,ȹh烒Hu4=e[50[$up"b߶|E_q[ެc6 mJkM=LH*&BʱDXlIa5=mĔ$r*; l~YN%9HAlgGL 4S#A6zf( -ϕy9&ut,QV6 1b~\o;qTWYm\]e6A\(#|]C)&* n^P3 0MXzBA]$32Di <ƶ\7hdYT(#Few!@ w'Mc:α6M6;[k%Dr{47Ddp֫6Ä9VȉJuG,A؍S qEːtNaMXt[mbg//F qsѩkxp.OLFƆ/D2.R\66ST4SÀq@vX꣺ &HrC{ypT7͈PgǑZ/C!q-;Bz*G$rqm\ʄ o33R2҈*DOM,e=|}3O=sI,$raMd"5HaUFkצH o*C9n暚2\ObRmGI *UM$6#\&1NKtvlA[hnQ8˾MÓ=y٢hL8|olYG$ǭ-٘iEV>TfC$|GWY$ }~`ug ~R.p rskBj| %E5RJ҈j@$x'o}UfYsDR@z頤bA=Dd! U: `F$s 8R=)A1t'f5{O3R*DHB5kHCinnamon-2.8.6/files/usr/share/cinnamon/faces/7_bat.png0000664000175000017500000001154212625666430021634 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>2IDATxyWufF3#Y2Fބ70d6 lv YL I1H*)*R–T@r(6pl oƲؒ%Y/_{Gۤ MBS]osw{mA (P@ (P@ (P@ (P@A{8uk&׏ ]#ٚ~0/^B Æ_<800pgw}k TU^>40!ebb̞x(7EDp/N[ 6֓ExdE+T 9Nti__ip`@(~E]AW p58nh T`ػ}i xq=4,|D):\~:j\?t`\EU MS5 ,|D39ŰAYgEe,VQ"XLo"*jyNWUA{Dj_{Rjԫhv?phBs%̧8uRVPD~22w #'R9SfnP$S0<{ "ĮG(Ucrܸ3TWO 01lf+ 0@Ce}"Q \ۊI n~z?BQ@0#CAEiC"!A41&wB?HJ'~e>RI'1of֯$ H<i7s䗗4xR\)N(OzZ')u4|vul}Aj"QkIT<|ȡQ)̨%)-2$Mfqg6qfIرg$4T$paB˃O霄ΰͳ~X1|"CKWn܍8!aضT7E x|jV,yjOl~|r[ ty"֎ׯXi"r"Y'ohRZ[lr,{]Ǹd<`=ٔ}6B! ?f3 ?i#EĀs=cnga{ػk7"fJfFHSyAY73Ef%W:yX{*ZR?Q+k:!X̾qOcY0BS,bI=4[cvQAd=g***>']:iw*R_ȫTm5lIv621L8ɰK^WP>0`?;hʼn~5e9rr6{3Abەo)V%,rˢFaH2hONhziyUYT/%NRd +N/?cI1%ಾ 1p":C/sԩÉkb Ut%O{C1z?}'P vF2mZ8!iKy 0S`h'\{6Q}#K[TǜE*NQsn8O'OM8&e襋'vfLD+Ȼ萓lm%e|-%5?-˛[y͒P*su|dH[]Uԡ'تL=4ނ;,މ^F(F"ղD{' ԭέw.,d|PѵNTP/ȓo@j޼qoaO'ݷLG5K{$!D!KkDM `ވJT56ASz^]gF Ga0y$r<ނn7ov'O.[ì*s:.$V|hǓZJIa4h* HC`̑R@PkZ J"z4&d O'H6>k#hn0w4gd{`'o<.& 8Zw+r.ܰLҶPܷ) HcKw <,@6-yA;yjMk|Z!yA # f)þ3 unpiRad"W5Q單 ϭ^'p:fyABHJ}‰f-F gYvnfS4*kߓyuaO a7.7W֩Cg%`m(Q°HѓT ՉO[HQk !Rr.v?4HSM<1!;h,Ӛ774R`! =9X&YwE*2,*zE֪ .3͐sϾ͕Xj4n CJy"̠NqёuMBz$ Ќg /hՄ,l̉#`;N?!AWO[Dd&|WbKH+R/>E uӲüP͚qρXfE9؁ko9A5Avl7a9vH~MHRRKs%N\J, ?4\jy@ŔrK@U>a.OSmK#!ZW4 ~[͛OᖉGN;N{Pޒ-UD?)9Me/ Z"6߷p_c9xn SBѠ)k3|HqM\5Ao2CP k[Xu sw6GlWW%&"ş!GT䍂 sUAG&f mD;=@\$JUquL|͕K!Sβ336`Y0TD6{aÖ6bWm e^AZ%M#7r{Y8m8EGIE@Z2 Cږ|so;|xlҠ 2Q;|B\1J̆I)̛2">(@+JN.p.;NndŮ-ޢ L>xIV@o?{1 '}?M/j<3 |sZydZL8a$|Gv''-/N$ "j}m9EX PV [ifk [ ߗHչkiW0HS6!/-TEџ+i>";Cs&h/0 o8в`8.{9^jr`S1[V7O>a'bO=!t#&@n=;r׷Dy-9ys,uK^FYY!:U9OCI@ oM`}XcAj`cŰKZ{ζ@ϞΟ=um2{gGE05QA2p3BȞ!Q=iB^:v4&)}G޳.GD6Fxt6zD*k=V̇\jRlAE\`倥C ! ^Wsi; 0EcC^j t4D8#Zo{oT*ebDԨYRK缅 `|X%QL@L;>rIbJGx.`BP*}ǥ|qLLdQV}蓾S߷!Ua,Xc9fͨl.5P*+H숻pY`j ,#u̒|2HߩVY01Df2gF'ԌN3!X* (P@ (P@ (P@ (P/leӾnZ%tEXtdate:create2013-01-07T15:52:22+00:00I"%tEXtdate:modify2009-03-10T22:43:10+00:00 ttEXtSoftwareAdobe ImageReadyqe<IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/0_guitar.jpg0000664000175000017500000000435112625666430022346 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``" 6!1A"Qaq2R#B3b,!1"AQa#qB ?T(BR JR%)Jo4FmEnarmK#pm˜ R&zh#xTչ҄`JRs)YBw- z;kt+985g/Ygc3l`u 0,jM_6U$sŋ(c$ow#Mm[J>%֖} )?;Tr4ĩ Ea)mGڳ6bMjTN?1 ܐ7V{ $6 r95!S-@mZ9uY38$d'nu()-+?# 0v9]]e2܉RZ^Yqi;p`J@ӥ6SdVI>=Ӊ*ݶ(ܤO̒Nڼ)Jn q7JR!zEaRZmRN֭_0bu o~u>7Mo*%-L#`U[¹c')]5#}zxN0F,ka,Zcspvkj7uf9j"ƞhei)koxV[YdyKD~\mNnQSLǸcU}\M9#[:![ϕk#~> xgeI$rT`9LVN7b0vCoyRs m;~e9mVO띆\_< dž@' I0K|;%kf$]Gt *;P¡)T!h=?pqڪ6ҷ;zJmD(FAUC*c s݇!;5M8/2e,4BH) juEclp4+f%)JGф;PێPw|_)p](:R/q7Lc! \d.d|[nfꮾUosPr2H.ҔQ(k2ow'49KH%#RI\>-RSdLb֗0_|={x Mchh4)B_Cinnamon-2.8.6/files/usr/share/cinnamon/faces/6_flake.jpg0000664000175000017500000001017012625666430022137 0ustar fabiofabioJFIFFFExifMM*C  !"$"$C``"4!"1AQ2q#a3BR%4b*!1Aa"Qq#2 ? NG?:FkIEUp1SFRUI>OunfK t<2tB%6p4U zJbNT UN:iݹ=rk[VA 3$2=`8&}v0Fvz˅%M\-NdcO #ζDJ,;mk{Tnjzh+SaFˢ LL}Nq@&|ni(7tJc 7!ۓ߹wdӱLaVč:\N ՎXu[2:WEYnh*a<!A~;bʛq-Ɩ;2H O w+%88F@T뎍qv*ĶZqӹ^QKtRQSR"!".I'P=`qR;}o`K<6ΌW$_#N;*]ǜt;{tI B:1I|?{{;tTii'I%XA< q 55ɒ&UsXKݝ#3+p?c* g!\gYx\vOQLq 'fKuV}:hڔ,spH^ۚ[49OhI U#"38~ W$VB`h=m7-ZIAGEhdx*6 گr]wM\gX$UAI?fMiHTOWe#"Ot?{VU(Қ9$*r,w#"lxɯ($;J )F7[Zc(u>ggD=f-Ir?}E:H2S#2c9no{[yʙ\23c :/u)\ 2<(ѩ"bÌ$HѶ2_5b;\eXb-?:w{ܧe \Ҵ,'F99P#aoZd[ࠨi,YQ/~#jnxxyN۹*n-oYQ XHY9'`\d.۳r+V+E;QV2%L\ eSp:!Al PT r;jjuyTѾ륕f8qP,43[j\m o gw/˷%F 2@.yu|>RUTѬOl׊ 2gGvțƷ{+Y0PKV?@*0f蒪K /LK}}2mmMKiMDI >G?տ)ij*hh%VTH4jUʸ#3aǖy_]ކ)/p<O])-CI_OC$h- a]I59|uhx &Tˬܪj! IY(V0NBޛ8$~߯kB{5AMJA)y ([e nA}_KfZJ{ZJP'# ,=DpnC|}V;q+D8yavhCIWj[̾L %^r+)@W Je1NGu#e \j䤦 C̀Rx~t$й:eD8xroD$WDA+h8 c"#ehiOc7fK4*&zq 㱮疷MeǰU]ne3RRE:۳91mW:ͻH$U#ē f.n}ĤKCV7LM)"mxEl` '|_r{VƽQYvdtF̠Ɣ[]aq]_ajId2̭UiH>R )SNW;[̸\d3R7/ݕ8c-BuӿΐEw{u]J$i9ne21'a>:mww Q>حEZ:iR)Vϵe Pc LzY,{gknLM2Zb‰G@ $:_.vkiە3-:F 1ȜԒ=0-N ۓX>Knbi{k= jw_J$v]7^wĿ\muڍTf5ht/^l]YIIL:[}ضpFx F*]Vq5eV ƙjoU[;3 d\[rceW@oWY*c8ICinnamon-2.8.6/files/usr/share/cinnamon/faces/7_lion.png0000664000175000017500000002463012625666430022031 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>(hIDATx|ydUuoPu}{nnhAT5@5yI{Iԗ L< QQi@hCUsk?9Uu/=>{S]ԩSZkio8:8:8:# 06q}&F+r=9ZL<ҏz%F?^/}hߊ hZ0 ƘegȳZX٢?:~;(p`IF3ZL A+ 0 < NX'_w^>2+?D\|jzkߣ4&ܻ+H>XpKi4PWjB JQ`p,Nv![ ɞ1|l<&hڨFX!@$smHi9G#C^?z`nh4R$RЊ` Dx/(]0ZNpr.peRtQxt4Qu [OX'WdžXSӿ\\w윰'uC D!2EEO s'hf""+Ćtդ@g v|uVm+>zdA/gԑF*%z68R0z4w@4DiR4 4jqФ8"Di9"Zg[0QcqD4K-Af%n/_kbKS=\B@PM̒(A)t(|'Xr|oϭ\Ss>~x+u$Pן,龿XD@ `a3DQHKkF 1 13}3w4"% TDʬ!\ˠW . ߄;13gxه~9|`(bFPOzBOM"z7 ߶cqDƷ߱,rV=V_Z`  #hCe@5 Bp 䅠UȼDp N%`5TY:-viE9N|ZM[2\~P Iixsi E,Tg/]J4B1%6xg P%x_֋˭$s+\4}Or &cv$\9Fc]lN"ڕƴ46HCkP+Y$ E]P*W+PJ /`pQEJ&̇+H}sqѧ~.||卣`ڃUD%6Pu Hk%z/RjBEfGU"h&"HZ)hs *=/gRl^3MFm_bX/k1]\Ot--}((& r&\z;9H}/"$=6@JX)h!h(!@ J))(U?)+䱠Uӌ軿P| +j1} kjBl0>{ gfsav<*V5U_kQ X1VH1PDb@TIhy/Jctd Z9}4V6RzBA&\\Ɏ%1ys  (,P8ACt Du A**c [PR "FqD #2Ĩ@*,`%PP}b@|PM%ɖ2T~Pp%^($q4;$nf;A|9u2ڹ]݀ʍ-]6eb6ls {6s UHcF, W/Aw4I*%X.U <c0P5f\믩Hc :ؗvy.1qj( }ԗOa~fw3voBݶH=$ 0'n]P7,`:*D@i":#2ROBGR]{:`̷sI/SAmdH-8d! +_t>|'f 0#Hh*5@z: l08߽u˦nfS #UNZn^J'0WY`6fϽ΂&湇J:u#D<0xHoP"Htr*7xH x$Hev:i% Ъ.Gc@,{t ֺEԁ踐D}X070s Pdl*e, W ⵒζƁ*Oޔ/)bB" ݗN/,f9s +z.B.//ϤDW(dܶohV ](8?;v^+=ָi^e-xuOZ:DFZ:h "vKWer`:O ߇8hBІR6L%"tl륯]oyak'&nvj:Zp UB+ N $SG$'0XWQg%KƦ G jpW<-gl3 5ac|xr A` u`VC0Q נLBw:k0&]Ǩ'RȠ6齯RRP)P^u+ wfA iyC%^h BZ Cњg@iY Q҇%``h)$@05sS{K >!QUY4R E.L<:LDhx ȧ|LիGsV) YXiLH Z[b志FTV pxjS,zl/r\xX:3ls㐜bI H I}+֞Q!$q ! Кi9Vz_?$E `pdLTCkn®*Eknb 踥,n!Eeц`=[h'IMW aפ'Mr"y,+39 eOHa8ޭ`UbR<NfqǞ%Jchz -YQn@kct7PJA/YB5::D?/JTAI~0Y5A'/)~ZoyZPj;5|,X\K#0;3}{!X P*} ĕD(0|-⤯­Cip#'apxjhH>bHq=0pZӹ+k R!贌T"Nՠv&*X" Zou|,~qٛފ%K78 DQ<~ _aL|z 8xXIZGZ@RHAuH}nApTL=w['=fd̘ٟ͆F'"u_J 6SJ?j5g4J/-?7oi-w܆e+W˯J(mwnsLlpwb`'; lj#1^L88X'pvy|`fX[`p2z+1&`f'@;g2aw\{# gC/HTՀn~^€15 Dx񋞇!j)Sn1&h&bzz?xhg9,?JZyݵ;m-/(>scTfYV&ȭ``>}BLքkoWXSXR3=z @(f_;2 ~9wݏȠїBPٱ!L!LbjrRݘ@snYkz!=ngT'Z~EAp=;]sVX/ǁOJEn|V.,?FUl>oIK  DQc (SN:Q9ZiZbl<4v=RQC\E7m9=SU'䅌['t^.Oloe?3ThVX@DVJUUS&(C֜+aL("@@0Ɣ.gr_z+rX|%9Ue˃)Sz-}c=g[dPҁ(6E2L7??p/E0P89 }ʇK^($nX{<("CJIU刈0oXj wY#;hm$9TvZWw6g"A;;\#!,'d}t "rbd.9X>iԔ؆Մs~z@Rn!ݾRؽܽG!ǤDsVׅ"av"-&P LZD:.xIcx@ \uḮ$G: = TF@mff"Cұ9 ~gAD wX=ctr /)h[)ZZM}Y @#Z!ĆF9EƖ'i /3<<,ւ?$8df4sTɝVn{fwa5Lػgvx=R>\q"hOFDN)41!F>)xIax@8vY9o.5q|.tGѿoFO@AZ8g~U Dk-wpuRYk-sS{y;1Ɛ@'1[UձHW[6P { TO2,(*AWWrO,,H1[(V N{FЃ[n7h>RNQ\:0,G#Ikh[h(0?7lǷ0gH@R vq@AHE1!wt[ޑ7\{))rZTu'}6I Ll25)XM a8IE<{(H{ї{E)$I,Cs~1XwQPz1 e %ƆSR\pl\*X1K8nViAE t,QW qL!8Bi DQ\~?9F(ଃwݹ{[בemL١9?}HuA駲3 =f!rNOYw&n}e' :[Ydg9AD-m UsU1jHĮ?3IU1H,븲mP+ Πjzg+P,lV{nH#MW+RL/gh'd!{{2|6?-r^ndm,)\+< VHW z] ރiF @ ;dYsȲ&"CՂօ ( [-LMcnvYn fHݡMQWUI$_R d,v(";V^i|^>GՊ$5΋4ET P]PzNF=Ď̍ 7vs.E16~a@瘝iW++Ggw9Nۄ~'Z j=u:YCGB^J9F~yqe:Yh! Ta-\FwDJ1(29G:s< [syWdЭ M=UmXbڨеxhxp;]-F3wxÿt j=N]rD76(%(FӌZK''0>=tVU5(.ށm*H[cX܉|u|TJ=sx$(xJ\Y<>;6CN '* UJesH0 I=8Vln5 dR (}5DC?ڨFR .fdJ=ۑ~`YI*wR:Ž+RU{ 'z±'ѡ% !k"a_=-!6|t'@ΩKAZnDP*O+QpXVȜrog;A_ Fj^lpew6?q_9^{!UtoXu"1ՂvH+TIhT0pIUX˞Yxk1>|.`rXp uN,epXá;IzzX wދ V4kXV(;>χCe{{  G`Y"KM (spnQ[Q^! #vSCD!߶3k{Ep ;=_Վ.\ R5Wiͯ\H'@ၙ\8!<BKj(Us_;H9,[Y'#*db ݴ"(t%)4:낶ܟ23U2dDZ[LFt Un[vRҞ BeE; ?4vaaٸb/0ڪ{—*5X1`6*,R#fR .Yh4Wh̠>^59 >Ӷ Ec߿Ha#vo<%Alh1z[/([]s`c$46*6t*Tc&@@e^^\T B `OM;y(bF1qu.ŬM0kcMNvj\i=}p _;l!0# (I'5H랂WVc[ap^4"Gtwg`׾  oϢ9?Y͡՜Ga<ݔɹ~뻯:lXm+qŵ`zVV=-==>z#DD@X$b~QoAB4fX mWʫ|*(R~qzd΅Juem4疴ZTi}X7>"TJz97דF_8k($ƺNc7bbl}gB=>'a)gb{b86vi-rR)RUHQv-U4]Ka򕰧 iZC@'90u_̂"b嗙е<{'¦JO yaaF4` 1a{ʥj`i'ζLټgdU-qGg"X:k 䀄}5.S>t>04v[ȃY΢I[zw{)y Y[d>\K oZg9`2w۳`z>}ٴ@y6g^졟:go" o+E9ϳr"<_[Ӎ&'"bٱӴg>Idgv10)d|‰^w_EJ˕R礰ZGHI٢n)wXQE>H}Q EA)B~y g݇vѥҷ/~kJ&)Fg̜կt;_wu܇%KOSr=w-ַeexy]0DZ!Ѐ1 ~-Dʅ໕6wFI' { Gg@5O|Fs?`hxvkljfAl~ߺF]uݗVFJKGxk[߮\<GqtXq}é%tEXtdate:create2013-01-07T15:52:22+00:00I"%tEXtdate:modify2009-03-10T22:42:48+00:00wItEXtSoftwareAdobe ImageReadyqe<IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/4_leaf.jpg0000664000175000017500000000510312625666430021762 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``" ;!1AQa"2qR#3Bbr$.!1AQ"a2q# ?*("((B(("(iY pV+웚u{%[HV 1 赦H<(~/͇/"M)sD18[fmW"#gx"2^JA/T(φcI{.iFxݤ\z !|<"zFtٙLk?thש4^NwAC/7 M*իtC^EzRA) Hו;.Ȑv[:Ёu)D8hBs57jqGi*F[G8unA ЙLvnn9$IMEg"ͳ-c9<|rG H)Wvh;o_?o]-tq AQ#&\?JC )έ lj#1Kt8CGaJ@ iѿL-K[7DI4)[X 3tRo&z;\MŴRrDm`3>)Mv,[SrRRk-؊U-MiX$IZATw*a/l$1R.8[soy\Wxla"n- 꾀.{ uvji}mÁޤ\9]$@[JXrT.Y'T_ڞAyXhmӨ2\=7Tcg6^Jn V~afc V&Ǩ8s!Dk-t t؄*+H1K/ҧB$F*t(YX[/`Rl֜T3 O`%Y'~tuv֫L)ۭGa$$h7˥a:a;bKVRc5ӭnLT($kz!)aStm::G~uCqgVJʓc[I{g%|#RJ~#~:ۡ؈CITj i6Cinnamon-2.8.6/files/usr/share/cinnamon/faces/7_panda.png0000664000175000017500000002016612625666430022153 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>FIDATxy\U?{sN wȽIBH@ H`#вl۶ZJ7O[F!cD$BB@BᎹCUaMnֻe::un9orPA9(jjyt:M6!Q(!x `RjW޻q?( D|ňD|{~=^iЉ{\DWJ-rY&º"89pHHJDD+ⶉu"n%WD]0ɧ]sNcQJ}M)u vsK~h}^),p" ZoylAk s$I>Gwmty"d0R5E!.ĊKv"0c3f,c̝ƘFP "XkpC)ׄS GI5 "8'>rǝ(/JMSZg2J+Ct0G@k7L`9Ҧ?<R)x}c@Nv|[_]DQ4 hll1?5 <҈Q>9{>sKJooAD"lb[v-ŗ^A1EQ&+}}}N΀z| ((=t&ͭ[PZ. UJ 7>z/AQ?=qeCTMygFke&Ŝ䳟Fsy(6םsXkㄺ OxF>D_y/.h!Z)lbq""k^ 0 |T)Q(.p!;wA vZz5y;8qL8Mvzs|vWtX  pnPZ?sag(F ]=MI"*^)_/-P0 Ig2k׮--#]ݢΚyhcREϤP*C% s}Vq.&冖 7V䙧ӟVN&JѺ(,#Fq _ U^vKwPYeRG9=nS\ qTm&-]Ƌ/̖m$IXc|茳hhY<0п?پ!0\9y74.q/;vΚ}CU)MMAmRXT ^_,w׼Zk%5cE|OJg+.cspݳT*. u|₅\|Ŵ*ĝ~8:k/J9mر ( |J{(e(:z7ofkzuud3Y:OdwyA|ɤ3Aa _Nm%@)䣔Ng4S+P#XLLe1 <?@&Ap6}RA-mmz 4#}w#g( sѳoaVvuÏq]wۃ(J{heݾ}g}mp2U4//~K aD*"33gr}hcF㬣gE'з$Yʟزy36pSObdq#`Rjd:` /?s'tuJqD?kBΝ$qA0iAP6eԩs$9|who9KaĽ[UecAEi!WUKjCTHA[$MX'̞=Txy-GO@?xd3Yoc6nVR g;K/1iT.j(FXދ8V~0 Ѧ8%9Us4J=T*sZ9 'ȧS olQa d"iIq"%%s!&39ADsVę+Zb0ҙ:F2kl8k>lzo+"p^̂bDy]y:;Sn Aκ=A%:[9E ;s['H9kX[4#G暷'_(`ŕW^E*eWU{e8+98F3#0Ɣp\׭6 ԌAg;MDJ_vN:$>uׇl*ZKv٘O0l?cSy&_ w R `\u ~qmަMS&O/.ӕR㝣N,F*zJZ3ɧ|#?KvRBa+JiOkMX=s<螋/c%ܹn<6I8u<.,k-Q=&DaSN=%('W;箋ځ ~*$IsL4pst^Xk9eȑC"r^0[F{(Z=eYܣqke~2SFN{&apa֚7Ga+ع( I;.̥L0a/˥1%CX1I)_w6Sk+K)'J3?LL?HTcOOPϤI}>P8&B (tވV[ճ xv#u(a_ x}R8!N5m~u-Y?ȒJe9#Rm?#Z[?Bڶn7^^SJ>|_Zoȣ*PNy8e})VN]J9oWd0u i޼?Am@c T*5<1ymWW2sLƏPT3AUu<<({ŷ}xd2bc:"nmݺ%yJhmm%_J)7Yy۶m^bĉL6mP\DU~}&xatjWj&+{zz90 hmM}}vHi=b(ુ<\.dz>ˎ;5k{}nux&n|_x&IX}pĬHS$\ʏɔW_}˗Ĕ)SJnl1vn;{ΘboT.NߍBfA3f@J)5`fE8'C΀=A TNs$qr "-}k]+,e/74wo3>֚8+Z eqqLss] ΤKS:Ju.p5P "AeŹ8cPjϟO:f_@:9"lzj<SG+n?Y5PԩS7n7V&PJq '/]'s_*"ZKvo"}Jk:::.oT֫_f…(( aHPxf wnd޼yw碧T9XxanMT3$]]=6'C" pEc;b⋴3^ ƍς0f̘8|@Ϯ^Vh-ݩl o9G4^-‰J)(f90&(_$C^*0a_ &c|ߧcǖF).;Dk1lWۀRUVprlpN 1cj+EJ>E)u"h{+{L lTOET,Ҭ T-Wq%GLV{&҈/ق\cmwv9uVKs6IM"璾F&cSL$+t*I*{L0^8q}k&kGZ_3TUtY*v4cF%XSf2 C$џrNe 5ً+bm譫'b>Pq{7@dy(N=qoEODP(ջ#.QJMUT{|6R3pysa@qC*H)8nwDkQS./D=s?O;y;ЏBlcǸ$W Z1PWP]Jk-1k-mӆƓWRiuT?wΛ$.q ww0%/z]@]:Ti'(,PcyK(ꄓ- t׿Uז)5l!DqS&Ir֥#F];!r-[[&b<.BU+5o.mqᘣ`VxqIȘQ68jDQ:KA\=yi5G\;0A9(rPA9(K_(oy3%tEXtdate:create2013-01-07T15:52:22+00:00I"%tEXtdate:modify2009-03-10T22:43:30+00:00N tEXtSoftwareAdobe ImageReadyqe<IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/5_kitten.jpg0000664000175000017500000001104712625666430022356 0ustar fabiofabioJFIFHHCreated with GIMPC     C   `` yYRvqL{\ȍZUq`̰iT+>*^%mlDȳh-wk '(YqZs}2]Hb ɃeىiH+YxNN4%ʮ"orVgўLC XLk.]uX^~'#}f"\ I$v8/ޕtխ['By{nyt7#-8 ==W+:"forێg艠nTlJgp׳yt4w.]|鲱!r6NP /1>Š=U3#ص8.|mvH nbmWODYj7ֱi8X"!1Ca|qY2l+p9m^5'UǤ/հ׼ϑ7>UVB)w;FO\nFu*S$CAZzSX&&`,[8Yo{`̰72+OPB" 6aFVYe-?Wo|]TiEyU"f'>"|luez+hZW Z63h#REUHUA'HTR+؃RLǂIL~̋5vzӬS4W< g9܄$@Լ?x鞍,iN:Ik;j)pW]5ɂJDSQF-pj$WAK*=ވ_GNvB5ƣ*CFtlFLgгKaKeT si2iޖᶯ+YnXE<3{9*- !Df 9rJD9;O`پWX쎩$})OHn>pͮl2_goN{4eZBtYl,M}ӊ$,Uo_e.p X ~aN,e9pⲞMC (9a)B<#k$;"_Vv^Q+~ɭs՛0s9qŇYܩpgbh?D!Puu? yZ@v2C;M܎Jcn qYE6As.Kb.HBZW'BFɷڹ 4k5?"BC.+ (L#V7+HcRud3ʙxUhw7Nɬ>#pv*Jv|3l)u+Nl?))UVW<3vRZm%!1AQaq?!5̣mXzgpzi}~Jxx%TNVB)5!25NaS)lή7 2,_wTy \:L4قH\aRtc"`Iy}rsn !kNa;Y/P\D 4ɹdkH ~",V~3E `R_ \gb-QZ-_1yDPrF+AK.KKT&Ⱥ|`ڨ &Qc>Pgq)ɳ`.Bτ6(eG[KC5h[ a̅)HvPǩ YKepP@@U4mg⒚gwMLl^E ?|_q ?u \pe /X.͵? Z {@E%|m]w1B힄-AQE: YAHY!1A Q?_SW| 1$C$;vTo&/SrgsBtXߟ d [ۄ&!B6y׷鄆^B=q S>4/6MQ+NuZ}WCCI=!L,PEm>f8L7JE!vGވ݄>[|]c6fۗ`l"'O~!1AQq??RAɮ@tZ-%3H`K 9i<g!.I_[1>&^#L|&[I,?Ht\s؛:D]~lyR7V=XB@vgz|2F_Nr>a|Mfw[%!1AQaq?  MkGRàYϢ5"a17]u=LĬc^^;A-kW;$חzvd;N@B8 ]ktJ8i>"0\TP2WFM<8$*7VF0=;Mmxw`~+!@ aP< \ǓgEzҀOWxnLl'gmz 27o.^4 1:$#U;l<z)6]zrs#-huEiX-_&z0Ƨ ;=P^rzqD% BL HYJaf:h.uES[qȎYx\UyS(WbVFPk7TH ᄇ˂dZ?yN?2w,iK5&H va 1;yDݸj'(lH1Eʁ~7N,bjՐ*j5j/mÜ'}3iR}:Iuw=(AMoūʟfz;D*=x˒k[ mJsI'gS1+ˊA'`@1" Bv8@9.*s"_w$,#v}a']U=pcW~VCO' tW8 JBPO\ XO^r\b\2Ӟ[ʫp>YB:Uԟx DW / s4Dj=-~օ;pm)~7ā#9e*+:zoIΰYT=}7c5UU=,FĆY Azoϖib;/90VR !zOe#y.!HSAw)3gCinnamon-2.8.6/files/usr/share/cinnamon/faces/7_elephant.png0000664000175000017500000002153212625666430022666 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>"*IDATx|y]Uo};!{ !!LJHI,*C복n--&JQ md$&D01KBt{0[N}{޿5iiiiiiiik%+O(8B!bX/c!"@iM)՞=ѮNwfyR?܂/sBessƺc@;=)_904tpf,/:חx7׍3gf[傳8c'N@""D4ޥ/c^uon~)߂VOwɶi|r,cEF>)OʵS7{o?zkǀ߀.>nljǾ*YkQD@DКJ͞/o7{=?q]Xn,3̜i~\q|cqͯ5=aD+s=k}wޟm| /^^~f=qpYr0A0yDznY.pxrVߡ׿>g^څe,?۾4gs,3#Kϙ3p!șxg޶w_3'L:I5Ǿ!q?3c`DiShA 4&H4<=Ү,kqW~pՓK?B\$8gEsLyZ9M8 5wş\gOsw398OPw=9/wO}xY+bӊ96Cwqxd0p&,pw;#cW\z6F󄹽߷-a|xh҉R:0EJ+x686bq%pﻳr'_{×C|¶LJk W/t';{CKX' C#I5m~&*ů%HW*Wϝ=\{vO΀S[ז 6y&MuC*zO0gxYY^0N/㌟0L Eh&/yv|^|/?7?wzPi"X–~*m4wׯpmgT榛k'`E/wd˼ loLjhPi&(TK?8:~97?رwB\;161$h4PZ?IhKKرC %1Ak" )/]ǟb#sy /6_f'Ǒ 1]H(fbιѭۯ2 qg8Ƙ(D!r=3Cz øGD¹@x#;" "'F`9;/8yõA~g3r~b D"0Ŝs1A#;M K; Ib;8 ak EL` anٱG/P€.UiiOIDZHi, &}3# Q F@gA3GlV7KO[hԌu ?0[p!;Y{xDDE/B$ m/6Ǖ !0 #c\dg}!:X1gYoY0{cLp|lsXEGD1!F$CgA(B'8(|l:D9Ku}x[4Z JS?.j0"nπE=`1N#3=t)MGDG4͵L3S2̌$"L ח1LI1bJZ36'eɘ 䀁& >,uSMJiʔ'(zej1ɱ6 `D0*  4  Lx4gb{Orʎu-;FfSf_JDE,2Kr4-#Ian,d&^A5C!ԔĵgDlr(%JJkb|I_JcyOcM]HSOq"Ƕbg` 0oBre9<4B153HM5̗<(ųDp)NDlҚ<߃TtdCv~SA*\i٭TwT.<߯O#> Sv\) SJ*)IobSA=ZE;9Z1PK@!0mNSv9XCFwJMF@F5Q=rVD6o)E<@Crd)SⰄ5!uAJ tJ(%\҄& ቢ _A"ciQaW$I`54RMK|f)J2D" _8$ QyIhآKf-~s*cN%LH ΃HI8 ^: 8,M?@ݺh>'À~/(dJF-WHBj@鬑E 0 2aa!/4{-͔RYQEe$hӈǷM4UFOkᅩB:5b[SLʆ RZM+fUt:Gzو a M]JآZ[pIRоW|L4^]iF)5&- h!&*Eg$C!o֑PJA+"HH C D&Qm?LI^k~BIܹ٭/71"8_N.wDA*Cpo/:JE,Y|":BW uG!5% 4=dČH,Gt4<|Z=Wf$[27=WܵlS%B09b] 0b, |\3oWհqZ 8X~[1^amV(JQ *΍ReBb7[1_Wn#'Ο{x@P0aM݆ȦD|yشn]YRc\dnƠ;1Z>~.{AhL 0p72: Hzo:o|cRpÃN7BPKIB߫etJ;8 Ճ'E*@,!C8cꦼC OXCr^8_i 0 Zcda@lẌXb%nSNGcR>۾=蝃Ɉ0לjCbRxCcXq釮AmLtIa#ЦҜ;#Y43m5Pec}S` (;`u$ NJuhu*7-ᴕ`Ν1 ;'Sj+\ʏ\Q1ZPЧ> #tQŔ30+1``0h]m-߷0 R 4qx:l V0i±,h-$:tBbsV> !소hag|oG)o.|r,7iɩ}4Y;`a/UPʾLx=*tH1)8\x&?2{z-.<gʙB,jM&ׄ|v@G!Lu,RZQ/p]tOOeZGbqs^]U˗nSd?qcB9H\X1Pu!GVC18-R0Ô!a왛Z4`9|é2oxZ- )%F+U yضgȜj:Z&D_șQVQ,rLhɛ4S>5(q2@9X꼬 ҹ; ˢyDdQ=1E*𕂊`RG|?+-J)HD`D@W]̈@!DS|e眕qXl`,RVJbV,`G;6.P*pF` ۶[ʂ*IZkx(KSQ9S ͳ¶]GAfԅI X Qܙڼ#_i>]K1sr * Aq3(1%y㍯ڮh& ?\Θ4М H8~TΖ |"䆋(S/Fd)%(ϳ:Qs] Wñ8ac)SAY&%a`ץ)s}J?4ixR;4W͹ ֯îzt]7]L2c $?4.DMN$J✣Texux5׋7XD] 6S)0GXc R28SaD %R͚,|I0c#r}/o9=+?zQ.^Dhta"K%BJOI ߗՆ/ y|x˙`qWC)mp ד`њxIiR1tl`,ʘR y׮VS *=޶oW e q筷aۺN\sޮզ!z2YGц6V꤬~moHb>w~ !GO,h"] 5 R>-e Ц:Snh!}ts(`[#@ }bY(f1#JD b "ߐ;e`Jkyկ|'v^ՀWvLWvg[,]o=mv?oטRZpE'֤2 QsTޮ,qS\|pr 8dpD|?_?r]=Ѯry1^XBKOSd5\' }kճ =zٹreK\S9,BY0)M/Hu_jc-;CwoOJ8?Y֕iS}A^p xb[ښuZ}dfL7Pw>8kepe;W}O|sftWNMs<ADJiݐJ \oxoG6mCpJazgUXrQ.ʹM3*UK=04w>4,a@m, v4f0 ]",3&{vD&NKJ##Q;*lӔ<{UB1:m'Qw ΍PYI*j{m¡]\?=.{)'~ M46{rG433BA9}`0ȀPQ`z]!|!;򃌱=)q9g)nT0;죋HSJ#reRandBh62&&&i?eiFHC@I _Ii5Utc׊GD:+RKqڟG偱j z#nMuZj *sJ z-0̀a\f5LaxR 2] J1Zbh"1F0\`R ADvR}Ϊ\cѓ'}p~7˄!D4Y#Ȯ u׃i}\Q/2G!HK}T xglh\G_ɀ{6`Jll a- Q@Ġ< %KMQ˹sP. :K77LWs2Gހ6,ӄ)hҵ#D^(!y}%D!q촠7fcs9h&:k5p eIi:ňt4pv/|/% Q.Qa C@]'΀EaV "vN/,[eǶg)86TT< pC/S2YKSP3U.AI|Je녧,NJ+؊+<!sϽx-S38a< @@Ѩ/_줓ܴiϗ0 zj n0L1p˄9se6 ǶQ.c[ePJ :߾y˷v=cL 7a2`_朮SWZ;wB8l˲J+p]-=o@_{E\oM k-;BaamY$FV;4;>Z!x0 Xreld͕D߻Ser9P]',^,r|[ }{qaeJPζh"ع\r}-[ѐL4M4M4M4M4M4M4M4Qv`%tEXtdate:create2013-01-07T15:52:22+00:00I"%tEXtdate:modify2009-03-10T22:42:00+00:00/tEXtSoftwareAdobe ImageReadyqe<IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/0_coffee.jpg0000664000175000017500000000661312625666430022305 0ustar fabiofabioJFIFHHExifII*  (2ib SONYCYBERSHOTHH2003:06:04 17:00:02"'P0210$ ,4  <0100@D2 2003:06:04 17:00:022003:06:04 17:00:02 = R980100(2SONYCYBERSHOTHH2003:06:04 17:00:02C  !"$"$C``" > !1AaQq"2#4Rb%&3BSr&1A!2"#Qa ?I;[%y5@䂣JN?V xpA[ד-Vd0vBsH;djΛ7_ ul /5P$CۀidzbOCe9FL*wMnTEH+dsW6T{4. Krb0'Њ)p+7k$䲾|$sGM]2èr ]3xӓt)Р8H*Zd|6 gǸs2x:Z/1dڧ*$FR~@T# ~r1{ie<É'Ҷ8LxQ#7Z'zC+ky3:qePRd`5L6Z, ~=}OjZƻVV&Ơ1S3e0BI% $p V^yj T57|$ Ez)ˬnr#qFyԘDY<[[&)l>[2x2РI7ܪvm}oXDCj#Ԛ~us+oe_ >f^"2)u)՟k+D^,ox@iϳ!G = s׃\+MEK@iuBnd!<2Tw&0fX$l |PXu-%EzCQn,ꎤajXkk+na aqG")|j<;8R{4՗9o,I2;y?3͓v g=yMzѱB?]_NܩUicݟڇZBr3qWw5_ZZt?k, m -6H 8YmǨ:ȟ.Lc-m,8 J؃\̈ p*I4(Yw;{k=圫c)TgOh$N6䩄Oq0G_/rW@ii.+8-!J)H&?1 s'3Ipnަۜ#@o [;jcí..@C|+"`ߦvb[cшZGR+u*) 9suJPI?[ }Dɒqվ=Fxc亰0?:}A)*9kUPvNOc[ 8 V& XmQ]P%*WƲZv{QL!#Hb;*R)8w4]^&\/-=2sUK%i;=OH|L|Q}`;B*=oʕ/(dhaFonwdͤˈ_Pߝ ޒBE+G#\׋7"DyJmJ ~}vi fslI#v^VS9 ";tٗ 1RRAI؏q]W7M[P#b; Lpz6 &7f2 J|~9Io xU@Fg㯡@ 9M4:;&b~lZR05pҔym'}J4/ܮ},6xWF; I. ^G!|Ň).m S\ Y%PP+o 4? jzQv#nhuݍC!RZ٤IX%' G"*cж7:՛Z@ciO$|i'^63gKڽ+!^QOSc#-5 2j3u sKJ CkRd5EO Hmj[KRrI9&!lU V10,+ ~5j v0 UCinnamon-2.8.6/files/usr/share/cinnamon/faces/4_flower.jpg0000664000175000017500000000676412625666430022367 0ustar fabiofabioJFIF``ExifMM*C  !"$"$C``"<!1A"Qaq2BR#Cb31!1AQ"qa23 ?,LLLb11rNWĥȖYnk_3:ʴj#$ [\X$\v{[G3ðcgNrs5L1ETt8CutK5 be:5/I;'H"ϘvdZ<#Hk-d(딗.`:T>%8]BBIyr*6[>Ӫty&!’W9T{&B]gkiUC-.ZyRԓ煖auK DQ-bJ5iQe|6R} ]PT҉|gg_;rYqC!lTݽ` *hlJFvJHS1 B9 .FkR !JKIqVKGE qenCQRpc뷖 FېGTWq*u`<͹b̅1!ˎPm_a9% +RA,f:Eٮ8Id5PI.uJP8Έ< uSx.m{/@v A*r?jšm)_2J~Fz Im^Q7I?J pc8CLXFC.u=)-:w=N? [Ɛ.MZe BI*S*:=zPE-n%@Q)w삔.QmR ct'RqiAM.Nӓ&ZVcz$v؝{stȒ)&4BYSWȦM[1ʅ*}P\aJy%ke{j^rMbRTjcK)O-ZsOQ;l1s3\%2G0#s(iaJNKXR2zoS#C`FHOt&?Én莖 Of3Y,|58z1\uĂy7' w[3.Q>Ie:;{@x,e-VxP9+;*,ok{&^TEC,!J'h5g&HGl~<tŚ*:(]7#O0r<>ڊVDn1D :kD 2jQAee:qg5iqs<' n0’eI*Ndž=O9: 1S+Zssni۔.M1nlgRG$_u)ӭQ$y8ol-+qD>̤iq*xrƎD/euQ4?g” P̜̔oCK Z9dylǎ_CUhK -L3%"2Y*迪">n#6%%3y.^6 h׻bD+)"BOVa#)3:|iv2Bo;i`s]=`6]>]&=gw.G ;N+'!"#$1/84;atԤIvE 0n$6e\^H5FzuF+WCI{Yu`dF"Pot4e UT Eub,0Obu0B;z_M)5S.nCK_b B/!u*ބy)0nkXخ|*r+nGab9''٣=Iǩ:߷Icrg6g*vkdAGS57++R{qU˲Y):vdⶳ3dkO??\ַ]݉F8N)3HuZ*6-*\z$!1A 0"2Q?R ڨBbE1ŭGaQ#OHXa!*J74%4bE54!BC3LԷnI>,E\ r NbPr UMMrB-cu!4ƓbȝlK 2є,\U_[=\X#!1"A 2?۳FZ5PFFKbVfLLLLVE왑rȓF2~Jd靏QsK'6OWt%Siw&$cY+ώr}u=,h}=ktҽy!$x=؝ǃ# ڟ<=xJ)yq4pc{`v,KVMp+XY ?1kė=9D0fp32&<~#1v]*1X&{~w%I٬3!1AQ"2B a#Rbq$0CS?9;ܯRܮW>ܡ?6o\ߑfqh!m᷆8 ((q`{ j{㩧0F05VlB) 1Ŋwn<$?(N$YRf1]l6=';R2V̋"//YZoSfaӟ>78WrAcSN67+2FD<5ꉱi`w,i>T,ζ*/d?ԟ JiNZO+Vڇ]:+ pl27HTo0a,J7ji%}C-u;M`d;@#CT E!AñZeM60dZˬ^=.oS4mx9xC#³vֆ FOK&,1`ZQ۹Wٵ%Ų˻cRSEr^tMPKkyRL:Ý͔údnɷbTVѷGBM#?),ogr; M/_cҁ+p pNԎBcs{ x~0-Ӈ nOW=Ke Kg&!1AQaq?!+BGܜ/&i}bL?d^'0s(8\ pGRL5Au愽 X!;rf_q 2ʞ y׉XHuԤ\0bS 9K3i]: 'H;Цh#]:&ŕ(8m,d }4X۠@]$r%1NUۥ,SF$XC4l2dT}P wxչEHSd i} tfSDpM׵BcȝnCY*_fNg/>0щv"U?0admͿ$Rʴs|| ag u[ek X<-63~]⠳JEuk!.SEzOW.Ҭ)>[z1Y][ 'EUD] D +DhhrZ5Pԥ2y F!/#/#ȢTSHW݌*()*!O&/wcw-GmSs8L_T̲^\z'u> Cxta࢝6'BC5 GgQz_K)A–8]n*ZmP!1AQaq ?W6VZl"$1 wb > l5} [ߴ/wՖb3/nN#M= .8?f6f7|[h}o[;\L6ㅤmLnH8~N7 v")~Hb}37 'Ę03K ͍y<f[_!!1AQa q? HoP=H$>088 Mq|oEO!7u/:9ߖ+^&X)lPZ4ku86]-t-r_C߽7_Bz_W"L~X\ 0ôkԸ!!+Ylmuhgכkяz-4M漢:1>3)36T̽]Þy<({繆\#|HǙXugiyͰb~#dF#!1AQaq?WGx ,;GDž+,Jߔ2bD _ v|֭?@xD<A㻀T㸷8>`*fDL VX.%e7(4״ 6ܱ|_Vlt. scX+UPa #oH,ҫq@R/<k4ʖ$IS1KqA#?5(̶ LmVmێbWrSP (KqM+n]'Sp"M*3]&r;V8Tx`ZAOm 9o,U e460By"X]&WQiDB`%\:,QEuRp]J*).,kt#VT)nH;ÛD P)AĤA@?XvQ)-ҴDa/HͶ&8ja]IK:5,hUhLS9ؘ)Af/AoÏ)}!a,2ÊhM[\H&8lyDi>' vn;[r"zSjW!g*o半ZK% !ro?2'4" LNv[M{_s׺mc9beA{&|̋]pUor|t*}Fv "a@3#7wu띱A:YE::IB\,6`x0ݧBBR,N[˧{ f {N /`\g`G = ZJW)@]{u D[6ߙ[Vh }U>6!/-Cinnamon-2.8.6/files/usr/share/cinnamon/faces/3_sky.jpg0000664000175000017500000000562412625666430021670 0ustar fabiofabioJFIFHHExifMM*C  !"$"$C``" 6!1"AQaq2BC#4RS.!1AQ"aq2R3 ?h1޸C'*L4hF|ULX%&iI۶Ga9.km?xւnF""O* ;=5T&l FZ߆1 ^ԉ+pTyϨ\~j[jx쇖$yqܜ}*z}%"}/w={ذ J􅠩 |)PtOKj; {zۭ6x-µ3)Bl$>½)3;ǡW]1;3'7l|~8>S޷@;ف)0:i+)%e k ]-m_1!8ӌM8āIo;Lk0!He8표V҈A3nxkhi $y}U(=6|6˗~f̴u>;)4ē7 q6UU:s>xxҎĶJޑ5ܩ*q`O|v5j;!%֙IiFm2çءF.E$j3pB!7q9;ji" <R,eCAdaڣc:V lx'S䥽-i 2_$XoPR<"~q4K"Y[?juBX5-l%f*I;x&}B#;' h6Kj[iyVWk퍽J묵j&GByZlR1(Sf[Dq!N2I=[=;[!+aqNdϩO;[ǬζYǪGXf~Lخqڽf7K'KzZ ]B%""HBQ#oloҚ6'ܤ5š=Bp I5)%Մ9SO]nM\# q۱UkR#ARTU;J=FoZZnJi,r[k n:^]dpLYon.qǀN6JGU凥 i]?X,s sP Ml7+ cvtYcm"Ֆ+B,ړ,C-3G)4ײ(P‘Zx oq+%J=)KZ B[GqG ã~aҨq.NwStgølME5-N<&9}fZPJ4@['I9 2zWX?3:Nei Kk<{#q XK!XRT6U~Z4i#JiI8il ې_AN1q|sIHE|uҠO8F#SK B| քA'Izӭ7}E5w-Ry<_-3+N\WsiuJJVH6ُ-:b q!R]Њmg /CwRkBoZz}ì[L$GUxDe-R ov.ۤODҐY)- 0 i1 %B2DfJ8 m׊N2cB=@$pAm6Y7LO <i VYuu?Uһxqg;1?ZK{Nz2$H%^N;dˠ}nZ.7?Z&܄IwC8IՒ[Rz*u^%`6ilJT@*H=?3Ū-PTt+E Xw #3+yVhIRp&DmEïyam%JZI'nON1:#{ʲ >sQ6؇_M/1.M3f2A,!E V}h1]EbN D-**N@9H?12AW6uyWv 'n;~pjGp].8Bmc8py;F{dJW2ӻ. p̍:q^|J|;p0=&uUN[.X@K *ĎO!C(%I 0xq6p7a\AL:e-kl˜֚BW*VTd⹜iv9VgiYT%OB!N$:~bچ P'PMpwHΡM0lڜ{Vn+ZcgZc9+Q?ZgRuzj%֬֙2`g+|Gںpu\3]J9넱dN)A8jdkR|Uܴ*{k[&^abH)$!9$ <ˆ\AFivњK$mi- #uRΈĈe|nezF~Y_;jձCjPھw8ƥSSgБ\\qg#rp3ҽo龐fȚ!rC `'9$J*ΣMZ*!k00'o$$dqU~)|c='k.O FR3ku&۶ߴ2n’Ґ)%Ĥ$t~5xTG+m2ұ;>I3 f,aSZi8oǏŏ"\"r%'ђ^0qzcS>"|diiA%C02q,B.L J€܍gyD~1Hb7Gtx ?7)$c٣vpZ*A1M* p> #Xsήj!ؕ4'3+3U1¶S)KZZI&Q(&E&A =*SQua #%J'i&֟ ̫P5A[E޴2\S8'Ti+]1 ZB-űpHG Uۑ*/^u٭yՄe XMwX/}z^j)S™lme-?c$85;C1U(魧 mxgI>E#5 (1I80;qjƽySRJv@+s~5a{5+nZ̲ !_k*Ŕc JHGL:y[iJRS n`qϺSοNTbK0XYi?7+U{5>G)r gG?UuOKJRПYcG :2c5$e $J{ .WإI2:y $Y=WF|ZV,h4W-ǜsj U2WZ`:`;*8Mnèa2I Rhu"J*Ť3Ex}_4M%YH(Wg{Ou )vMlݜrYkٺu, ⾧]³$-H=ioȠl$ 1W|;y\Em5+ٮ_Gy<,q>B *w⾷bD eKpҝՇLCJAMsPM_Cԓof|"; b8YRxE),*/t&;8IfB#P5r8FH߻a3p<>'}>.-}ҟ~U?6PpsU:gg@' yΙѾFB^ ![/ۍ"]5vUg|o:q.;pg=: d =h#h7۠k_Ew~"I6#\@:FܺMQ|?\L]_[=Q6qar#f~u^fiZ\|if;5{JE%UAː=ƺPk[2Ğ_0?z.=bKDF{S}UӝI??(6 Cinnamon-2.8.6/files/usr/share/cinnamon/faces/7_dog.png0000664000175000017500000001553412625666430021644 0ustar fabiofabioPNG  IHDR``w8gAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>,IDATx|yUuMw[UƮh 2Vy Ɨ!H  (/!!QCpJ@DdAf 4C]UUU]uo8~|߽U45:o:w>ꨣ:ꨣ:ꨣ:ꨣ:ꨣ:ꨣ:ꨣ:8Xf 6c?}rt_"y؎6laRU pX!K{i/vn2ș60АvqDc{Ry`󒕫5[]/.,+9O6FWT$*®=yܾp蓗+KN@U}0.`B14Lh ^pvN]~JgYy'ݖHQh=$eRXx~zO^穝YGdr(.-XHeYH!hV 8m6`B&Ƙym=ZV.ٝS>?O];sc1FkvT۷ƫ_Lzf-1% M+Fk &u!}KtN7eۧCJGV.C?7iXυ2j/M %P2z}xWkG-7gtz!c6(-* C6Mhլp<4l 9-:Ie.wP! * ͇>ӓeX􉰉bhT;C6JJU~o?:Hd*p7ڋݬX Ă0yp93*e[Zp9簮.7f,p"c 0F1Dd !,J #. l߶F^Z HK?lx;6ShѯW1F @ZIT*eR)|s b&7MԄL2q"Žs<lټ (hj~$ugh[젼u\P{jor햢D0 8Xy!XNbUcB~ס?Ehooǖ͛0k(Kw=ػ ;ۃ6&)08̆>8P@Jmś3UWA,kF9T P1$0m98⨣a6bˑu!Z;q f,\v:'f0Զ]p\QsdH44wT‚ p"DB$%Rjr ۙy1TbZQidy`A&eEQMXRtrJm<(!L΍CLJ32U_k/>06t*,"wZ+~dLV 5+@iRpj:zm[7c^8 5g}~lɒ_}d0=`m(˯FQ4(JZ *):iroh \.M1;{V-__Ji"a066755OX␪`IjT-l}7ߐc;ҐR+mB\670=X,_^,wADJ&=AM+5qJR= aO^-?"A ~U<!p W8cC>+s%%@8D>6g!Ir|Э̧, VCǯ=W|1KSа MKb& F^{~ %JIxBE!*S#{ ]w +n>#=-qc ٯ;*8=ZXo #lີoZ''JbޱK{'6]rwfN=:w}IXbcڌ1&ɓ$j +7< 3x)U_|RecKa{L N{ M YtwʁdžR>Iɨ3RIH"B(h܋ šwRhaAZ)!e0eQ2*BT_o:o9Lhg |>w7ѻ])upRRB+ Ү&twCgõf (  L['RB0e0#%cec]l/Ü"qnHUxHxÙ)X !;MZh!V$;)u7bN~'ı)?a9:'u`hBeCY. 'AI l10@*%1E"'P]9r;`f- 8q!0mu ,bN \oژ׵o Gu!wua %ƥaޓ=S-0Z5l:R߀RJUM ͘j1 &h`J"aI\JI(^Ty:. DKHF/WÄfah/&̈́eLf^Z)ah5Jq mRjq#Mi~ibRxDkqq(@VjdL80@OJ)o-WU'GEяݯTTB<;شRzJIPQ :ޛW#va4+SVPa!yQ]Rf&0v:/Xj:8>2TBص !JBP AbqTJkKc1a,|q׭nѺg@ 3Ot's؎]2[Zld$>*2HQPWQhJ2+CT ach0Zq7/~_^ne0Z qTSJmqa!y<~IؕP'cCaBpq,1B~/c4'C"C!'Ljp31k>~Kae_,5'DƖepl'^Mi2kա+BOۣ(! ۶lB5m=^Z+7I^QH7ˌ+LDˍR;ȧqg:ϙ1]U?_炋ٶUc1 d7r,5q c@d) 0̆9\ǁ?OlRh$~gmekb^CTXI|;~3VV OFU3-#rQa8f?A)ш|C&%X٤}DifLi˿R|[{/0sA?}wT9⸭ 0vMLRJ\촽6.U0 \,fn(0\Tұl;vM^UiF@!K?u}GC02(p˞-e_>]GF?8hP,E) Z0 B0ۦF0rRO[ 2!(XaE Ypke|g\꺀8HimS٧~|ט|O]k~rRFUd|;!|+={RL``16ƅg{Mܶ0ARd`Bb?:Xz簫\[_v\²tn:VIVb"jJvNeSr?~qg*ږg,3X#蝹PTŏ6t#](20EdUЯǶ彯ҭ?}O0\Z=lC)t 06HlYV0>,7,ZЋ/~7+/Cߓ?D}֨bng<s vة"Å50Sx>q:ꨣ:ꨣ<3=~"%tEXtdate:create2013-01-07T15:52:22+00:00I"%tEXtdate:modify2009-03-10T22:43:56+00:00 tEXtSoftwareAdobe ImageReadyqe<IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/2_12.png0000664000175000017500000003627612625666430021316 0ustar fabiofabioPNG  IHDR``mo pHYs  tIME ;FtEXtCommentCreated with GIMPW IDATxڤ%u'v=*k߫&)J"g(,ٞ/0 a؀-H#{LJffo+wnVWef!ᄌq=w~ J)>;r⬣u[ ]9_ܯNx~ks*85Y_PLj PqjEMFK*yü#拊F_yv{v^\.F}V|,<ڒ9<>G0R39J1}?gExn}^4|9xGCg?JgxT=t::ucԄz^6_Ts^KrW?*8ׯYz>QO/>zi|}?ϡ>r(>gw1c?wCb~M)BsN8?JH>W@AHX!ϭثWg+Ǡs_ p :_\x3~i<_-sqc1bc( 8FC1̋i19~1G~ͩ:;>y'zi~e?J/NM<~L"oQ(\~c*z(qSDO~*iQԫgUǧ35b4ʨFax)=(7<^WVNh<tԴjmv.5,Mc0V;TEQU98PvT5BH1y!b!b &wk4 Zum^cnU 2L0~DŽE:~;Jr>wl!hD"!EQpUaorjbTkR3WMa:Bc(1CxnUP!s %B6X`碔$ބheW;ն*52)3!ꋹst]^Qx;HHX\J)ՇdʠqcLYe<3Q !X Ҭx{SY8Sk薭k#AX"Kxi=Pz&q=0d%C%&O ѽ4($N ͤ9ox+(:YHK̬@" Hա06qVrf[kujSF@yT/HGSkfb%6 ATCHu?Yv )HbT!l3+\FM kPf/ r3)cub1m&˩7ת\h7[ajLGI^022 PgO/]ITPgT15WΞjRW:A@O&^"q^Ji@%@I癃(-9ODz"/̓x8}& AH}w8z /zx`!w޶xSv/El?h8 r@Ԋl[hFkDh8h)8.`0DA" bY)0BBJ]q~筭B  DpiJ%!AUlK_<1xf FUed;T}6;BWUZq *c^$ G`J15ՙ$DRJ"@(,s5$)5[9UPI^gb Άpx@,`>=VGx7^U^8]IeyցY׍ e1W)ZNsg" \d p0ۻ{-GqQukӑxtKKr.@P@Y9ߙSkjA1Ek_WBcH <!%` owmSU23l' YsͤMSOrB[ҴHŪ"8.V>6ոv]3 D L1)BQpxD m%qO5e2tC^/JI/ۻ-W?xN?2; Ҭ]iJُj t՚qyΔlTBT/~]?1Z1`KLml5^Y M( JmO:S_6Wwo#¨301,lm{`!|.H x$7Ssx,'=~@j F4 }q]54bZguu{{pGȒRiBڨ߼7çw(վvo_+iR(  @A!!c k6OBPf MfbJ!QB @Js(2B|_פח2ϒ(hwEnm\e;"j'0+h{tN̟zⰗ (!H x/woyr6PFn6(i5 Ttb@B31R)0  (x )ϑȀB [}-#yn^,Y2Gan4X0"1bwB+e`Xw۳ʙ7oZӏFz{ !8fn91:" B   L @J $@hfzIo J D0Hq$8H} _biD Ih;练IΠO۵MT-eK"U/5Oͳ)g(2fXgT*^jBSFyn@!dۍ'{y8΢qMD8LnY"LCQHGPj YHYTzrڳg B=?~usWx$pTbTl ngf}?pEs]ڨΜ<- )#9":BH( B@<I*_US !ꖭ.-fD740Ue ]J1J(+!%/E$J HO.DcLC`CBrҰGE0\`f՛/a %@S$JMW T0CH 1A!5f2ED,@b(@ GNN݈b1BC<3jo]9X@G ;;#.jal,s aVzw'RM7 FkJqQ:`Bna]'1JZl!K׀BB!BaDhF`%TRBAB! a BB4{PP*D7<83vb?'F8j8+~/( ]3Sr+ݓS/,&vfT԰4M%'T0r5ˆJ@I)#DuD 8 xTE b2'L=")RGE0!( ,aD~ (_Sg(2ͤ(򫶳)0rʲt?l2Ti,@iV^LSf a!(& (%zZRP!%PJH \)(D(R(֞T" ! R@B<|O㿼yiԮ,Jx/W SwCEqiT*2 Uf>4ǵkpOH_f8Bx-yI(S@!ee"Q^S̘B!ċlod ~4f%Q@u  J|/,EɓX "RRJ)@I1?-ۏߺ:{_sSyިs_8a)x !uQJqI@_y<{̉뎫@L6>ZS烝 /}5<~򑎔.Ω hQq3mSlW %TAĎTX\%ЮU,BTE$Y9?5(8[0,6x(Imΐ"TL+E8+ӕ;:mC毾i[ܺwU)F{h/@]<. "2DZ`[²ʝNxmJ*qxwΉCX;5GnꭖYb!WLJw x^[8Q/v i7D2ORQy Ϸ Qzz^8SO&]1V^yD8QYo;*eg/+U@z^ZOuf0IԿ/~x㻿7ZqMVPM&˧*ՆT97K5 D[}鸐j($B !>!lcNxfI>*y 7=؟,bu8s͟x2QDz>|c1o9=^Y'Nw2*9ftۈ}wrQHtLm✻~cjm:B$d$h\n/q”BLtx\ל[5#SM7"%eZ#aJ0 LjAߴ~v5D$ki.KRY&W/zbw߼rrQgЬż(ykkhkUhu"0?I$ wz )eXvYF~ݞ.ۍkXyez71BE: "1O~%KmةFKK38Ą*(ռղk/ٵ3BH!T14 0ify3Jf aک-89* ;UV6i?qOVۨ8anσ%S G=?j>ٛ\i:ZbowGg.v%fN-J 'ncv6߮Y(O|;ըQ^pyoS@Z]u4.6Zi#%RH֚s^@$yt+ʅ̲ 4 !r?h +5JAQ|6IzqV~}*MJt'kI 5<]h@ Dg> ]DE77λ짻#?͕Re߾q ]Wʘ"/ qzVUiԝ]yx- #3LLr}Ge4BrB(ɋ}•ZX CRAXJŋ"str;,;/ F<שn_y;54ӵ[_h}ifF@4m?Ra"8`eIFi.x&d:1fEV9#1e0%x(Nj@#".5=B|taҩe -4 {hQPFkWu]6ߢRxrŘyW{$3ɲ4ěH[[X<5Ep4 S.ݽ(/9P# 3UQ2WJEAdEаW3XbcDܖL]qȥŹaǣ]״ G6 TxޮK)Զm3\zZL%@cdYp{2jLӤ!Ͱ(3eC侗_ךKœ0q _ޜ!NU+]"qslGܵXD7yzܻb#(/|oG7vD|"P:ɼ!4>L8{8DI>׫d0N|AHal+{{[=2yte{Aȥ3$M i0 ɼQʀwnXj2sF]scZ/1eI0ϥY39J0J!}0w xd*KmJ>œhPէ ~ #}wߩ6~pӰOlU@yӪ0.]r[z]|(#<|_[ld{QQ]y+ < s4UFq>,e 8TAQT S9È`E+@:Ì@ c薍 *qzV>쏂ٹ%5ǵ0/* lz>j,˦dN$:ھm+lbnWƲqV>Fk≥v&gkJQ~AFBq{{;X5xج"l-;);:nhdkGFњںb ~iqajؓL^bF#'dY|g,2ruW*Ă2$i&01( QH)&Ta,*5:.Wo7gyn8IW0 ٪0+N K9e2 L_xwlqqQKmB?~wwЋcK{7lPAb*8 ДS(\>s:|'N(xm>e풱<~ڻqxP{LŚ؎fDEGQ(LC3FR KTb \@YfXVwە0떾xDg:LM(GwTBUm10u<xX !;1ǰH Y Z~{knzY IE!D;B=iu^(DM$][9fLO$RE"SVХMtnqQ؟ JѴ-I_n Kz|m >SݩljbO E|ꅻ绣Ǔbnao8 I!N4Y (Z%rG~|DV[^Xpu# G 5H$|%L"T0k^ei# 1V %F h/ZL88kaI%kZDz~-{-wi3yk7gj82I2ߞL'~jl 8&Nd2Ϯy'n? 9M}/l׫ZFL7nk ɼBjU>jt@[eF R~S5Tq8H=$Ľ82ub b\p<3fmJ2>LU<;5ݮqQk/Q|oo4|ՑyFv()3' Xh U7Cɳc=@'pm\1nT'赖 tw 2J\=oc8YHJk@b,V)E~OÕ41&,]nVm/O͵ Wx[Q  P+P1$Dyjљ4[yP[^Xxד.L?Ua0NY@XXXER4yܛLdm8|yI,.-t"8VdNS6(VśZ҈q^'p%I,0t%IO+?Y7Zq`HE($IذMΟ̵ޞmJ\,^-2ޭi>ʋ @MDi[9$(ϊ>;gZ6Ov7.a:Phn'F`&ؖijq9&tEKv_UM%V8|Ei77H3 ][v1: qBJB#)q2.$͢Ҭa2.lkSJauiLj knfz=%MR :(&,v ݶυ*/_:= XΥyQܙZ3?3-p7bgL*zRšNJ (=?{qN|Oݝ~ yAJpJ'n=Nqg["絮`쟬ZOzug(mlck(w1sLDqި8BY~}ӘP!8RK*nh0 ,IjuNmfAdnA v"Դ?ZqˠYhkqjDM_%& g뱗<ryIY~idm[MR'⌥&+Ϟ7x=؜ghޥVE<'9|-LN2M*J&ybSʵdV.mݢ/58 K/ZqOR2.8/ u]iayJᙅ[A;;Ê*eJteA3b+VN|i,+퍰(beQ/F0βJ~)~^YpXIxȯ8JL|xҝatl]cQHqBO򵸸QӖ83rj!0H˷MF85lPl Lf1]8e!(6A R aYv,su*C"JS3NFVĖE ?bdXH+l2ײR8E%050"W۝hsG,DvuwO Bg;Cr)Csŕ*S!Ue0K~[~7*f!gZjq>IpXtNn=-}~0՛ += f>t\ܮ{(Y^_8-Z5fUw Ȏ53IYyc\dIR*!4)ꬊsݸӉYz  yL= t]s-F*YAL$NJ8N#GQt(vSgSkUi{xi/H5{@f۴ NVk1a%em%cN2i7.Z>,.ZgFWdQneu~ܖ/ lfɄ ޺(<9;izyޤ[խlo,w[kkviyM+V 7Ni ;oˡ65JL~4&p (©$;kfNѪY?]Rd2(E1:G.!A PwoLb yհ!V۶}01"raDǣSlr*KVlx0r!c^`.e.M!<)%/u^lP*^Z8%,AFTۏ~ <}(%U#]ZL 7e+?ˆa0D|]w:@`L",DjVL!dEFCoWoVoꚫv&4Nk< UY=\QxzMa|?] ͧ]Ǎ7&UjCx.IFa(Tٞ!TŽb7L[WH 0.^sjĿ궕W\15{ɥ\ICu6ls >&#U%c 1v(E[0Pb>YHe7Z,z,ݔ"q[T}#VӭYK&dQp{Zj4( ^nJF8HCm1H{d''v/B- =x~ߘ|֡9Kyu v֪%6Q'M ,4PQ0B#q@ixiyzq- _IENDB`Cinnamon-2.8.6/files/usr/share/cinnamon/faces/2_10.png0000664000175000017500000002206312625666430021301 0ustar fabiofabioPNG  IHDR``w8bKGDi pHYs  tIME/(uhT IDATx}yp\.}o^խ%yZ䥌m `0NBȰ& $bb&,C*eTM=** &&I^fP=!#˖^nw#>_}}-+YUdz;_3kfͬ5f̚Y3kf%_-[ r(}ߟ+b8jr9ˊD"Q'A<]˲)J#~9|AUӴX,v$IF"Ղ t J<,#@Eض u I{~q= 裏eY$I_D"("G"Ȳ DDy" ()$AQ"BEyA$I0 eA 8(DQyx7Oc͚5C6i4"ȏLY۶( &0ƫw7xW}{ 7ߢhY,;A -d$!2S(4 7c=gyi" `ոۋx<Q|GLF @UUmu=|R|W|+(GQv+dd,3as GFFuVl۶ Ji6EKŰl2\Xr%$&r!"ض aY ė*OoB?EYiڏ92MX,ON

b1={;n݊RĄI. q{سg<"ڳgnӣx05;S<,_1D2(,$%~pBT}^{-.\`ʶmV#X$i0::*A}׮]#gu|¶mttt#;Rt.I.RpkRITgώ:9^0]f 2 g$iMP'I,X~tvvbppd4.2ִ#,syK/pNꫯ/Z7|;%{u2(5kְR4 W 2S/2 poF.C{{;2 +ir$ mmmp]iqtvvC'! SJ ܹs1000fQ5u/?}̙3C"`7tuu!5a)K3W(Jh472~tck=xDؽ ]jU_俧:ߏI$IB6eGo鬘 =X,rbT*vhšN8/>s]mY7 V|ƍPH[ZZ088D"y$K風R;cKBx<θ:ddZ HdnyKX k׮zOIyq(-A À(H$,᫝_ω i#>}䃎-Sw'͛|>˗#͢X,2Ȕeqjrz-ib1!7  7 }p ))6H cǎT:aHfVB__8̙VLLL0Z8t]G"`5R;v@288ۛ:WTƞNgKEaMmVRq2L.GVY988+V45y?ӕ Τ/Q <II+ LN80 y~SSShkküypaq6NKuTUT*bttuzM,?>k%Nǀ;}8p+ЎL?_D"˲([<0i sUUe~a(xa62 Y: `/80kaxx֭c)39QX5Fߦqeض͒10X3](?K70qKxgXf!~@aϞ=x`&e!c)b1@߯_{‡NA<[oՄm):gT- G0=470̖ϲ^jlقbx\Sh[ES8Ae|ʬQ, YכI0DΙwjp?ژ|Cm8z(~1}1] x'>~_aM wޏO$I,?8OYe ؇Pd&b$?])wAX_ؽ{qPV]4]0\z!vatJRlxr)+Zm?IzΪ~*Q "D}P.r—ysô8f6~a:t"Rtޏy.\He]>y k2t}GTBVCZmRqhM>JA_O?}w=6]J 8عs'qf<,~GᵷjoL&ȑ#ij ꚑx* 2h?ƌϔ]Ŗ-[g4 &p.B/`蹺`a'oa"s#KxlP\ru{nSAX,0066Ɯi0Ms̖jv{c yKT*w#Gh0Ewxg} '[Q;^ pN$yi4RVLl~,8pC&|TYP8[&!Jꫯg''"]'OW_}x:aۋRJmۮv QV!J%cCUU yCG}G.;OOlfQż%q £LoʕaB'?F:etvv>#b#Krؿ?ǏsK$h1򋏆dַtRZ>a ז;[~Hhoos=y_Oμypw#N]wݓiwŪbhtwwb?*"E 0011C\.7NDlNiN|cc;x9L|ߍ$PIӃb?OY>a_l6oތD"Rt[{1)>I>"bi(X,!@ujm"<RE|Sb'WN>擮p+"ϟ^xG Q׏~뮻( \q6^q;Nkz2 (JN,ۥi h4BMj+V /(8x ,>ٳQ.1[$|dž v%4EbALᘝ )0?9^Mp-`Æ 8 ?q@[[Y}UU/EQ<a0kt΃J֖-=\?\z饸ۛ9x+'';YsR`ǝ".\{k׮(Jp]`]s5I+`jj ---5M[!7&tRs90Mldbb8GիW;@&aQE6ypx-s}vŦ\jlW_dxJb͚5wH?NNoR?yٸ⋱dr9 ~\ Ŋ{T&) %|VX4Eww7:;;q뭷<EAP@Tyi߲,O>$κ`ttT~zljj J'L7N59s`ժUXr%=8$aʕظq#Z[[8|+0PBVNG#rL&zK⮻²e`6* Jφ Nzzj/^|EV{4m:``t6vb}bΝ}V-܂֦vf1'_$K qP ȶmTUOvmSbߝ.,X@VWʲD"e---lT'aܹhmmE<G<G[[ڐJ ?b߾}8l9Th* nῗ L{дE}Ys_/}tr;mSdžV2fy]Q,*#ZY166ƒp?lYu6m*ֱ]QTvu}aZƦ)FtaLNNJ˲P 2$jyޯAPU՜(uB```ms~ɲT* F-"+3 ?04Mn7h46}_NYeTB2|@W&9' VattOVLT*UUqAƖ&o*L|/ RS@m44MV۶׽nY7]kt JQyGP@&a3<..xw<#,H1 aaV:հJLѸ{ݶmp=Vkh0u]V0 iLMMZ2H,c>ۍ|8O/_dVhk^G^GZo4:_oI\.iQbܹPr3 Rf͚EQsNx~jFy&#',,1 ټy濭Wooo\Uի4M$F!uDQ X ld2bo!%@Ě#s|Mc;DQ?EnKQ_D" g2immŬY`6? DOɓ‡\.P(0 IG%IzEQ{}}|ax  T*(ʵhr`0oh $|tTQBU N&׉Dؼyvq_gtRU˲.|eY1sD DD"ibw'TYۇU"`nDD@ff 0`&fI É@D>HL#Um? HD̝"<"EG1'{E&L@5Kp9vwdhQ301]XcW N9L1{dHf&!™ *̜n b@ "" L@g ,D`&wL&̈@FxyyQ:"n!B9#[ d*"!戰jIPMtD0 e(q"B~N"HN0g n,$I"pg!J! !#c+3Pp ^j1eTdL/{L@T[`t]!✊]"2Y YǍdbieR[+!qf2Apf233efFy4С Q$<(Ĥ\dfK+,@h-_Zx*OU Θ 3%L`" 0l%p!̉TfɌ_e#rkQ~Mfx YG#"BgӔIT$۫bFLElhgY"vj) i'##ly~;AF 4!D JvEQZ'h-ӊJ&TKq7dDDz 2ɽ]n舅 0RfFP? OyGZRzӝuC 8!2@@ tj+-Oj U%K$vE3Ъ^^Ed$넳&ZkF$"ighVwvA(cӛ?x֩*@Lt@G$3%u4<1=!G)""TfVRD{ծ00Uj)E*dfDj,9 4S7HONDU bbL3#LjODP'e{֢EY$"jdTJG$L_όPGGStw"sQ@tjb_DfGf jJ/}SfUUJ!ֱZFxLx0Pp3S2in" T"r8"ȢFKB22<Ñ`N<"Rǟ[_n>#+WGd{nK4P B TwbY0 lOt0uE/"Ȗ~U̘o F2d͔eJfNIk5oUN0=Yoh@ O받_7mEDk^*pN)AY $" }t"BWYRU9. "rlW^Ce ȌPr?7kw܂9i/L(EGMuhCSF^̀ -wgM w ]j̤*,LȌF&!UdZIH> *1q#"+3YFEYfzdDi^c( ę I,6bM%F #)"]f,;ݿal'P0VkNKFYpba! Q{f=ȫfzu'p3xa9{i$DN'n8 H 0:S :Y##="= 9,o|70: Eu2\EҷuDզDdD0șYJ)L6.&'3*" pwHD[B V† rΔ"kL@$U2ֱk"H6 7 pɣq¤"IXd.tcᦪ lID !kfDnPBZ]XT;X@Du" 5H"uP鑞XHVfj R,v-g]! 2 DFD7 S5P$^cff8xĨ5‹YlF蔌 q$693vbA`ilhS[JSxHtpb5?8q w&n>`b z"̅YqiW( w#"fp`& B@uX}@@S#(83H=U" 1scI&J1H$#k\/Ȟ63|){.Z\^ī_z'C$ٮQ0 Fk+$"ZJ8g6.u#S7X6.`Ȑi( Jf$fl`HG &ه R5ʡM4~YLoӻ2#cX~^#foοE_a5=O7]Z#4MMP'1'f"DPU717`Ոhd/ "nX53CEYE30R2֚#"S(LBDF:n.3dgxYi?cG'%HɌ6J̺ʍ^Hs[3n$~1Jd4ֱUE3kRh)6gR£).L"f^ke)-Hg}GDԊe ԺdF]L3,ztmg :Lnuyz~6ӿSb7p!JDQH&M0'm Ɯ'Lw( 1S w"E{62 *pS"Lth9sRza&`Y2s"#j (&&b'Dr7+em7$ͳUN v_F/b 3Ah(QaVLB6:X04/=sɬdWBT\g}*MliOȘ#3j&D"t%fw~QΣ=QER5I,n`F):ڕ(&*CEtqx-y\}ǝh>槾͌xVS&0yMQbi)"L$PNJnڤIHxF)ŭ&^Pa(Bjul7%ĵ<#=m7֮r1# 0G@8p yx5עZF;?q "CQ.]b!y}5 &!HR(jh ͢ڠBr4^+(hb&33‰Yhߦ'0nY_J)K-EݝɂpcE8RJYD̪.ε25&j1?\>$_ SLNj}uPxޘ2QtPUwgVa*:3TE4&>EAP&072MmuLBpniCy ͳu 7ۮul{e6:jXu~(c"/}u[pk3qakP[4::\iO( aRZڞS3VL2dfmM&@d#PgzE(QWJ, WRRf&NkaVk"tڭ~{q/OnSb/lD{d&'7b}x8Noj61jRulnVtA+{vlif ;qU@ 4Ș5Z~mHD2[i:3QpWˣo}_'>TOms9?8"$t~n,]߭}Y|YgۋL"$ai6sa= i0H 3\vʍ~V=D$&B6}6nt&q350$#½0H{apQUI>n솗" DHM(]麢Z:a4DŽUk E٪!UʕKSȬ,ܼ[-"Zf)L 4[#Y'"p-`5FE֏[\_.| YK-@fpϸmO;]u'%bjM4BCvXQ p/4zXpt̐6}L>&pҾJȋF^Yjc>jD0K;I$x9D'0^X]{sWK){FN"]Yh-Ձ"{7mu֗ɔAI:jfn]-2ަ'iBīˤj 1Y9I#SKbiz%3t 0V Uu=befiou}W>ĶZ7g{G,,m4$QI@f~tZ-(4l!›0;fMҘЌ~`]9&KxKꈬ&##E[Q3ziGD"ZNn7::EDj/k,ZXdn6ۧվm׶ۂ(ܘUY_/oÓNEUA(6҈DT[bQd%#F3"&_& UJ{D,ҙ{E]L$iy#@N' Ot/e÷D*"?¶̐n6n<$D*ųd6f> 7QY@y0W,?_̍s+Ĝ,mM^ qȈYW 7""ZnW ?ΏD,ETwrn^V'򍷮f~)"vy|wwppau|^ח`if)ΟnYw|ņˋ\γ׹,Hf0 j6z]vt-3H̽ɮ0%ISʌt=H8͠Ȣ"Y1)Ӽ1VD8嫟{f]kZ3F{?Et׏oRJ'{>}döf6szR"猧E^)%l]/< .u٬&E$xi% R0w]A0\ p+|M/jZ麙z$+orR|v|Uo2̞wwxr 7;.n}ꛋku{I?Q+U/~ٷe[bugTF^Xlu%QE veFG537p!񇐳o[-Eh.j.0!hR Y[o*3c-o7߾^=vUj1NR`\lC~Z.Vkץ0(Ƴe/G^vu3O3J!狗^_x^>S)e1G7b_/(*`-Mm>ǯymlD L)].2;+.€hfN|>դ9`OoUCo[ aD(BԀU=ED[{7L,"915q,$H5"7~Tyxz-_4fDWwn($i_Dfn*w?O m<{9ΏOǤ?2s8yPc_Hg$ {.yl)ܼ}W+Jѐ][ -]p#Z:bH0zƯ޵_=|:b:Ne`.{Go\s3 V#ܽO۪"yc?vrCH")DҬS/|h^k<5ptik Ez$STnf4Q(ݝ}ƘTjZ]o7^6. "@$?|Ua󈫭UHr׺~\,WGƽDU`LzC5Xܽ '72v 1Gpc(%tI[քlWHRpʣy@62O H+/}vOv8{lYXH):_lI,o^'jD_0NU]w~ft%b"Ant"df*UD? _:n 4}ff,;A#`M3΢mBfD 0؏㖇ntSg&T$2-oOFlwu*-AL00f >}3Y (‰D.,B:LjAJ&H"!4 Yշ/m}\Z^v(8JȨ(1XwtnYg 翶x fa5kX.@~iV2_k[@xM]읿K}~_s>+ZvXG< q߉>IǟmEHJ͢5f#X12i"Rz뽃\Ykؘ\ܖ6DXyVTw̙%L"Z͖7>/|/坟n~GAv|amp8XJp~ח߆0itrتHR<5ȸuL n`dpD= FŽgFXqX j/.C0ԊZT)() ]dhsZ_V˙ӟIQI>%eT-]R3g #V3|XC쩭ϟۏ 3uI"-.TRh{7@ͣ&Ju@dFVSuɭ+"Fw$8zF/Ο=+Q 9yLMImeË׎?7~k:hǸONbdbPe^ư!9-nff?Ow5ÿ"zmIYih]e']YU2%fam@$pf6YXj%"NumO86OcrٝL'ZP]_/olϒ(ZY瓞dY/2[xlw9<uo?n^ 5\4W@nE%ó@0ɤ"0=긛,]HL,^}N-Hjm94ji>.>^UiO8˶~31/]?_py6n. ]"HΏ}ըkL?|Ku}vvffi=Ա PP\6,c6^q.nQoXv [-2A05->QG21UXp<\ H*@.*˗^3?S?B[Gӏxxv(l]wp:=&ǬQwÓ;2_be?=pEƨEѶj RDmM B)@0ddU#fnl5̑!"䦛!sb,2Lv۰v`Q;L}rs#drxz _[>XJ?;:-V:/n6ItxvRz֛u'e'ճGw۟?ܝ ԬXg}:EKEI՟nI@ ;on27ˤDֱ1 1 06wtHA5{Y&1;=]V릭bZkX&m֞4k76gF&eHk4cX(;w>$я~/>[o~+:^i?;Qgnu{3Ѳ;]/ztg_?i:0@f ew4DR-#_sQG4AJT8Vcv,oܒH"uLwXvÀak`7CۨCA$|;LgL+]<:6_.ϟ_d=;8:897^Ius^OMz.]7>}0>=v-pA[~Is 0P6jxF[gg) kfZD/@|d}'k-`uo3-"J단5Tx3!V1Q=3fY+) }G{ .ۈgF:0{4 ONON>~9;ɢ˛|'9ӿ}_nu}F8g&Z)Mբ _ͣ~գr6aa?_^ZɻQ$nufd#.4;.ʟܳ717 u%eYʰQ;bq'߻Ownz;TX"? ^]Sfi'y5P2e5v*zh_g? wUmmpwۭ6>9lsqH7 4#p^qׇ :f5栛D;"ibvRٳOyZ7[l~߳ݶ[tGwg?Z|pc1:e/՚E 7Ց:צapc?(FU|xYg 4.6FFe8܀$%;nH4 œ(i3=>?gKBP0Ƀ=zh)]_^ykp(%"6??}inwnqpH 3lI`[ʴPvI;Ā>sTkmS;JL]܈Ӧ@>F.ng|y_h.8lSkFnƱe[V5d(f7kw@&Jv\owe}QgHɤO/{;<9)k׹|1?=/oDow]7@ QTt բtuu];J:ŋ#›63j1k҈JQv79Uv(jC^)7..!&Gɱ x0#`hvFc3]Q-}! ޸~xr6 cڠqjiS"3+E.7cf>;-N]۱(3k$Bh՚حb+3j=6ij"%Y-;Q`رc% Ydmld#b$q,YeSE7ܡ꜓EG꡻F=u}EjT9g*{sbvU,9xό@4SyZp.Ht `Y4` Z1 Z1MP"y[ٍMS bY#( rSPd f_]U#TBt!F"6V޻b L Xj˧?O(I9/GhŨPPABmrEb$"ٱ/[ւ<*[eӾEtLSh2L #d1D '4t{-^'Ȫ6$(K, n'ϫ M6M817;VM];uӢr|Ǣh=;5U3)(;fFSQ j`HD,<hQ^I`PeN9ۭ0L ez9Cʐ:xvW{ zh4+\>X{mf|QUz{m!<3sEz/R5-.1 YD2a2BQL01R3sHDAI /hP2(y3EUWgSށ‘z![@P~-\\gv-wtT.PP;!T:GDZ6 XʲtjOg~;v_ Ky0R"M(+Cޗ,cDEIDfS9c2Ff ǨW~;Sl1Y jB7B?fo6{SQ ~'GK|uVw>i(gUhV#,ۛ ,_uwW2t+<0HDNqTU3xX,NV S|~{3\򃿽?7WhC0 ,#DZ#0 rH?~c]AIDռgQSV\E)<  K' }rRO189{ $uU=o3r9[,;YNrt6 'o̖`PW|H!"y1K)b,jaK2wptd(kEYHv<ژ*Ef\t\m ?x󶁉JN9޹޻i_HIn?d)=Ѣ){=h& h2 Ü~ڦˣrӶ`~uoޟ|q/ׯkinqkξY VC~qƔ=g EkR)gǮiR:S&4m]s~1b >zOd' )0a/sxЁOTlR>'?d)&|1h0 ~\i_~\|\VN?k' Yv#ӏ~<k:v/?;'Y٬qƓv KS!#]ΈxTʉIknr("4U,V<nmtrNM!{t0mCߟ۟?t{?|[o!kj?{f1 3ՔL(ɮBS޹P`{q:ȲƔݾR6얲3Q3ਐ+#=,fV DLgs]_(IQV Bv!8bvDo8?^>?~{oU / isA{i{-CgKra|>89{EUӘ* JҜS6"͐TUfɒ( rt$ڽnUmH"/`rH3?zDpWOᵇw^/ߓnyi_C xvMWU͎jb~գ_:łuXМD0/?~v  R)g&sN"&,2,"2☂DXwoU? False 6 dialog True False 6 True False end gtk-cancel True True True False True False False 0 gtk-ok True True True False True False False 1 False True 3 end 0 True False gtk-missing-image False True 3 1 True False label False True 3 2 True False True False label False True 0 True False True False <b>"</b> True False True 0 True True True False False True True True True 1 True False <b>"</b> True False True 2 True True 1 True True 3 3 ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-screensaver-lock-dialog/cinnamon-screensaver-lock-dialog.pyCinnamon-2.8.6/files/usr/share/cinnamon/cinnamon-screensaver-lock-dialog/cinnamon-screensaver-lock-d0000775000175000017500000000436112625666430032572 0ustar fabiofabio#!/usr/bin/env python2 import os import subprocess import gettext from gi.repository import Gtk import pwd # i18n gettext.install("cinnamon", "/usr/share/locale") class MainWindow: ''' Create the UI ''' def __init__(self): user_id = os.getuid() username = pwd.getpwuid(user_id).pw_name real_name = pwd.getpwuid(user_id).pw_gecos home_dir = pwd.getpwuid(user_id).pw_dir real_name = real_name.replace(",", "") if real_name == "": real_name = username self.builder = Gtk.Builder() self.builder.add_from_file("/usr/share/cinnamon/cinnamon-screensaver-lock-dialog/cinnamon-screensaver-lock-dialog.ui") self.window = self.builder.get_object("main_dialog") self.button_cancel = self.builder.get_object("button_cancel") self.button_ok = self.builder.get_object("button_ok") self.entry = self.builder.get_object("entry_away_message") self.image = self.builder.get_object("image_face") self.window.set_title(_("Screen Locker")) self.window.set_icon_name("cs-screensaver") self.builder.get_object("label_description").set_markup("%s" % _("Please type an away message for the lock screen")) self.builder.get_object("label_away_message").set_markup("%s: " % real_name) if os.path.exists("%s/.face" % home_dir): self.image.set_from_file("%s/.face" % home_dir) else: self.image.set_from_file("/usr/share/pixmaps/nobody.png") self.window.connect("destroy", Gtk.main_quit) self.button_cancel.connect("clicked", Gtk.main_quit) self.button_ok.connect('clicked', self.lock_screen) self.entry.connect('activate', self.lock_screen) self.builder.get_object("dialog-action_area1").set_focus_chain((self.button_ok, self.button_cancel)) self.window.show() def lock_screen(self, data): message = self.entry.get_text() if (message != ""): subprocess.call(["cinnamon-screensaver-command", "--lock", "--away-message", self.entry.get_text()]) else: subprocess.call(["cinnamon-screensaver-command", "--lock"]) Gtk.main_quit() if __name__ == "__main__": MainWindow() Gtk.main() Cinnamon-2.8.6/files/usr/share/xsessions/0000775000175000017500000000000012625666430017302 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/xsessions/cinnamon.desktop0000664000175000017500000000023312625666430022475 0ustar fabiofabio[Desktop Entry] Name=Cinnamon Comment=This session logs you into Cinnamon Exec=cinnamon-session-cinnamon TryExec=/usr/bin/cinnamon Icon= Type=Application Cinnamon-2.8.6/files/usr/share/xsessions/cinnamon2d.desktop0000664000175000017500000000041412625666430022724 0ustar fabiofabio[Desktop Entry] Name=Cinnamon (Software Rendering) Comment=This session logs you into Cinnamon (using software rendering) Exec=cinnamon-session-cinnamon2d TryExec=/usr/bin/cinnamon2d Icon= Type=Application X-Ubuntu-Gettext-Domain=cinnamon X-GNOME-Provides=cinnamon2d Cinnamon-2.8.6/files/usr/share/cinnamon-session/0000775000175000017500000000000012625666430020527 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon-session/sessions/0000775000175000017500000000000012625666430022375 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/cinnamon-session/sessions/cinnamon.session0000664000175000017500000000026512625666430025607 0ustar fabiofabio[Cinnamon Session] Name=Cinnamon RequiredComponents=cinnamon;cinnamon-settings-daemon;cinnamon-screensaver;nemo-autostart;nm-applet;cinnamon-killer-daemon; DesktopName=X-Cinnamon Cinnamon-2.8.6/files/usr/share/cinnamon-session/sessions/cinnamon2d.session0000664000175000017500000000031412625666430026030 0ustar fabiofabio[Cinnamon Session] Name=Cinnamon (Software Rendering) RequiredComponents=cinnamon2d;cinnamon-settings-daemon;cinnamon-screensaver;nemo-autostart;nm-applet;cinnamon-killer-daemon; DesktopName=X-Cinnamon Cinnamon-2.8.6/files/usr/share/polkit-1/0000775000175000017500000000000012625666430016704 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/polkit-1/actions/0000775000175000017500000000000012625666430020344 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/polkit-1/actions/org.cinnamon.settings-users.policy0000664000175000017500000000142712625666430027157 0ustar fabiofabio Authentication is required to run the Users and Groups system-users auth_admin auth_admin auth_admin /usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.py true Cinnamon-2.8.6/files/usr/share/desktop-directories/0000775000175000017500000000000012625666430021227 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-c.directory0000664000175000017500000000514012625666430025177 0ustar fabiofabio[Desktop Entry] Name=Control Center Name[af]=Beheersentrum Name[ar]=مركز التحكّم Name[as]=Control Center Name[ast]=Centru de control Name[be]=Цэнтар кіраваньня Name[be@latin]=Centar kiravańnia Name[bg]=Контролен център Name[bn]=নিয়ন্ত্রণ কেন্দ্র Name[bn_IN]=Control Center Name[ca]=Centre de control Name[ca@valencia]=Centre de control Name[crh]=Kontrol Merkezi Name[cs]=Ovládací centrum Name[da]=Kontrolcenter Name[de]=Kontrollzentrum Name[dz]=ཚད་འཛིན་ལྟེ་བ་ Name[el]=Κέντρο ελέγχου Name[en@shaw]=𐑒𐑩𐑯𐑑𐑮𐑴𐑤 𐑕𐑧𐑯𐑑𐑼 Name[en_GB]=Control Centre Name[es]=Centro de control Name[et]=Juhtimiskeskus Name[eu]=Kontrol-zentroa Name[fi]=Hallintakeskus Name[fr]=Centre de contrôle Name[ga]=Lárionad Rialaithe Name[gl]=Centro de control Name[gu]=નિયંત્રણ કેન્દ્ર Name[he]=לוח בקרה Name[hi]=नियंत्रण केंद्र Name[hu]=Vezérlőközpont Name[id]=Pusat Pengaturan Name[it]=Centro di controllo Name[ja]=コントロール・センター Name[ka]=მართვის ცენტრი Name[kn]=ನಿಯಂತ್ರಣ ಕೇಂದ್ರ Name[ko]=그놈 제어판 Name[lt]=Valdymo centras Name[lv]=Kontroles centrs Name[mai]=नियंत्रण केंद्र Name[mk]=Контролен центар Name[ml]=നിയന്ത്രണകേന്ദ്രം Name[mr]=नियंत्रण केंद्र Name[ms]=Pusat Kawalan Name[nb]=Kontrollsenter Name[nds]=Kontrollzentrum Name[ne]=नियन्त्रण केन्द्र Name[nl]=Configuratiecentrum Name[nn]=Kontrollsenter Name[or]=ନିୟନ୍ତ୍ରଣ କେନ୍ଦ୍ର Name[pa]=ਕੰਟਰੋਲ ਕੇਂਦਰ Name[pl]=Centrum sterowania Name[pt]=Centro de Controlo Name[pt_BR]=Centro de Controle Name[ro]=Centrul de control Name[ru]=Центр управления Name[sk]=Ovládacie centrum Name[sl]=Nadzorno središče Name[sq]=Qendra e kontrollit Name[sr]=Управљачки центар Name[sr@latin]=Upravljački centar Name[sv]=Kontrollpanel Name[ta]=கட்டுப்பாட்டு மையம் Name[te]=నియంత్రణా కేంద్రం Name[th]=ศูนย์ควบคุม Name[tr]=Kontrol Merkezi Name[ug]=باشقۇرۇش مەركىزى Name[uk]=Центр керування Name[uz]=Boshqaruv markazi Name[uz@cyrillic]=Бошқарув маркази Name[vi]=Trung tâm điều khiển Name[zh_CN]=控制中心 Name[zh_HK]=控制中心 Name[zh_TW]=控制中心 Icon=cinnamon-settings Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-utility.directory0000664000175000017500000001641312625666430026465 0ustar fabiofabio[Desktop Entry] Name=Accessories Name[af]=Toebehore Name[an]=Accesorios Name[ar]=ملحقات Name[as]=আনুষঙ্গিক Name[ast]=Accesorios Name[be]=Інструмэнты Name[be@latin]=Aksesuary Name[bg]=Помощни програми Name[bn]=আনুসাঙ্গিক Name[bn_IN]=আনুষঙ্গিক Name[br]=Mavegoù Name[ca]=Accessoris Name[ca@valencia]=Accessoris Name[crh]=Aksessuarlar Name[cs]=Příslušenství Name[cy]=Ategolion Name[da]=Tilbehør Name[de]=Zubehör Name[dv]=އެކްސެސަރީސް Name[dz]=ཡན་ལག་ཅ་ཆས། Name[el]=Βοηθήματα Name[en@shaw]=𐑨𐑒𐑕𐑧𐑕𐑼𐑦𐑟 Name[en_CA]=Accessories Name[en_GB]=Accessories Name[eo]=Utilaĵoj Name[es]=Accesorios Name[et]=Tarvikud Name[eu]=Gehigarriak Name[fa]=لوازم Name[fi]=Apuohjelmat Name[fr]=Accessoires Name[fur]=Robutis Name[fy]=Assesoires Name[ga]=Oiriúintí Name[gl]=Accesorios Name[gn]=Nhamba'eapoa Pygua Name[gu]=સહાયક કાર્યક્રમો Name[gv]=Claaghyn Name[ha]=Aksesori Name[he]=עזרים Name[hi]=उपसाधन Name[hr]=Pomagala Name[hu]=Kellékek Name[hy]=Աքսեսուարներ Name[id]=Aksesoris Name[ig]=Nnyemaka ngwaọrụ Name[io]=Acesori Name[is]=Aukahlutir Name[it]=Accessori Name[ja]=アクセサリ Name[ka]=აქსესუარები Name[kk]=Қалыпты Name[kn]=ಸಲಕರಣೆಗಳು Name[ko]=보조 프로그램 Name[ku]=Bernameyên Alîkar Name[ky]=Стандарттык иштемелер Name[lt]=Reikmenys Name[lv]=Piederumi Name[mai]=संलग्नक Name[mg]=Fiasana Name[mk]=Услужни Name[ml]=ഉപകരണങ്ങള്‍ Name[mn]=Нэмэлт төхөөрөмж Name[mr]=शिवाय उपकरणं Name[ms]=Aksesori Name[nb]=Tilbehør Name[nds]=Tobehör Name[ne]=सहायक उपकरण Name[nl]=Hulpmiddelen Name[nn]=Verktøy Name[oc]=Accessòris Name[or]=ସହାୟକ ବସ୍ତୁ Name[pa]=ਸਹਾਇਕ Name[pl]=Akcesoria Name[ps]=ملتوکي Name[pt]=Acessórios Name[pt_BR]=Acessórios Name[ro]=Accesorii Name[ru]=Стандартные Name[si]=උපාංග Name[sk]=Príslušenstvo Name[sl]=Pripomočki Name[sq]=Aksesorë Name[sr]=Алатке Name[sr@latin]=Alatke Name[sv]=Tillbehör Name[ta]=துணைப்பொருள்கள் Name[te]=సహాయకాలు Name[th]=เครื่องใช้ไม้สอย Name[tr]=Donatılar Name[ug]=قوشۇمچە Name[uk]=Стандартні Name[vi]=Bổ trợ Name[xh]=Izinto ezongezelelwayo Name[yo]=Àwọn Àsẹ́sírì Name[zh_CN]=附件 Name[zh_HK]=附屬應用程式 Name[zh_TW]=附屬應用程式 Comment=Desktop accessories Comment[af]=Werkskermtoebehore Comment[an]=Accesorios d'o escritorio Comment[ar]=ملحقات سطح المكتب Comment[as]=ডেস্কটপৰ ব্যৱহৃত আনুষঙ্গিক বস্তু Comment[ast]=Accesorios d'Escritoriu Comment[be]=Настольныя інструмэнты Comment[be@latin]=Aksesuary stała Comment[bg]=Набор от помощни програми Comment[bn]=ডেস্কটপের আনুসাঙ্গিক অ্যাপ্লিকেশন Comment[bn_IN]=ডেস্কটপের ব্যবহৃত আনুষঙ্গিক বস্তু Comment[br]=Mavegoù ar burev Comment[ca]=Accessoris de l'escriptori Comment[ca@valencia]=Accessoris de l'escriptori Comment[crh]=Masaüstü donantıları Comment[cs]=Příslušenství prostředí pracovní plochy Comment[cy]=Ategolion penbwrdd Comment[da]=Skrivebordstilbehør Comment[de]=Desktop-Zubehör Comment[dv]=ޑެސްކްޓޮޕް އެކްސެސަރީސް Comment[dz]=ཌེཀསི་ཊོཔ་གི་ ཡན་ལག་ཅ་ཆས། Comment[el]=Βοηθήματα επιφάνειας εργασίας Comment[en@shaw]=𐑛𐑧𐑕𐑒𐑑𐑪𐑐 𐑨𐑒𐑕𐑧𐑕𐑼𐑦𐑟 Comment[en_CA]=Desktop accessories Comment[en_GB]=Desktop accessories Comment[eo]=Labortablaj utilaĵoj Comment[es]=Accesorios del escritorio Comment[et]=Töölaua tarvikud Comment[eu]=Mahaigainaren gehigarriak Comment[fa]=لوازم رومیزی Comment[fi]=Työpöydän apuohjelmat Comment[fr]=Accessoires du bureau Comment[fur]=Robutis dal desktop Comment[fy]=Bureaublêd assesoires Comment[ga]=Oiriúintí deisce Comment[gl]=Accesorios do escritorio Comment[gn]=Nhamba'eapoa Regua Comment[gu]=ડેસ્કટોપ સહાયક કાર્યક્રમો Comment[ha]=Aksesori na Kwamfyutan Tebur Comment[he]=עזרי שולחן עבודה Comment[hi]=डेस्कटॉप उपसाधन Comment[hr]=Pomagala radne površine Comment[hu]=Asztali kellékek Comment[hy]=Աշխատասեղանի աքսեսուարներ Comment[id]=Aksesoris desktop Comment[ig]=Nnyemaka ngwaọrụ Desktọọpụ Comment[io]=Pupitrosuprala acesori Comment[is]=Aukahlutir fyrir skjáborð Comment[it]=Accessori del desktop Comment[ja]=デスクトップ用のアクセサリーです Comment[ka]=სამუშაო მაგიდის აქსესუარები Comment[kk]=Қалыпты қолданбалар Comment[kn]=ಗಣಕತೆರೆಯ ಸಲಕರಣೆಗಳು Comment[ko]=바탕환경의 보조 프로그램 Comment[ku]=Bernameyên alîkar ên sermaseyê Comment[ky]=Иш столунун иштемелери Comment[lt]=Darbo aplinkos reikmenys Comment[lv]=Darbvirsmas piederumi Comment[mai]=डेस्कटाप संलग्नकसभ Comment[mg]=Fiasan'ny desktop Comment[mk]=Алатки за работната површина Comment[ml]=പണിയിടോപകരണങ്ങള്‍ Comment[mn]=Дэлгэцийн нэмэлт төхөөрөмжүүд Comment[mr]=डेस्कटॉपशी निगडित शिवाय उपकरणं Comment[ms]=Aksesori Desktop Comment[nb]=Tilbehør for skrivebordet Comment[nds]=Schrievdisk Tobehör Comment[ne]=डेस्कटप सहायक उपकरण Comment[nl]=Bureaubladhulpmiddelen Comment[nn]=Verktøy for skrivebordet Comment[oc]=Accessòris del burèu Comment[or]=ଡେସ୍କଟପ୍ ସହାୟକ ବସ୍ତୁ Comment[pa]=ਡੈਸਕਟਾਪ ਸਹਾਇਕ Comment[pl]=Akcesoria pulpitu Comment[ps]=د سرپاڼې ملتوکي Comment[pt]=Acessórios de área de trabalho Comment[pt_BR]=Acessórios da área de trabalho Comment[ro]=Accesorii pentru desktop Comment[ru]=Стандартные приложения Comment[si]=මූලික තිරය උපාංග Comment[sk]=Príslušenstvo pracovnej plochy Comment[sl]=Namizni pripomočki Comment[sq]=Aksesorë hapësire pune Comment[sr]=Алатке окружења Comment[sr@latin]=Alatke okruženja Comment[sv]=Skrivbordstillbehör Comment[ta]=மேஜை துணைப்பொருள்கள் Comment[te]=రంగస్థల సహాయకాలు Comment[th]=โปรแกรมเครื่องใช้ไม้สอยบนเดสก์ท็อป Comment[tr]=Masaüstü donatıları Comment[ug]=ئۈستەل ئۈستى قوشۇمچە دېتاللىرى Comment[uk]=Стандартні програми Comment[vi]=Bổ trợ môi trường Comment[xh]=Izinto ezongezelelwayo ze-Desktop Comment[yo]=Àwọn àsẹ́sírì ojù-iṣẹ́ Comment[zh_CN]=桌面附件 Comment[zh_HK]=桌面附屬應用程式 Comment[zh_TW]=桌面附屬應用程式 Icon=applications-accessories Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-graphics.directory0000664000175000017500000001541012625666430026556 0ustar fabiofabio[Desktop Entry] Name=Graphics Name[af]=Grafika Name[an]=Graficos Name[ar]=رسوميات Name[as]=গ্ৰাফিক্স Name[ast]=Gráficos Name[be]=Графіка Name[be@latin]=Hrafika Name[bg]=Графика Name[bn]=গ্রাফিক্স Name[bn_IN]=গ্রাফিক্স Name[br]=Skeudennoù Name[ca]=Gràfics Name[ca@valencia]=Gràfics Name[crh]=Grafikler Name[cs]=Grafika Name[cy]=Graffeg Name[da]=Grafik Name[de]=Grafik Name[dv]=ގްރެފިކްސް Name[dz]=ཚད་རིས། Name[el]=Γραφικά Name[en@shaw]=𐑜𐑮𐑨𐑓𐑦𐑒𐑕 Name[en_CA]=Graphics Name[en_GB]=Graphics Name[eo]=Grafikoj Name[es]=Gráficos Name[et]=Graafika Name[eu]=Irudiak Name[fa]=گرافیک Name[fi]=Grafiikka Name[fr]=Graphisme Name[fur]=Grafiche Name[fy]=Grafysk Name[ga]=Grafaic Name[gl]=Gráficos Name[gn]=Baemo Ra'angaa Jajapoa Name[gu]=ગ્રાફિક્સ Name[gv]=Graafaghyn Name[ha]=Zane-Zane Name[he]=גרפיקה Name[hi]=चित्रादि Name[hr]=Grafika Name[hu]=Grafika Name[hy]=Գրաֆիկա Name[id]=Grafik Name[ig]=Nsepụtainyogo Name[io]=Grafikarti Name[is]=Myndefni Name[it]=Grafica Name[ja]=グラフィックス Name[ka]=გრაფიკა Name[kk]=Графика Name[kn]=ಗ್ರಾಫಿಕ್ಸ್ Name[ko]=그래픽 Name[ku]=Grafîk Name[ky]=Графика Name[lt]=Grafika Name[lv]=Grafika Name[mai]=आलेखी Name[mg]=Sary Name[mk]=Графика Name[ml]=ഗ്രാഫിക്സ് Name[mn]=График Name[mr]=चित्र-विज्ञान Name[ms]=Grafik Name[nb]=Grafikk Name[nds]=Grafik Name[ne]=ग्राफिक्स Name[nl]=Grafisch Name[nn]=Grafikk Name[oc]=Grafisme Name[or]=ଆଲେଖୀ Name[pa]=ਚਿੱਤਰਕਾਰੀ Name[pl]=Grafika Name[ps]=کښنيزونه Name[pt]=Gráficos Name[pt_BR]=Gráficos Name[ro]=Grafică Name[ru]=Графика Name[rw]=Ibishushanyo Name[si]=චිත්‍ර Name[sk]=Grafika Name[sl]=Grafika Name[sq]=Grafikë Name[sr]=Графика Name[sr@latin]=Grafika Name[sv]=Grafik Name[ta]=வரைகலை Name[te]=చిత్ర రూపాలు Name[th]=รูปภาพ Name[tr]=Grafik Name[ug]=گرافىكلار Name[uk]=Графіка Name[uz@cyrillic]=Графика Name[vi]=Đồ họa Name[xh]=Iimo zezibonakalisi-nkqubo Name[yo]=Gíráfíìsì Name[zh_CN]=图形 Name[zh_HK]=美工繪圖 Name[zh_TW]=美工繪圖 Comment=Graphics applications Comment[af]=Grafika-toepassings Comment[an]=Aplicacions graficas Comment[ar]=تطبيقات الرّسوميات Comment[as]=চাত্ৰাঙ্কিত অনুপ্ৰয়োগ Comment[ast]=Aplicaciones gráfiques Comment[be]=Графічныя дастасаваньні Comment[be@latin]=Hrafičnyja aplikacyi Comment[bg]=Програми за работа с графика Comment[bn]=গ্রাফিক্স অ্যাপ্লিকেশন Comment[bn_IN]=গ্রাফিক্স অ্যাপ্লিকেশন Comment[br]=Arloadoù skeudennaouiñ Comment[ca]=Aplicacions de gràfics Comment[ca@valencia]=Aplicacions de gràfics Comment[crh]=Grafikiy uyğulamalar Comment[cs]=Aplikace pro grafiku Comment[cy]=Rhaglennu graffeg Comment[da]=Grafikprogrammer Comment[de]=Anwendungen zur Grafikbearbeitung Comment[dv]=ގްރެފިކްސް އެޕްލިކޭޝަންތައް Comment[dz]=ཚད་རིས་ཀྱི་གློག་རིམ། Comment[el]=Εφαρμογές γραφικών Comment[en@shaw]=𐑜𐑮𐑨𐑓𐑦𐑒𐑕 𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟 Comment[en_CA]=Graphics applications Comment[en_GB]=Graphics applications Comment[eo]=Grafikaj aplikaĵoj Comment[es]=Aplicaciones gráficas Comment[et]=Graafikatöötlusprogrammid Comment[eu]=Irudi aplikazioak Comment[fa]=برنامه‌های گرافیکی Comment[fi]=Grafiikkasovellukset Comment[fr]=Applications graphiques Comment[fur]=Aplicazions di grafiche Comment[fy]=Grafyske programma's Comment[ga]=Feidhmchláir grafaice Comment[gl]=Aplicativos gráficos Comment[gn]=Baemo Ra'angaa Jajapoa Oia Comment[gu]=ગ્રાફિક્સ કાર્યક્રમો Comment[gv]=Claaghyn graafagh Comment[ha]=Shiryoyin ayukan zane-zane Comment[he]=יישומים גרפיים Comment[hi]=चित्रादि अनुप्रयोग Comment[hr]=Grafički programi Comment[hu]=Grafikai alkalmazások Comment[hy]=Գրաֆիկական ծրագրեր Comment[id]=Aplikasi grafik Comment[ig]=Usoro iheomume nsepụtainyogo Comment[io]=Programi pri grafikarti Comment[is]=Myndefnisforrit Comment[it]=Applicazioni grafiche Comment[ja]=グラフィックス関連のアプリケーションです Comment[ka]=გრაფიკული პროგრამები Comment[kk]=Графикалық қолданбалар Comment[kn]=ಗ್ರಾಫಿಕ್ಸ್ ಅನ್ವಯಗಳು Comment[ko]=그래픽 응용프로그램 Comment[ku]=Sepanên grafîkê Comment[ky]=Графикалык иштемелер Comment[lt]=Grafinės programos Comment[lv]=Grafikas programmas Comment[mai]=आलेखी अनुप्रयोगसभ Comment[mg]=Rindran'asa fanaovan-tsary Comment[mk]=Графички апликации Comment[ml]=ഗ്രാഫിക്സ് പ്രയോഗങ്ങള്‍ Comment[mn]=График програмууд Comment[mr]=चित्र-विज्ञान उपकरणं Comment[ms]=Aplikasi Grafik Comment[nb]=Grafiske programmer Comment[nds]=Grafikprogramme Comment[ne]=ग्राफिक्स अनुप्रयोग Comment[nl]=Grafische toepassingen Comment[nn]=Grafikkprogram Comment[oc]=Logicials grafics Comment[or]=ଆଲେଖୀ ପ୍ରୟୋଗ Comment[pa]=ਚਿੱਤਰਕਾਰੀ ਕਾਰਜ Comment[pl]=Programy graficzne Comment[ps]=کښنيزونو کاريالونه Comment[pt]=Aplicações gráficas Comment[pt_BR]=Aplicativos gráficos Comment[ro]=Aplicații pentru grafică Comment[ru]=Графические приложения Comment[si]=චිත්‍රක යෙදුම් Comment[sk]=Grafické aplikácie Comment[sl]=Grafični programi Comment[sq]=Programe grafiku Comment[sr]=Графички програми Comment[sr@latin]=Grafički programi Comment[sv]=Grafikprogram Comment[ta]=வரைகலை பயன்பாடுகள் Comment[te]=చిత్రరూప కార్యక్షేత్రాలు Comment[th]=โปรแกรมสำหรับรูปภาพ Comment[tr]=Grafik uygulamaları Comment[ug]=گرافىك قوللىنىشچان پروگراممىلىرى Comment[uk]=Графічні програми Comment[uz@cyrillic]=Графика дастурлари Comment[vi]=Ứng dụng đồ họa Comment[xh]=Iinkqubo ngeemo zezibonakalisi-nkqubo Comment[yo]=Àwọn ìṣàmúlò-ètò gíráfíìsì Comment[zh_CN]=图形应用程序 Comment[zh_HK]=繪圖工具 Comment[zh_TW]=繪圖工具 Icon=applications-graphics Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-menu-applications.directory0000664000175000017500000001307612625666430030414 0ustar fabiofabio[Desktop Entry] Name=Applications Name[af]=Toepassings Name[an]=Aplicacions Name[ar]=تطبيقات Name[as]=অনুপ্ৰয়োগ Name[ast]=Aplicaciones Name[be]=Дастасаваньні Name[be@latin]=Aplikacyi Name[bg]=Програми Name[bn]=অ্যাপলিকেশন Name[bn_IN]=অ্যাপলিকেশন Name[br]=Arloadoù Name[ca]=Aplicacions Name[ca@valencia]=Aplicacions Name[crh]=Uyğulamalar Name[cs]=Aplikace Name[cy]=Rhaglenni Name[da]=Programmer Name[de]=Anwendungen Name[dv]=އެޕްލިކޭޝަންސް Name[dz]=གློག་རིམ། Name[el]=Εφαρμογές Name[en@shaw]=𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟 Name[en_CA]=Applications Name[en_GB]=Applications Name[eo]=Aplikaĵoj Name[es]=Aplicaciones Name[et]=Rakendused Name[eu]=Aplikazioak Name[fa]=برنامه‌ها Name[fi]=Sovellukset Name[fr]=Applications Name[fur]=Aplicazions Name[fy]=Programma's Name[ga]=Feidhmchláir Name[gl]=Aplicativos Name[gn]=Enhepynru Apy Name[gu]=કાર્યક્રમો Name[gv]=Claaghyn Name[ha]=Shiryoyin Ayuka Name[he]=יישומים Name[hi]=अनुप्रयोग Name[hr]=Programi Name[hu]=Alkalmazások Name[hy]=Ծրագրեր Name[id]=Aplikasi Name[ig]=Usoro ihe omume Name[io]=Programi Name[is]=Forrit Name[it]=Applicazioni Name[ja]=アプリケーション Name[ka]=პროგრამები Name[kk]=Қолданбалар Name[kn]=ಅನ್ವಯಗಳು Name[ko]=프로그램 Name[ku]=Sepan Name[ky]=Иштемелер Name[lt]=Programos Name[lv]=Lietotnes Name[mai]=अनुप्रयोग Name[mg]=Rindran'asa Name[mk]=Апликации Name[ml]=പ്രയോഗങ്ങള്‍ Name[mn]=Програмууд Name[mr]=उपकरणं Name[ms]=Aplikasi Name[nb]=Programmer Name[nds]=Programme Name[ne]=अनुप्रयोग Name[nl]=Toepassingen Name[nn]=Program Name[oc]=Aplicacions Name[or]=ପ୍ରୟୋଗ Name[pa]=ਐਪਲੀਕੇਸ਼ਨ Name[pl]=Programy Name[ps]=کاريالونه Name[pt]=Aplicações Name[pt_BR]=Aplicativos Name[ro]=Aplicații Name[ru]=Приложения Name[rw]=Porogaramu Name[si]=යෙදුම් Name[sk]=Aplikácie Name[sl]=Programi Name[sq]=Programe Name[sr]=Програми Name[sr@latin]=Programi Name[sv]=Program Name[ta]=பயன்பாடுகள் Name[te]=కార్యక్షేత్రాలు Name[th]=โปรแกรม Name[tr]=Uygulamalar Name[ug]=قوللىنىشچان پروگراممىلار Name[uk]=Програми Name[uz@cyrillic]=Дастурлар Name[vi]=Ứng dụng Name[xh]=Iinkqubo Name[yo]=Àwọn Ìṣàmúlò-ètò Name[zh_CN]=应用程序 Name[zh_HK]=應用程式 Name[zh_TW]=應用程式 Comment=Applications Comment[af]=Toepassings Comment[an]=Aplicacions Comment[ar]=تطبيقات Comment[as]=অনুপ্ৰয়োগ Comment[ast]=Aplicaciones Comment[be]=Дастасаваньні Comment[be@latin]=Aplikacyi Comment[bg]=Програми Comment[bn]=অ্যাপলিকেশন Comment[bn_IN]=অ্যাপলিকেশন Comment[br]=Arloadoù Comment[ca]=Aplicacions Comment[ca@valencia]=Aplicacions Comment[crh]=Uyğulamalar Comment[cs]=Aplikace Comment[cy]=Rhaglenni Comment[da]=Programmer Comment[de]=Anwendungen Comment[dv]=އެޕްލިކޭޝަންސް Comment[dz]=གློག་རིམ། Comment[el]=Εφαρμογές Comment[en@shaw]=𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟 Comment[en_CA]=Applications Comment[en_GB]=Applications Comment[eo]=Aplikaĵoj Comment[es]=Aplicaciones Comment[et]=Rakendused Comment[eu]=Aplikazioak Comment[fa]=برنامه‌ها Comment[fi]=Sovellukset Comment[fr]=Applications Comment[fur]=Aplicazions Comment[fy]=Programma's Comment[ga]=Feidhmchláir Comment[gl]=Aplicativos Comment[gn]=Enhepynru Apy Comment[gu]=કાર્યક્રમો Comment[gv]=Claaghyn Comment[ha]=Shiryoyin Ayuka Comment[he]=יישומים Comment[hi]=अनुप्रयोग Comment[hr]=Programi Comment[hu]=Alkalmazások Comment[hy]=Ծրագրեր Comment[id]=Aplikasi Comment[ig]=Usoro ihe omume Comment[io]=Programi Comment[is]=Forrit Comment[it]=Applicazioni Comment[ja]=アプリケーション Comment[ka]=პროგრამები Comment[kk]=Қолданбалар Comment[kn]=ಅನ್ವಯಗಳು Comment[ko]=프로그램 Comment[ku]=Sepan Comment[ky]=Иштемелер Comment[lt]=Programos Comment[lv]=Lietotnes Comment[mai]=अनुप्रयोग Comment[mg]=Rindran'asa Comment[mk]=Апликации Comment[ml]=പ്രയോഗങ്ങള്‍ Comment[mn]=Програмууд Comment[mr]=उपकरणं Comment[ms]=Aplikasi Comment[nb]=Programmer Comment[nds]=Programme Comment[ne]=अनुप्रयोग Comment[nl]=Toepassingen Comment[nn]=Program Comment[oc]=Aplicacions Comment[or]=ପ୍ରୟୋଗ Comment[pa]=ਐਪਲੀਕੇਸ਼ਨ Comment[pl]=Programy Comment[ps]=کاريالونه Comment[pt]=Aplicações Comment[pt_BR]=Aplicativos Comment[ro]=Aplicații Comment[ru]=Приложения Comment[rw]=Porogaramu Comment[si]=යෙදුම් Comment[sk]=Aplikácie Comment[sl]=Programi Comment[sq]=Programe Comment[sr]=Програми Comment[sr@latin]=Programi Comment[sv]=Program Comment[ta]=பயன்பாடுகள் Comment[te]=కార్యక్షేత్రాలు Comment[th]=โปรแกรม Comment[tr]=Uygulamalar Comment[ug]=قوللىنىشچان پروگراممىلار Comment[uk]=Програми Comment[uz@cyrillic]=Дастурлар Comment[vi]=Ứng dụng Comment[xh]=Iinkqubo Comment[yo]=Àwọn Ìṣàmúlò-ètò Comment[zh_CN]=应用程序 Comment[zh_HK]=應用程式 Comment[zh_TW]=應用程式 Icon=applications-other Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-look-and-feel.directory0000664000175000017500000002636012625666430027401 0ustar fabiofabio[Desktop Entry] Name=Look and Feel Name[af]=Voorkoms en gedrag Name[an]=Visualizacion y comportamiento Name[ar]=المظهر Name[as]=চেহেৰা ছবি Name[ast]=Aldéu Name[be]=Вонкавы выгляд Name[be@latin]=Vyhlad Name[bg]=Външен изглед Name[bn]=দেখুন ও উপভোগ করুন Name[bn_IN]=চেহারা ছবি Name[br]=Neuz ha Feson Name[ca]=Aspecte i comportament Name[ca@valencia]=Aspecte i comportament Name[crh]=Körüniş ve Seziliş Name[cs]=Vzhled a chování Name[da]=Udseende og opførsel Name[de]=Darstellung Name[dv]=ބަލާބެލުން Name[dz]=བལྟ་ནི་དང་འཚོར་སྣང་ Name[el]=Ρυθμίσεις εμφάνισης Name[en@shaw]=𐑤𐑫𐑒 𐑯 𐑓𐑰𐑤 Name[en_GB]=Look and Feel Name[eo]=Aspekto kaj konduto Name[es]=Visualización y comportamiento Name[et]=Välimus ja tunnetus Name[eu]=Itxura eta izaera Name[fa]=شکل و رفتار Name[fi]=Ulkoasu ja käyttötuntuma Name[fr]=Apparence Name[fur]=Aspiet e stîl Name[fy]=Uterlik Name[ga]=Cuma agus Mothú Name[gl]=Aparencia Name[gu]=દેખાવ અને વર્તણૂક Name[gv]=Jeaghyn as Name[he]=מראה ומרגש Name[hi]=रूप-रंग Name[hr]=Izgled Name[hu]=Megjelenés Name[hy]=Տեսք Name[id]=Tampilan dan Rasa Name[io]=Aspekto e Sento Name[is]=Útlit og viðmót Name[it]=Aspetto e stile Name[ja]=ルック&フィール Name[kk]=Сыртқы түрі Name[kn]=ನೋಟ ಹಾಗು ಮಾಟ Name[ko]=모양새 Name[ku]=Dîtin û Hestkirin Name[lt]=Išvaizda ir elgsena Name[lv]=Izskats un uzvedība Name[mai]=रूप-रंग Name[mk]=Изглед Name[ml]=ചമയം Name[mn]=Харагдах байдал Name[mr]=दृश्य व प्रभाव Name[nb]=Utseende og oppførsel Name[nds]=Utsehn Name[ne]=हेर्नुहोस् अनुभव गर्नुहोस् Name[nl]=Uiterlijk en gedrag Name[nn]=Utsjåande Name[or]=ଦେଖନ୍ତୁ ଏବଂ ଅନୁଭବ କରନ୍ତୁ Name[pa]=ਦਿੱਖ ਅਤੇ ਵਰਤੋਂ Name[pl]=Wygląd Name[ps]=ښکارېدنه Name[pt]=Aparência e Comportamento Name[pt_BR]=Visual e comportamento Name[ro]=Aspect și comportament Name[ru]=Оформление Name[si]=පෙනුම සහ දැනිම Name[sk]=Vzhľad a dojem Name[sl]=Videz in občutek Name[sq]=Pamje dhe Ndjesi Name[sr]=Изглед и понашање Name[sr@latin]=Izgled i ponašanje Name[sv]=Utseende och beteende Name[ta]=பார்வையும் உணர்வும் Name[te]=రూపు రేఖలు Name[th]=รูปลักษณ์และการใช้งาน Name[tr]=Görünüş ve İşleyiş Name[ug]=كۆرۈنۈش تۇيغۇسى Name[uk]=Оформлення Name[uz@cyrillic]=Ташқи кўриниши Name[vi]=Sắc thái Name[zh_CN]=外观 Name[zh_HK]=外觀與感覺 Name[zh_TW]=外觀與感覺 Comment=Settings controlling the desktop appearance and behavior Comment[af]=Instellings wat die werkskerm se voorkoms en gedrag beheer Comment[an]=Propiedaz que controlan l'apariencia y o comportamiento d'o escritorio Comment[ar]=إعدادات تتحكّم في مظهر وسلوك سطح المكتب Comment[as]=ডেস্কটপৰ চেহেৰা ও আচৰণ নিয়ন্ত্ৰণকাৰী বৈশিষ্ট্য Comment[ast]=Propiedaes que remanen l'aldéu y el comportamientu del escritoriu Comment[be]=Настаўленьні, якія кантралююць вонавы выгляд і паводзіны асяродзьдзя Comment[be@latin]=Nałady vyhladu j pavodzinaŭ Comment[bg]=Настройки за външния изглед и поведение на работния плот Comment[bn]=ডেক্সটপের অবয়ব ও আচরণ নিয়ন্ত্রণের সেটিংস Comment[bn_IN]=ডেস্কটপের চেহারা ও আচরণ নিয়ন্ত্রণকারী বৈশিষ্ট্য Comment[br]=Arventennoù a reol neuz ar burev hag e emzalc'h Comment[ca]=Paràmetres que controlen l'aspecte i el comportament de l'escriptori Comment[ca@valencia]=Paràmetres que controlen l'aspecte i el comportament de l'escriptori Comment[crh]=Masaüstü körüniş ve sezilişini muraqabe etken tesbitler Comment[cs]=Nastavení ovládající vzhled a chování pracovního prostředí Comment[da]=Indstillinger for kontrol af skrivebordets udseende og opførelse Comment[de]=Einstellungen für das Aussehen und das Verhalten der Arbeitsoberfläche Comment[dv]=ޑެސްކްޓޮޕް ބަލާބެލުމާއި ހަރަކާތްތައް ކޮންޓްރޯލްކުރާ ސެޓިންގްސް Comment[dz]=ཌེཀསི་ཊོཔ་གི་འབྱུང་སྣང་དང་སྤཡོད་ལམ་འདི་ཚད་འཛིན་འབད་མི་སྒྲིག་སྟངས་ Comment[el]=Ρυθμίσεις για την εμφάνιση και συμπεριφορά της επιφάνειας εργασίας Comment[en@shaw]=𐑕𐑧𐑑𐑦𐑙𐑟 𐑒𐑩𐑯𐑑𐑮𐑴𐑤𐑦𐑙 𐑞 𐑛𐑧𐑕𐑒𐑑𐑪𐑐 𐑩𐑐𐑽𐑩𐑯𐑕 𐑯 𐑚𐑦𐑣𐑱𐑝𐑘𐑼 Comment[en_GB]=Settings controlling the desktop appearance and behaviour Comment[eo]=Agordaro reganta aspekton kaj konduton de la labortablo Comment[es]=Propiedades que controlan la apariencia y el comportamiento del escritorio Comment[et]=Töölaua välimuse ja käitumise juhtimine Comment[eu]=Mahaigainaren itxura eta portaera kontrolatzeko ezarpenak Comment[fa]=تنظیماتی که ظاهر و رفتار رومیزی را کنترل می‌کنند Comment[fi]=Työpöytäympäristön ulkoasua ja toimintaa ohjaavat asetukset Comment[fr]=Paramètres contrôlant l'apparence et le comportement du bureau Comment[fur]=Impostazions ch'a controlin l'aspiet e il compuartament dal desktop Comment[fy]=Ynstellingen foar it gedrach en it uterlik fan it bureaublêd Comment[ga]=Socruithe ag rialú cuma agus oibrithe na deisce Comment[gl]=Axustes que controlan a aparencia e o comportamento do escritorio Comment[gu]=ડેસ્કટોપ દેખાવ અને વર્તણૂક સંભાળી રહેલ સુયોજનો Comment[gv]=Reighyn mychoine yn aght ta'n Desktop jeaghyn as yn ymmyrkey. Comment[he]=הגדרות לשליטה במראה והתנהגות שולחן העבודה Comment[hi]=डेस्कटॉप रूपाकार व व्यवहार का नियंत्रण करने वाले सेटिंग Comment[hr]=Postavke za upravljanje izgledom i ponašanjem desktopa Comment[hu]=Az asztal megjelenését és viselkedését vezérlő beállítások Comment[hy]=Աշխատասեղանի տեսքի և վարքի հատկություններ Comment[id]=Mengelola tampilan dan perilaku desktop Comment[is]=Stillingar sem stjórna útliti og hegðun skjáborðsins Comment[it]=Impostazioni che controllano l'aspetto e il comportamento del desktop Comment[ja]=デスクトップの外観や動きを調整します Comment[kk]=Жұмыс үстелін көркейтетін баптаулар Comment[kn]=ಗಣಕತೆರೆಯ ಸ್ವರೂಪ ಹಾಗು ವರ್ತನೆಯನ್ನು ನಿಯಂತ್ರಿಸುವ ಸಿದ್ಧತೆಗಳು Comment[ko]=바탕환경의 모양새와 행동을 설정합니다 Comment[ku]=Mîhengên xuyabûn û tevgerînên sermasê Comment[lt]=Darbo aplinkos išvaizdos ir elgsenos nustatymai Comment[lv]=Iestatījumi, kas nosaka darbvirsmas izskatu un uzvedību Comment[mai]=डेस्कटाप रूपाकार आ व्यवहारक नियंत्रण करैबला सेटिंग Comment[mk]=Поставувања кои го контролираат изгледот на работната површина и однесувањето Comment[ml]=പണിയിടത്തിന്റെ കാഴ്ചയും പെരുമാറ്റവും നിയന്ത്രിയ്ക്കുന്ന സജ്ജീകരണങ്ങള്‍ Comment[mn]=Ажлын талбарын харагдалт ба харьцааны тохиргоо Comment[mr]=डेस्कटॉप दृश्य व वागणूक नियंत्रीत करणारी संयोजना Comment[nb]=Innstillinger som kontrollerer utseende og oppførsel for skrivebordet Comment[nds]=Instellensstüern vun Schrievdiskverhollen un -utsehn Comment[ne]=डेस्कटप देखावट र व्यवहार नियन्त्रण गर्ने सेटिङ Comment[nl]=Instellingen voor het uiterlijk en gedrag van de werkomgeving Comment[nn]=Innstillingar som kontrollerer utsjånaden og åtferda til skrivebordet Comment[or]=ଡେସ୍କଟପ ପରିଦୃଶ୍ୟ ଏବଂ ଆଚରଣକୁ ନିୟନ୍ତ୍ରଣ କରୁଥିବା ବିନ୍ୟାସ Comment[pa]=ਡੈਸਕਟਾਪ ਦਿੱਖ ਅਤੇ ਰਵੱਈਆ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਸਥਾਪਨ Comment[pl]=Ustawienia wyglądu i działania środowiska Comment[ps]=د سرپاڼې د ښکارېدو او کړه وړې امستنې Comment[pt]=Definições que controlam a aparência e comportamento do ambiente de trabalho Comment[pt_BR]=Configurações que controlam o visual e o comportamento da área de trabalho Comment[ro]=Opțiuni privind aspectul și comportamentul desktopului Comment[ru]=Параметры, влияющие на внешний вид и поведение рабочего стола Comment[si]=මුලිකතිතයෙ පෙනුම සහ ක්‍රියාකාරිත්වය පාලනය කරන්නාවු සැකසුම්් Comment[sk]=Nastavenia ovládajúce vzhľad a správanie pracovnej plochy Comment[sl]=Nastavitve videza in obnašanja namizja Comment[sq]=Rregullime që kontrollojnë pamjen e hapësirës së punës dhe sjelljen Comment[sr]=Подешавање изгледа и понашања радног окружења Comment[sr@latin]=Podešavanje izgleda i ponašanja radnog okruženja Comment[sv]=Inställningar som styr skrivbordets utseende och beteende Comment[ta]=மேல்மேசையின் தோற்றம் மற்றும் நடத்தைகளை கட்டுப்படுத்தும் வடிவமைப்பு Comment[te]=రంగస్థల రూపం మరియు ప్రవర్తనను నియంత్రణ చేయు అమరికలు Comment[th]=ตั้งค่าควบคุมรูปโฉมและพฤติกรรมของเดสก์ท็อป Comment[tr]=Masaüstü görünüm ve işleyişini kontrol eden ayarlar Comment[ug]=ئۈستەل ئۈستى ئۇسلۇبى ۋە ھەرىكىتىنى كونترول قىلىش تەڭشىكى Comment[uk]=Параметри, що впливають на зовнішній вигляд та поведінку робочого столу Comment[uz@cyrillic]=Иш столининг ташқи кўриниши ва хусусиятларига оид мосламалар Comment[vi]=Thiết lập điều khiển diện mạo và cách ứng xử của hệ thống Comment[zh_CN]=控制桌面外观和行为的设置 Comment[zh_HK]=控制桌面顯示方式及行為的設定 Comment[zh_TW]=控制桌面顯示方式及行為的設定 Icon=preferences-desktop Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-education.directory0000664000175000017500000000460712625666430026737 0ustar fabiofabio[Desktop Entry] Name=Education Name[af]=Opvoeding Name[an]=Educacion Name[ar]=تعليمي Name[as]=শিক্ষাবিষয়ক বস্তু Name[ast]=Educación Name[be]=Адукацыя Name[be@latin]=Adukacyja Name[bg]=Обучение Name[bn]=শিক্ষা Name[bn_IN]=শিক্ষাবিষয়ক বস্তু Name[br]=Deskadurezh Name[ca]=Educació Name[ca@valencia]=Educació Name[crh]=Maarif Name[cs]=Vzdělávání Name[cy]=Addysg Name[da]=Læringsprogrammer Name[de]=Bildung Name[dv]=ތައުލީމު Name[dz]=ཤེས་རིག Name[el]=Εκπαίδευση Name[en@shaw]=𐑧𐑛𐑿𐑒𐑱𐑖𐑩𐑯 Name[en_CA]=Education Name[en_GB]=Education Name[eo]=Edukado Name[es]=Educación Name[et]=Õppeprogrammid Name[eu]=Hezkuntza Name[fa]=آموزش Name[fi]=Opetusohjelmat Name[fr]=Éducation Name[fur]=Istruzion Name[fy]=Edukaasje Name[ga]=Oideachas Name[gl]=Educación Name[gn]=Arandu Name[gu]=શિક્ષણ Name[gv]=Ynsagh Name[ha]=Ilimi Name[he]=לימוד Name[hi]=शिक्षा Name[hr]=Edukacija Name[hu]=Oktatás Name[hy]=Ուսուցում Name[id]=Pendidikan Name[ig]=Ọmụmụ Name[io]=Edukado Name[is]=Menntun Name[it]=Istruzione Name[ja]=教育・教養 Name[ka]=განათლება Name[kk]=Оқыту Name[kn]=ಶಿಕ್ಷಣ Name[ko]=교육 Name[ku]=Perwerdehî Name[ky]=Билим берүү иштемелери Name[lt]=Švietimas Name[lv]=Izglītība Name[mai]=शिक्षा Name[mg]=Fanabeazana Name[mk]=Едукација Name[ml]=വിദ്യാഭ്യാസം Name[mn]=Боловсрол Name[mr]=शिक्षण Name[nb]=Opplæring Name[nds]=Erteken Name[ne]=शिक्षा Name[nl]=Educatie Name[nn]=Utdanning Name[oc]=Educacion Name[or]=ଶିକ୍ଷା Name[pa]=ਸਿੱਖਿਆ Name[pl]=Nauka Name[ps]=زدکړه Name[pt]=Educação Name[pt_BR]=Educativo Name[ro]=Educație Name[ru]=Образовательные Name[si]=අධ්‍යාපනිකපනික Name[sk]=Vzdelávanie Name[sl]=Izobraževanje Name[sq]=Edukim Name[sr]=Образовање Name[sr@latin]=Obrazovanje Name[sv]=Utbildning Name[ta]=கல்வி Name[te]=విద్య Name[th]=การศึกษา Name[tr]=Eğitim Name[ug]=مائارىپ Name[uk]=Навчання Name[uz@cyrillic]=Таълим Name[vi]=Giáo dục Name[yo]=Ẹ̀kọ́ Name[zh_CN]=教育 Name[zh_HK]=教育 Name[zh_TW]=教育 Icon=applications-science Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-hardware.directory0000664000175000017500000002017312625666430026555 0ustar fabiofabio[Desktop Entry] Name=Hardware Name[af]=Hardeware Name[an]=Hardware Name[ar]=عتاد Name[as]=যান্ত্ৰিক সামগ্ৰী Name[ast]=Hardware Name[be]=Апаратура Name[be@latin]=Aparatura Name[bg]=Хардуер Name[bn]=হার্ডওয়্যার Name[bn_IN]=হার্ডওয়্যার Name[br]=Periant Name[ca]=Maquinari Name[ca@valencia]=Maquinari Name[crh]=Donanım Name[cs]=Hardware Name[da]=Hardware Name[de]=Hardware Name[dv]=ހާރޑްވެއަރ Name[dz]=སྲ་ཆས་ Name[el]=Υλικό Name[en@shaw]=𐑣𐑭𐑮𐑛𐑢𐑧𐑮 Name[en_GB]=Hardware Name[eo]=Aparataro Name[es]=Hardware Name[et]=Riistvara Name[eu]=Hardwarea Name[fa]=سخت‌افزار Name[fi]=Laitteisto Name[fr]=Matériel Name[fur]=Hardware Name[fy]=Hardware Name[ga]=Crua-Earraí Name[gl]=Hardware Name[gu]=હાર્ડવેર Name[gv]=Yiarnrey Name[he]=חומרה Name[hi]=हार्डवेयर Name[hr]=Hardver Name[hu]=Hardver Name[hy]=Երկաթ Name[id]=Perangkat Keras Name[io]=Metalajaro Name[is]=Vélbúnaður Name[it]=Hardware Name[ja]=ハードウェア Name[kk]=Құрылғылар Name[kn]=ಯಂತ್ರಾಂಶ Name[ko]=하드웨어 Name[ku]=Hişkalav Name[lt]=Aparatinė įranga Name[lv]=Aparatūra Name[mai]=हार्डवेयर Name[mk]=Хардвер Name[ml]=ഹാര്‍ഡ്‌വെയര്‍ Name[mn]=Техник хангамж Name[mr]=हार्डवेअर Name[nb]=Maskinvare Name[nds]=Hardware Name[ne]=हार्डवेयर Name[nl]=Hardware Name[nn]=Maskinvare Name[oc]=Material Name[or]=ହାର୍ଡୱେର Name[pa]=ਜੰਤਰ Name[pl]=Sprzęt Name[ps]=هډوتري Name[pt]=Equipamento Name[pt_BR]=Hardware Name[ro]=Hardware Name[ru]=Оборудование Name[si]=දෘඩාංග Name[sk]=Hardvér Name[sl]=Strojna oprema Name[sq]=Hardware Name[sr]=Уређаји Name[sr@latin]=Uređaji Name[sv]=Hårdvara Name[ta]=வண் பொருட்கள் Name[te]=హార్డ వేర్ Name[th]=ฮาร์ดแวร์ Name[tr]=Donanım Name[ug]=قاتتىق دېتال Name[uk]=Обладнання Name[uz@cyrillic]=Асбоб-ускуналар Name[vi]=Phần cứng Name[zh_CN]=硬件 Name[zh_HK]=硬件 Name[zh_TW]=硬體 Comment=Settings for several hardware devices Comment[af]=Instellings vir verskeie hardewaretoestelle Comment[an]=Propiedaz ta varios dispositivos de hardware Comment[ar]=إعدادات للعديد من أجهزة العتاد Comment[as]=বিবিধ যন্ত্ৰৰ সামগ্ৰীৰ সংক্ৰান্ত বৈশিষ্ট্য Comment[ast]=Opciones pa dellos preseos de hardware Comment[be]=Настаўленьні для апаратуры Comment[be@latin]=Niekatoryja aparaturnyja nałady Comment[bg]=Настройки за много хардуерни устройства Comment[bn]=কয়েকটি হার্ডওয়্যার ডিভাইসের সেটিংস Comment[bn_IN]=বিবিধ হার্ডওয়্যার ডিভাইস সংক্রান্ত বৈশিষ্ট্য Comment[br]=Arventennoù evit meur a drobarzhell Comment[ca]=Paràmetres per a diferents dispositius de maquinari Comment[ca@valencia]=Paràmetres per a diferents dispositius de maquinari Comment[crh]=Bir qaç donanım cihazı içün tesbitler Comment[cs]=Nastavení různých hardwarových zařízení Comment[da]=Indstillinger for flere hardware-enheder Comment[de]=Einstellungen für Hardware Comment[dv]=ހާރޑުވެއަރ ޑިވައިސްތަކުގެ ސެޓިންގްތައް Comment[dz]=སྲ་ཆས་ཀྱི་ཐབས་འཕྲུལ་ལེ་ཤ་ཅིག་གི་དོན་ལུ་སྒྲིག་སྟངས་ Comment[el]=Ρυθμίσεις για διάφορες συσκευές Comment[en@shaw]=𐑕𐑧𐑑𐑦𐑙𐑟 𐑓𐑹 𐑕𐑧𐑝𐑼𐑩𐑤 𐑣𐑭𐑮𐑛𐑢𐑧𐑮 𐑛𐑦𐑝𐑲𐑕𐑩𐑟 Comment[en_GB]=Settings for several hardware devices Comment[eo]=Agordaro por pluraj aparatoj Comment[es]=Propiedades para varios dispositivos de hardware Comment[et]=Mõnede riistvaraliste seadmete sätted Comment[eu]=Hainbat gailuentzako ezarpenak Comment[fa]=تنظیمات دستگاه‌های مختلف سخت‌افزاری Comment[fi]=Laitteiston asetukset Comment[fr]=Paramètres pour certains périphériques Comment[fur]=Impostazions par cetancj dispositîfs hardware Comment[fy]=Ynstellings foar ferskillende hardware apparaten Comment[ga]=Socruithe do roinnt ghléasanna crua-earraí Comment[gl]=Configuracións para varios dispositivos de hardware Comment[gu]=ઘણા હાર્ડવેર ઉપકરણો માટે સુયોજનો Comment[he]=הגדרות עבור מספר התקני חומרה Comment[hi]=कई हार्डवेयर युक्तियों के लिए सेटिंग Comment[hr]=Postavke za nekoliko hardverskih uređaja Comment[hu]=Különböző hardvereszközök beállításai Comment[hy]=Տարբեր սարքերի հատկություններ Comment[id]=Pengaturan untuk sejumlah perangkat keras Comment[is]=Uppsetning á ýmsum vélbúnaði Comment[it]=Impostazioni per parecchi dispositivi hardware Comment[ja]=使用するハードウェア・デバイスを設定します Comment[kk]=Құрылғылардың баптаулары Comment[kn]=ಹಲವಾರು ಯಂತ್ರಾಂಶ ಸಾಧನಗಳಿಗಾಗಿನ ಸಿದ್ಧತೆಗಳು Comment[ko]=하드웨어 장치를 설정합니다 Comment[ku]=Mîhengên çend cîhazên hişkalavê Comment[lt]=Kai kurių aparatinių įrenginių nustatymai Comment[lv]=Dažu aparatūras ierīču iestatījumi Comment[mai]=कतेक हार्डवेयर युक्तिक लेल सेटिंग Comment[mk]=Поставувања за неколку хардверски уреди Comment[ml]=പലയിനം ഹാര്‍ഡ്‌വെയര്‍ ഉപകരണങ്ങള്‍ക്കുള്ള സജ്ജീകരണങ്ങള്‍ Comment[mn]=Техник хангамжийн тохиргоо Comment[mr]=विविध हार्डवेअर साधन करीता संयोजना Comment[nb]=Innstillinger for forskjellige maskinvareenheter Comment[nds]=Instellens för unnerscheedliche Hardware Comment[ne]=विभिन्न हार्डवेयर यन्त्रका लागि सेटिङ Comment[nl]=Hardware-instellingen Comment[nn]=Innstillingar for fleire maskinvareeiningar Comment[oc]=Paramètres per d'unes periferics materials Comment[or]=କେତେକ ହାର୍ଡୱେର ଯନ୍ତ୍ରଗୁଡିକ ପାଇଁ ବିନ୍ୟାସ Comment[pa]=ਕਈ ਜੰਤਰਾਂ ਲਈ ਸਥਾਪਨ Comment[pl]=Ustawienia dla wielu urządzeń Comment[ps]=د ډېرو هډوتري وزلو لپاره امستنې Comment[pt]=Definições para vários dispositivos de equipamento Comment[pt_BR]=Configurações para vários dispositivos de hardware Comment[ro]=Opțiuni hardware Comment[ru]=Параметры нескольких аппаратных устройств Comment[si]=විවිධ දෘඩාංග උපාංග සදහා සැකසුම් Comment[sk]=Nastavenia pre niektoré hardvérové zariadenia Comment[sl]=Nastavitve več strojnih naprav Comment[sq]=Rregullime për dispozitivë të ndryshëm Comment[sr]=Подешавања за неке уређаје Comment[sr@latin]=Podešavanja za neke uređaje Comment[sv]=Inställningar för ett flertal hårdvaruenheter Comment[ta]=பல வண் பொருட்களுக்கான அமைப்புகள் Comment[te]=హార్డ వేర్ పరికరాలకి అమరికలు Comment[th]=ตั้งค่าอุปกรณ์ฮาร์ดแวร์ต่างๆ Comment[tr]=Bazı donanım aygıtları için ayarlar Comment[ug]=قاتتىق دېتاللار تەڭشىكى Comment[uk]=Параметри деяких апаратних пристроїв Comment[vi]=Thiết lập cho các thiết bị phần cứng Comment[zh_CN]=硬件设置 Comment[zh_HK]=多種硬件的設定 Comment[zh_TW]=多種硬體的設定 Icon=preferences-desktop-peripherals Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-system.directory0000664000175000017500000001326412625666430026307 0ustar fabiofabio[Desktop Entry] Name=System Name[af]=Stelsel Name[an]=Sistema Name[ar]=نظام Name[as]=ব্যৱস্থাপ্ৰণালী Name[ast]=Sistema Name[be]=Сыстэма Name[be@latin]=Systema Name[bg]=Система Name[bn]=সিস্টেম Name[bn_IN]=সিস্টেম Name[br]=Reizhiad Name[ca]=Sistema Name[ca@valencia]=Sistema Name[crh]=Sistem Name[cs]=Systém Name[da]=System Name[de]=System Name[dv]=ސިސްޓަމް Name[dz]=རིམ་ལུགས་ Name[el]=Σύστημα Name[en@shaw]=𐑕𐑦𐑕𐑑𐑩𐑥 Name[en_GB]=System Name[eo]=Sistemo Name[es]=Sistema Name[et]=Süsteem Name[eu]=Sistema Name[fa]=سیستم Name[fi]=Järjestelmä Name[fr]=Système Name[fur]=Sisteme Name[fy]=Systeem Name[ga]=Córas Name[gl]=Sistema Name[gu]=સિસ્ટમ Name[gv]=System Name[he]=מערכת Name[hi]=तंत्र Name[hr]=Sustav Name[hu]=Rendszer Name[hy]=Համակարգ Name[id]=Sistem Name[io]=Sistemo Name[is]=Kerfið Name[it]=Sistema Name[ja]=システム Name[kk]=Жүйелік Name[kn]=ವ್ಯವಸ್ಥೆ Name[ko]=시스템 Name[ku]=Pergal Name[lt]=Sistema Name[lv]=Sistēma Name[mai]=तंत्र Name[mk]=Систем Name[ml]=സിസ്റ്റം Name[mn]=Систем Name[mr]=प्रणाली Name[nb]=System Name[nds]=System Name[ne]=प्रणाली Name[nl]=Systeem Name[nn]=System Name[oc]=Sistèma Name[or]=ତନ୍ତ୍ର Name[pa]=ਸਿਸਟਮ Name[pl]=System Name[ps]=غونډال Name[pt]=Sistema Name[pt_BR]=Sistema Name[ro]=Sistem Name[ru]=Система Name[si]=පද්දති Name[sk]=Systém Name[sl]=Sistem Name[sq]=Sistemi Name[sr]=Систем Name[sr@latin]=Sistem Name[sv]=System Name[ta]=கணிப்பொறி Name[te]=వ్యవస్థ పనిముట్లు Name[th]=ระบบ Name[tr]=Sistem Name[ug]=سىستېما Name[uk]=Системна Name[uz@cyrillic]=Тизим Name[vi]=Hệ thống Name[zh_CN]=系统 Name[zh_HK]=系統 Name[zh_TW]=系統 Comment=System settings Comment[af]=Stelselinstellings Comment[an]=Preferencias d'o sistema Comment[ar]=إعدادات النظام Comment[as]=ব্যৱস্থাপ্ৰণালী সংক্ৰান্ত বৈশিষ্ট্য Comment[ast]=Preferencies del sistema Comment[be]=Сыстэмныя настаўленьні Comment[be@latin]=Systemnyja nałady Comment[bg]=Системни настройки Comment[bn]=সিস্টেম সেটিংস Comment[bn_IN]=সিস্টেম সংক্রান্ত বৈশিষ্ট্য Comment[br]=Arventennoù ar reizhiad Comment[ca]=Paràmetres del sistema Comment[ca@valencia]=Paràmetres del sistema Comment[crh]=Sistem tesbitleri Comment[cs]=Systémová nastavení Comment[da]=Systemindstillinger Comment[de]=Systemeinstellungen Comment[dv]=ސިސްޓަމް ސެޓިންގްސް Comment[dz]=རིམ་ལུགས་སྒྲིག་སྟངས་ Comment[el]=Ρυθμίσεις συστήματος Comment[en@shaw]=𐑕𐑦𐑕𐑑𐑩𐑥 𐑕𐑧𐑑𐑦𐑙𐑟 Comment[en_GB]=System settings Comment[eo]=Sistema agordaro Comment[es]=Preferencias del sistema Comment[et]=Süsteemi sätted Comment[eu]=Sistemaren ezarpenak Comment[fa]=تنظیمات سیستم Comment[fi]=Järjestelmän asetukset Comment[fr]=Paramètres système Comment[fur]=Impostazions di sisteme Comment[fy]=Systeem Ynstellingen Comment[ga]=Socruithe an chórais Comment[gl]=Configuracións do sistema Comment[gu]=સિસ્ટમ સુયોજનો Comment[gv]=Reighyn System Comment[he]=הגדרות מערכת Comment[hi]=तंत्र सेटिंग Comment[hr]=Postavke sustava Comment[hu]=Rendszerbeállítások Comment[hy]=Համակարգի հատկություններ Comment[id]=Pengaturan sistem Comment[io]=Sistemala situesi Comment[is]=Kerfisstillingar Comment[it]=Impostazioni di sistema Comment[ja]=システム規模の設定を変更します Comment[kk]=Жүйелік баптаулар Comment[kn]=ವ್ಯವಸ್ಥೆಯ ಸಿದ್ಧತೆಗಳು Comment[ko]=시스템 설정 Comment[ku]=Mîhengên pergalê Comment[lt]=Sistemos nustatymai Comment[lv]=Sistēmas iestatījumi Comment[mai]=तंत्र सेटिंग Comment[mk]=Системски поставувања Comment[ml]=സിസ്റ്റത്തിന്റെ സജ്ജീകരണങ്ങള്‍ Comment[mn]=Системийн тохиргоо Comment[mr]=प्रणाली संयोजना Comment[nb]=Systeminnstillinger Comment[nds]=Systeminstellens Comment[ne]=प्रणाली सेटिङ Comment[nl]=Systeeminstellingen Comment[nn]=Systeminnstillingar Comment[oc]=Paramètres del sistèma Comment[or]=ତନ୍ତ୍ର ବିନ୍ୟାସ Comment[pa]=ਸਿਸਟਮ ਸੈਟਿੰਗ Comment[pl]=Ustawienia systemowe Comment[ps]=غونډال امستنې Comment[pt]=Definições de sistema Comment[pt_BR]=Configurações do sistema Comment[ro]=Configurări de sistem Comment[ru]=Системные параметры Comment[si]=පද්දති සැකසුම් Comment[sk]=Systémové nastavenia Comment[sl]=Sistemske nastavitve Comment[sq]=Rregullime sistemi Comment[sr]=Подешавања система Comment[sr@latin]=Podešavanja sistema Comment[sv]=Systeminställningar Comment[ta]=கணினி அமைப்புகள் Comment[te]=వ్యవస్థ యొక్క అమరికలు Comment[th]=ตั้งค่าระบบ Comment[tr]=Sistem ayarları Comment[ug]=سىستېما تەڭشەكلىرى Comment[uk]=Системні параметри Comment[uz@cyrillic]=Тизим мосламалари Comment[vi]=Thiết lập hệ thống Comment[zh_CN]=系统设置 Comment[zh_HK]=系統設定 Comment[zh_TW]=系統設定 Icon=preferences-system Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-office.directory0000664000175000017500000001451112625666430026212 0ustar fabiofabio[Desktop Entry] Name=Office Name[af]=Kantoor Name[an]=Oficina Name[ar]=مكتب Name[as]=কাৰ্যালয় Name[ast]=Oficina Name[be]=Офіс Name[be@latin]=Ofis Name[bg]=Офис Name[bn]=অফিস Name[bn_IN]=অফিস Name[br]=Bureveg Name[ca]=Ofimàtica Name[ca@valencia]=Ofimàtica Name[crh]=Ofis Name[cs]=Kancelář Name[cy]=Swyddfa Name[da]=Kontor Name[de]=Büro Name[dv]=އޮފީސް Name[dz]=ཡིག་ཚང་། Name[el]=Γραφείο Name[en@shaw]=𐑪𐑓𐑦𐑕 Name[en_CA]=Office Name[en_GB]=Office Name[eo]=Oficejo Name[es]=Oficina Name[et]=Kontor Name[eu]=Bulegoa Name[fa]=اداری Name[fi]=Toimisto Name[fr]=Bureautique Name[fur]=Ufici Name[fy]=Kantoar Name[ga]=Oifig Name[gl]=Ofimática Name[gn]=Nhamboparaa Regua Name[gu]=ઓફિસ Name[gv]=Oik Name[ha]=Ofis Name[he]=משרד Name[hi]=कार्यालय Name[hr]=Ured Name[hu]=Iroda Name[hy]=Օֆիս Name[id]=Perkantoran Name[ig]=Ụlọọrụ Name[io]=Kontoro Name[is]=Skrifstofa Name[it]=Ufficio Name[ja]=オフィス Name[ka]=ოფისი Name[kk]=Кеңселік Name[kn]=ಆಫೀಸ್ Name[ko]=오피스 Name[ku]=Nivîsgeh Name[ky]=Офис Name[lt]=Raštinė Name[lv]=Birojs Name[mai]=कार्यालय Name[mg]=Office Name[mk]=Канцелариски Name[ml]=ഓഫീസ് Name[mn]=Офис Name[mr]=कार्यालय Name[ms]=Pejabat Name[nb]=Kontorstøtte Name[nds]=Kontor Name[ne]=कार्यालय Name[nl]=Kantoor Name[nn]=Kontor Name[oc]=Burèu Name[or]=କାର୍ଯ୍ଯାଳୟ Name[pa]=ਦਫ਼ਤਰ Name[pl]=Biuro Name[ps]=افس Name[pt]=Produtividade Name[pt_BR]=Escritório Name[ro]=Birou Name[ru]=Офис Name[rw]=Ofise Name[si]=කාර්‍යයාලීය Name[sk]=Kancelária Name[sl]=Pisarna Name[sq]=Zyrë Name[sr]=Канцеларија Name[sr@latin]=Kancelarija Name[sv]=Kontor Name[ta]=அலுவலகம் Name[te]=కార్యాలయం Name[th]=สำนักงาน Name[tr]=Ofis Name[ug]=ئىشخانا Name[uk]=Офіс Name[uz@cyrillic]=Офис Name[vi]=Văn phòng Name[xh]=i-Ofisi Name[yo]=Ọ́fíìsì Name[zh_CN]=办公 Name[zh_HK]=辦公 Name[zh_TW]=辦公 Comment=Office Applications Comment[af]=Kantoortoepassings Comment[an]=Aplicacions d'oficina Comment[ar]=التطبيقات المكتبيّة Comment[as]=কাৰ্যালয়ৰ অনুপ্ৰয়োগ Comment[ast]=Aplicaciones d'Oficina Comment[be]=Офісныя дастасаваньні Comment[be@latin]=Ofisnyja prahramy Comment[bg]=Офисни програми Comment[bn]=অফিস অ্যাপলিকেশন Comment[bn_IN]=অফিস অ্যাপলিকেশন Comment[br]=Arloadoù burev all Comment[ca]=Aplicacions ofimàtiques Comment[ca@valencia]=Aplicacions ofimàtiques Comment[crh]=Ofis Uyğulamaları Comment[cs]=Kancelářské aplikace Comment[cy]=Rhaglenni Swyddfa Comment[da]=Kontorprogrammer Comment[de]=Büroanwendungen Comment[dv]=އޮފީސް އެޕްލިކޭޝަންސް Comment[dz]=ཡིག་ཚང་གི་གློག་རིམ། Comment[el]=Εφαρμογές γραφείου Comment[en@shaw]=𐑪𐑓𐑦𐑕 𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟 Comment[en_CA]=Office Applications Comment[en_GB]=Office Applications Comment[eo]=Oficejaj aplikaĵoj Comment[es]=Aplicaciones de oficina Comment[et]=Kontoritarkvara Comment[eu]=Bulegorako aplikazioak Comment[fa]=برنامه‌های اداری Comment[fi]=Toimistosovellukset Comment[fr]=Autres applications Comment[fur]=Aplicazions di ufici Comment[fy]=Kantoar programma's Comment[ga]=Feidhmchláir Oifige Comment[gl]=Aplicativos de oficina Comment[gn]=Jaekaa Omboparaa Comment[gu]=ઓફિસ કાર્યક્રમો Comment[gv]=Claaghyn Oik Comment[ha]=Shiryoyin Ayukan Ofis Comment[he]=יישומים משרדיים Comment[hi]=कार्यालय अनुप्रयोग Comment[hr]=Uredski alati Comment[hu]=Irodai alkalmazások Comment[hy]=Օֆիսային Ծրագրեր Comment[id]=Aplikasi Perkantoran Comment[ig]=Usoro iheomume ụlọọrụ Comment[io]=Kontorala Programi Comment[is]=Skrifstofuforrit Comment[it]=Applicazioni da ufficio Comment[ja]=オフィス・アプリケーションです Comment[ka]=საოფისე პროგრამები Comment[kk]=Кеңселік қолданбалар Comment[kn]=ಆಫೀಸ್ ಅನ್ವಯಗಳು Comment[ko]=오피스 응용프로그램 Comment[ku]=Sepandinên Ofîsê Comment[ky]=Офистик иштемелер Comment[lt]=Raštinės programos Comment[lv]=Biroja lietotnes Comment[mai]=कार्यालयक अनुप्रयोग Comment[mg]=Rindran'asa Office Comment[mk]=Канцелариски апликации Comment[ml]=ഓഫീസ് പ്രയോഗങ്ങള്‍ Comment[mn]=Офисын програмууд Comment[mr]=कार्यालय संबंधित उपकरणं Comment[ms]=Aplikasi Pejabat Comment[nb]=Kontorstøtteprogrammer Comment[nds]=Kontorprogramme Comment[ne]=कार्यालय अनुप्रयोग Comment[nl]=Kantoortoepassingen Comment[nn]=Kontorprogram Comment[oc]=Logicials pel burèu Comment[or]=କାର୍ଯ୍ଯାଳୟ ପ୍ରୟୋଗ Comment[pa]=ਦਫ਼ਤਰ ਕਾਰਜ Comment[pl]=Programy biurowe Comment[ps]=د افس کاريالونه Comment[pt]=Aplicações de Produtividade Comment[pt_BR]=Aplicativos de escritório Comment[ro]=Aplicații de birou Comment[ru]=Офисные приложения Comment[si]=කාර්‍යාලිය යෙදුම් Comment[sk]=Kancelárske aplikácie Comment[sl]=Pisarniški programi Comment[sq]=Programe Zyre Comment[sr]=Програми за канцеларију Comment[sr@latin]=Programi za kancelariju Comment[sv]=Kontorsprogram Comment[ta]=அலுவலகப் பயன்பாடுகள் Comment[te]=కార్యాలయపు కార్యక్షేత్రాలు Comment[th]=โปรแกรมสำหรับสำนักงาน Comment[tr]=Ofis Uygulamaları Comment[ug]=ئىشخانا قوللىنىشچان پروگراممىلىرى Comment[uk]=Офісні програми Comment[uz@cyrillic]=Офис дастурлари Comment[vi]=Ứng dụng văn phòng Comment[xh]=Iinkqubo ze-Ofisi Comment[yo]=Àwọn Ìṣàmúlò-ètò Ọ́fíìsì Comment[zh_CN]=办公应用程序 Comment[zh_HK]=辦公室軟件 Comment[zh_TW]=辦公室軟體 Icon=applications-office Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-settings-system.directory0000664000175000017500000003023312625666430030140 0ustar fabiofabio[Desktop Entry] Type=Directory Name=Administration Name[af]=Administrasie Name[an]=Almenistracion Name[ar]=إدارة Name[as]=প্ৰশাসনিক বৈশিষ্ট্য Name[ast]=Alministración Name[be]=Адміністраваньне Name[be@latin]=Administravańnie Name[bg]=Администрация на системата Name[bn]=অ্যাডমিনস্ট্রেশন Name[bn_IN]=প্রশাসনিক বৈশিষ্ট্য Name[br]=Merañ Name[ca]=Administració Name[ca@valencia]=Administració Name[crh]=Memuriyet Name[cs]=Správa Name[cy]=Gweinyddiaeth Name[da]=Administration Name[de]=Systemverwaltung Name[dv]=އިދާރީ Name[dz]=བདག་སྐྱོང་། Name[el]=Διαχείριση συστήματος Name[en@shaw]=𐑩𐑛𐑥𐑦𐑯𐑦𐑕𐑑𐑮𐑱𐑖𐑩𐑯 Name[en_CA]=Administration Name[en_GB]=Administration Name[eo]=Administrado Name[es]=Administración Name[et]=Haldamine Name[eu]=Sistema-konfigurazioa Name[fa]=مدیریت Name[fi]=Ylläpito Name[fr]=Administration Name[fur]=Aministrazion Name[fy]=Administraasje Name[ga]=Riarachán Name[gl]=Administración Name[gn]=Nhambotea Regua Name[gu]=સંચાલન Name[gv]=Reirey Name[ha]=Hukuma Name[he]=ניהול Name[hi]=प्रशासन Name[hr]=Administracija Name[hu]=Adminisztráció Name[hy]=Կառավարում Name[id]=Administrasi Name[ig]=Nhazi Name[io]=Administrado Name[is]=Stjórnun Name[it]=Amministrazione Name[ja]=システム管理 Name[ka]=ადმინისტრაცია Name[kk]=Әкімшілік Name[kn]=ನಿರ್ವಹಣೆ Name[ko]=관리 Name[ku]=Rêveberî Name[ky]=Системаны башкаруу Name[lt]=Administravimas Name[lv]=Administrēšana Name[mai]=प्रशासन Name[mg]=Fitantanana Name[mk]=Администрација Name[ml]=ഭരണം Name[mn]=Удирдлага Name[mr]=व्यवथापन Name[nb]=Administrasjon Name[nds]=Verwaltens Name[ne]=प्रशासन Name[nl]=Beheer Name[nn]=Administrasjon Name[oc]=Administracion Name[or]=ପ୍ରଶାସନ Name[pa]=ਪਰਸ਼ਾਸਨ Name[pl]=Administracja Name[ps]=پازوالنه Name[pt]=Administração Name[pt_BR]=Administração Name[ro]=Administrare Name[ru]=Администрирование Name[rw]=ubuyobozi Name[si]=පරිපාලනය Name[sk]=Správa Name[sl]=Skrbništvo Name[sq]=Administrim Name[sr]=Администрација Name[sr@latin]=Administracija Name[sv]=Administration Name[ta]=நிர்வாகம் Name[te]=నిర్వహణ Name[th]=ดูแลระบบ Name[tr]=Yönetim Name[ug]=باشقۇر Name[uk]=Адміністрування Name[uz@cyrillic]=Бошқарув Name[vi]=Quản lý Name[xh]=Ulawulo Name[yo]=Ìṣàkóso Name[zh_CN]=系统管理 Name[zh_HK]=管理 Name[zh_TW]=管理 Comment=Change system-wide settings (affects all users) Comment[af]=Wysig instellings stelselwyd (affekteer alle gebruikers) Comment[an]=Cambear as preferencias globals d'o sistema (ta toz os usuarios) Comment[ar]=غيّر إعدادات النظام العامة (يؤثّر على جميع المستخدمين) Comment[as]=সম্পূৰ্ণ ব্যৱস্থাপ্ৰণালী ব্যাপী বৈশিষ্ট্য পৰিবৰ্তন কৰক (সকলো ব্যৱহাৰকৰোঁতা প্ৰভাবিত হ'ব) Comment[ast]=Fai cambeos nes preferencies globales del sistema (afeuta a tolos usuarios) Comment[be]=Зьмены сыстэмных настаўленьняў (датычацца ўсіх карыстальнікаў Comment[be@latin]=Źmiani systemnyja nałady (upłyvaje na ŭsich karystalnikaŭ) Comment[bg]=Промяна на глобалните настройки на системата (ще се отрази на всички потребители) Comment[bn]=সিস্টেমব্যাপী সেটিংস পরিবর্তন করা হবে (সব ব্যবহারকারীর জন্য প্রযোজ্য) Comment[bn_IN]=সম্পূর্ণ সিস্টেমব্যাপী বৈশিষ্ট্য পরিবর্তন করুন (সকল ব্যবহারকারী প্রভাবিত হবেন) Comment[br]=Kemmañ dre vras arventennoù ar reizhiad (evit an holl arveriaded) Comment[ca]=Canvia els paràmetres del sistema (afecta tots els usuaris) Comment[ca@valencia]=Canvia els paràmetres del sistema (afecta tots els usuaris) Comment[crh]=Bütün-sistem tesbitlerini deñiştir (qullanıcılarnıñ episine tesir eter) Comment[cs]=Změnit nastavení pro celý systém (ovlivní všechny uživatele) Comment[da]=Ændr systemindstillinger (påvirker alle brugere) Comment[de]=Systemweite Einstellungen ändern (dies betrifft alle Benutzer) Comment[dv]=މުޅި ސިސްޓަމް ގެ ސެޓިންގްސް ބަދަލު ކުރައްވާ - ޔޫޒާޒްއެކުލެވޭގޮތަށް Comment[dz]=རིམ་ལུགས་ཡོངས་ཀྱི་ གཞི་སྒྲིག་བསྒྱུར་བཅོས་འབད (ལག་ལེན་པ་ཀུན་ལུ་ཕན་གནོད་ཡོད) Comment[el]=Τροποποίηση ρυθμίσεων συστήματος (επηρεάζει όλους τους χρήστες) Comment[en@shaw]=𐑗𐑱𐑯𐑡 𐑕𐑦𐑕𐑑𐑩𐑥-𐑢𐑲𐑛 𐑕𐑧𐑑𐑦𐑙𐑟 (𐑩𐑓𐑧𐑒𐑑𐑕 𐑷𐑤 𐑿𐑟𐑼𐑟) Comment[en_GB]=Change system-wide settings (affects all users) Comment[eo]=Ŝanĝi plensistemajn agordojn (aplikiĝas al ĉiuj uzantoj) Comment[es]=Cambia las preferencias globales del sistema (afecta a todos los usuarios) Comment[et]=Ülesüsteemiliste sätete muutmine (mõjutab kõiki kasutajaid) Comment[eu]=Aldatu sistema osoko ezarpenak (erabiltzaile guztiengan du eragina) Comment[fa]=تغییر تنظیمات کل سیستم (همه‌ی کاربران متأثر می‌شوند)‏ Comment[fi]=Muuta järjestelmänlaajuisia asetuksia (vaikuttaa joka käyttäjään) Comment[fr]=Modifie globalement les paramètres du système (affecte tous les utilisateurs) Comment[fur]=Modifiche impostazions di sisteme (par ducj i utents) Comment[fy]=Systeem-wide ynstellingen (alle brûkers) Comment[ga]=Athraigh socruithe ar fud an chórais (tionchar ar gach úsáideoir) Comment[gl]=Cambiar as configuracións globais do sistema (afecta a todos os usuarios) Comment[gu]=સિસ્ટમ-લગતા સુયોજનો બદલો (બધા વપરાશકર્તાઓને અસર કરશે) Comment[he]=שנה הגדרות מערכת כלליות (משפיע על כל המשתמשים) Comment[hi]=तंत्र स्तरीय सेटिंग बदलें (सभी उपयोक्ता प्रभावित होते हैं) Comment[hr]=Izmjeni postavke za cijeli sustav (utječe na sve korisnike) Comment[hu]=Rendszerszintű beállítások módosítása (minden felhasználóra kihat) Comment[id]=Pengaturan sistem secara luas (mempengaruhi seluruh pengguna) Comment[is]=Breyta stillingum fyrir allt kerfið (hefur áhrif á alla notendur) Comment[it]=Modifica impostazioni di sistema (influenzano tutti gli utenti) Comment[ja]=システム規模の設定を変更します (すべてのユーザに影響します) Comment[kk]=Жүйелік баптауларды өзгерту (барлық пайдаланушылар үшін) Comment[kn]=ವ್ಯವಸ್ಥೆಯಾದ್ಯಂತದ ಸಿದ್ಧತೆಗಳನ್ನು ಬದಲಾಯಿಸಿ (ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಮೇಲೂ ಪರಿಣಾಮಬೀರುತ್ತದೆ) Comment[ko]=시스템 전체 설정을 바꿉니다 (모든 사용자에게 영향을 미칩니다) Comment[ku]=Eyarên hemû pergalê biguherîne (ji bo hemû bikarhêneran derbas dibe) Comment[lt]=Keisti sisteminio masto nustatymus (paveikia visus naudotojus) Comment[lv]=Sistēmas iestatījumu maiņa (ietekmē visus lietotājus) Comment[mai]=तंत्र स्तरीय सेटिंग बदलू (सभटा प्रयोक्ता प्रभावित होइछ) Comment[mk]=Промени ги поставувањата на целиот систем (има ефект врз сите корисници) Comment[ml]=സിസ്റ്റം മുഴുവനുളള സജ്ജീകരണങ്ങളില്‍ മാറ്റം വരുത്തുക (എല്ലാ ഉപയോക്താക്കളേയും ബാധിക്കുന്നു) Comment[mn]=Систем нийтийн тохируулгыг өөрчлөх (бүх хэрэглэгчдэд нөлөөлнө) Comment[mr]=प्रणाली-करीताचे संयोजना बदलवा (सर्व वापरकर्ते प्रभावीत होतात) Comment[nb]=Endre systeminnstillinger (påvirker alle brukere) Comment[nds]=Systemwiete Instellens ännern (Geiht all Bruker wat an) Comment[ne]=सम्पूर्ण प्रणालीका सेटिङ परिवर्तन गर्नुहोस् ( सबै प्रयोगकर्तालाई प्रभाव पार्छ) Comment[nl]=Systeembrede instellingen (op alle gebruikers van toepassing) Comment[nn]=Endra innstillingar for heile systemet (påverkar alle brukarar) Comment[or]=ତନ୍ତ୍ର ବ୍ଯାପି ବିନ୍ଯାସକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ (ସମସ୍ତ ଉପଭୋକ୍ତା ମାନଙ୍କୁ ପ୍ରଭାବିତ କରିଥାଏ) Comment[pa]=ਪੂਰੇ ਸਿਸਟਮ ਲਈ ਸਥਾਪਨ ਬਦਲਾਅ (ਸਭ ਯੂਜ਼ਰਾਂ ਨੂੰ) Comment[pl]=Zmiana ustawień systemowych (wpływa na wszystkich użytkowników) Comment[ps]=(په ټولو کارنانو کارول کيږي) غونډال-ارت امستنې بدلول Comment[pt]=Alterar definições de sistema (afecta todos os utilizadores) Comment[pt_BR]=Modifique configurações gerais do sistema (isso afeta a todos os usuários) Comment[ro]=Configurarea sistemului (afectează toți utilizatorii) Comment[ru]=Изменение системных установок (влияет на всех пользователей) Comment[si]=පද්ධති පුරා සැකසුම් වෙනස් කරන්න (සියළුම පරිශීලකයන්ට බලපානු ඇත) Comment[sk]=Zmeniť nastavenia pre celý systém (ovplyvní všetkých používateľov) Comment[sl]=Spremeni sistemske nastavitve (za vse uporabnike) Comment[sq]=Ndrysho rregullimet në rang sistemi (ndikon tek të gjithë përdoruesit) Comment[sr]=Промените системска подешавања (утиче на све кориснике) Comment[sr@latin]=Promenite sistemska podešavanja (utiče na sve korisnike) Comment[sv]=Ändra inställningar för hela systemet (påverkar alla användare) Comment[ta]=கணிப்பொறியின் அனைத்து அமைப்புகளை மாற்றுக (பயன்படுத்துவோர் அனைவரையும் பாதிக்கும்) Comment[te]=వ్యవస్థకు సంబందించిన అన్ని అమరికలను మార్చు (ఈ మార్పులు అందరు వినియోగ దారులకు వమర్చబడతయి) Comment[th]=การตั้งค่าที่มีผลทั้งระบบ (มีผลต่อผู้ใช้ทุกคน) Comment[tr]=Sistem geneli ayarları değiştir (tüm kullanıcıları etkiler) Comment[ug]=سىستېما تەڭشىكىنى ئۆزگەرت (ھەممە ئىشلەتكۈچىگە تەسىر كۆرسىتىدۇ) Comment[uk]=Зміна системних параметрів (впливає на усіх користувачів) Comment[uz@cyrillic]=Бутун тизимга оид мосламаларни ўзгартириш (барча фойдаланувчиларга таъсир қилади) Comment[vi]=Thay đổi thiết lập cho toàn hệ thống (cho mọi người dùng) Comment[zh_CN]=更改系统设置(影响所有用户) Comment[zh_HK]=更改全系統設定值(影響所有用戶) Comment[zh_TW]=更改全系統設定值(影響所有用戶) Icon=preferences-system Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-audio-video.directory0000664000175000017500000001613712625666430027172 0ustar fabiofabio[Desktop Entry] Name=Sound & Video Name[af]=Klank & video Name[an]=Vidio y soniu Name[ar]=صوت و فيديو Name[as]=শব্দ ও ভিডিও Name[ast]=Soníu y Vídeu Name[be]=Аўдыё і відэа Name[be@latin]=Aŭdyjo j videa Name[bg]=Звук и видео Name[bn]=শব্দ ও ভিডিও Name[bn_IN]=শব্দ ও ভিডিও Name[br]=Son ha video Name[ca]=So i vídeo Name[ca@valencia]=So i vídeo Name[crh]=Davuş & Video Name[cs]=Zvuk a video Name[cy]=Sain a Fideo Name[da]=Lyd og video Name[de]=Multimedia Name[dv]=އަޑު އަދި ވީޑިއޯ Name[dz]=སྒྲ་སྐད་དང་གཟུགས་བརྙན། Name[el]=Ήχος και βίντεο Name[en@shaw]=𐑕𐑬𐑯𐑛 𐑯 𐑝𐑦𐑛𐑦𐑴 Name[en_CA]=Sound & Video Name[en_GB]=Sound & Video Name[eo]=Sono kaj video Name[es]=Sonido y vídeo Name[et]=Audio ja video Name[eu]=Soinua eta bideoa Name[fa]=صدا و تصویر Name[fi]=Ääni & video Name[fr]=Son et vidéo Name[fur]=Audio e video Name[fy]=Lûd en Fideo Name[ga]=Fuaim & Fís Name[gl]=Son e vídeo Name[gn]=Jaexaa, Porai Nhaendua Name[gu]=સાઉન્ડ & વીડિયો Name[gv]=Sheean & feeshan Name[ha]=Sauti & Bidiyo Name[he]=צליל ווידאו Name[hi]=ध्वनि व वीडियो Name[hr]=Zvuk i video Name[hu]=Hang és videó Name[hy]=Ձայն և Վիդեո Name[id]=Suara & Video Name[ig]=Ụda na Vidio Name[io]=Sono ed Video Name[is]=Hljóð og mynd Name[it]=Audio e video Name[ja]=サウンドとビデオ Name[ka]=ხმა და ვიდეო Name[kk]=Аудио және видео Name[kn]=ಧ್ವನಿ ಹಾಗು ದೃಶ್ಯ Name[ko]=음악과 비디오 Name[ku]=Deng & Vîdeo Name[ky]=Аудио и видео Name[lt]=Garsas ir vaizdas Name[lv]=Skaņa un video Name[mai]=ध्वनि आ वीडियो Name[mg]=Feo sy sarimihetsika Name[mk]=Звук и видео Name[ml]=ശബ്ദവും ചലച്ചിത്രവും Name[mn]=Дуу & Видео Name[mr]=आवाज व चलचित्र Name[ms]=Bunyi & Video Name[nb]=Lyd og bilde Name[nds]=Ton un Bild Name[ne]=ध्वनि र भिडियो Name[nl]=Geluid & Video Name[nn]=Lyd og bilete Name[oc]=Son e vidèo Name[or]=ଧ୍ବନି ଏବଂ ଭିଡିଓ Name[pa]=ਸੰਗੀਤ ਅਤੇ ਵੀਡਿਓ Name[pl]=Dźwięk i obraz Name[ps]=غږ او وېډيو Name[pt]=Som & Vídeo Name[pt_BR]=Multimídia Name[ro]=Multimedia Name[ru]=Аудио и видео Name[si]=ශබ්ද සහ දෘශ්‍ය Name[sk]=Zvuk a video Name[sl]=Zvok in video Name[sq]=Zë & Video Name[sr]=Звук и покретне слике Name[sr@latin]=Zvuk i pokretne slike Name[sv]=Ljud och video Name[ta]=ஒலி மற்றும் படக்காட்சி Name[te]=ధ్వని మరియు ద్రుశ్యం Name[th]=เสียงและวีดิทัศน์ Name[tr]=Ses ve Video Name[ug]=ئۈن ۋە سىن Name[uk]=Звук та відео Name[uz@cyrillic]=Товуш ва видео Name[vi]=Âm thanh và Ảnh động Name[xh]=Isandi neVidiyo Name[yo]=Ìró & Fídíò Name[zh_CN]=影音 Name[zh_HK]=影音 Name[zh_TW]=影音 Comment=Multimedia menu Comment[af]=Multimediakieslys Comment[an]=Menú de programas multimedia Comment[ar]=قائمة الوسائط المتعدّدة Comment[as]=মাল্টি-মিডিয়া তালিকা Comment[ast]=Menú multimedia Comment[be]=Мультымэдыя Comment[be@latin]=Multymedyi Comment[bg]=Меню за мултимедия Comment[bn]=মাল্টিমিডিয়া মেনু Comment[bn_IN]=মাল্টি-মিডিয়া মেনু Comment[br]=Lañser al liesvedia Comment[ca]=Menú multimèdia Comment[ca@valencia]=Menú multimèdia Comment[crh]=Çoqlu-vasat menüsi Comment[cs]=Nabídka multimédií Comment[cy]=Dewislen amlgyfrwng Comment[da]=Multimedie-menu Comment[de]=Multimedia-Menü Comment[dv]=މަލްޓިމީޑިއާ މެނޫ Comment[dz]=སྣ་མང་བརྡ་ལམ་དཀར་ཆག། Comment[el]=Μενού πολυμέσων Comment[en@shaw]=𐑥𐑩𐑤𐑑𐑰𐑥𐑰𐑛𐑰𐑩 𐑥𐑧𐑯𐑿 Comment[en_CA]=Multimedia menu Comment[en_GB]=Multimedia menu Comment[eo]=Plurmediomenuo Comment[es]=Menú de programas para multimedia Comment[et]=Multimeediamenüü Comment[eu]=Multimediaren menua Comment[fa]=منوی چندرسانه‌ای Comment[fi]=Multimediavalikko Comment[fr]=Multimédia Comment[fur]=Menu multimediâl Comment[fy]=Media Comment[ga]=Roghchlár ilmheán Comment[gl]=Multimedia Comment[gn]=Baemo Jaexaa, Nhaendua Comment[gu]=મલ્ટીમીડિયા મેનુ Comment[ha]=Mazaɓen masilimanta Comment[he]=תפריט מולטימדיה Comment[hi]=मल्टीमीडिया मेनू Comment[hr]=Multimedijski izbornik Comment[hu]=Multimédia menü Comment[hy]=Համասփյուռ ծրագրերի ցանկ Comment[id]=Menu multimedia Comment[ig]=Mọltimidia menu Comment[is]=Margmiðlun Comment[it]=Menù multimedia Comment[ja]=マルチメディア関連のプログラムです Comment[ka]=მულტიმედიის მენუ Comment[kk]=Мультимедиа Comment[kn]=ಮಲ್ಟಿಮೀಡಿಯಾ ಅಂಶಪಟ್ಟಿ Comment[ko]=멀티미디어 메뉴 Comment[ku]=Pêşeka multîmedya Comment[ky]=Мультимедиа Comment[lt]=Daugialypės terpės meniu Comment[lv]=Multimediju izvēlne Comment[mai]=मल्टीमीडिया मेनू Comment[mg]=Karazan-tsafidin'ny haino aman-jery Comment[mk]=Мени со мултимедија Comment[ml]=മള്‍ട്ടീമീഡിയ മെനു Comment[mn]=Мультимедиа цэс Comment[mr]=मल्टीमिडीया मेन्यू Comment[ms]=Menu Multimedia Comment[nb]=Multimedia Comment[nds]=Multimedia Comment[ne]=मल्टिमिडिया मेनु Comment[nl]=Multimedia Comment[nn]=Multimediameny Comment[oc]=Multimèdia Comment[or]=ବହୁମାଧ୍ଯମ ତାଲିକା Comment[pa]=ਮਲਟੀਮੀਡਿਆ ਮੇਨੂ Comment[pl]=Multimedia Comment[ps]=ګڼرسنۍ غورنۍ Comment[pt]=Menu de multimédia Comment[pt_BR]=Menu multimídia Comment[ro]=Meniu multimedia Comment[ru]=Мультимедиа Comment[si]=බහුමාද්‍ය මෙනුව Comment[sk]=Ponuka multimédií Comment[sl]=Večpredstavnostni meni Comment[sq]=Menu multimediale Comment[sr]=Мени за мултимедију Comment[sr@latin]=Meni za multimediju Comment[sv]=Multimediameny Comment[ta]=பல்முனை ஊடகப் பட்டியல் Comment[te]=బహుళమాధ్యమాల జాబితా Comment[th]=เมนูระบบสื่อผสม Comment[tr]=Çokluortam menüsü Comment[ug]=كۆپ ۋاسىتە تىزىملىك Comment[uk]=Мультимедіа Comment[uz@cyrillic]=Мултимедиа менюси Comment[vi]=Trình đơn Đa phương tiện Comment[xh]=Imenu Yezixhobo eziphathekayo zokugcina ulwazi ngokuphinda-phindeneyo Comment[yo]=Àtòjọ-ẹ̀yàn agbé-ọ̀pọ̀-ìròyìn-jáde Comment[zh_CN]=多媒体菜单 Comment[zh_HK]=多媒體選單 Comment[zh_TW]=多媒體選單 Icon=applications-multimedia Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-network.directory0000664000175000017500000002763412625666430026462 0ustar fabiofabio[Desktop Entry] Name=Internet Name[af]=Internet Name[an]=Internet Name[ar]=إنترنت Name[as]=ইন্টাৰনেট Name[ast]=Internet Name[be]=Інтэрнэт Name[be@latin]=Internet Name[bg]=Интернет Name[bn]=ইন্টারনেট Name[bn_IN]=ইন্টারনেট Name[br]=Kenrouedad Name[ca]=Internet Name[ca@valencia]=Internet Name[crh]=İnternet Name[cs]=Internet Name[cy]=Rhyngrwyd Name[da]=Internet Name[de]=Internet Name[dv]=އިންޓަނެޓް Name[dz]=ཨིན་ཊར་ནེཊི། Name[el]=Διαδίκτυο Name[en@shaw]=·𐑦𐑯𐑑𐑼𐑯𐑧𐑑 Name[en_CA]=Internet Name[en_GB]=Internet Name[eo]=Interreto Name[es]=Internet Name[et]=Internet Name[eu]=Internet Name[fa]=اینترنت Name[fi]=Internet Name[fr]=Internet Name[fur]=Internet Name[fy]=Ynternet Name[ga]=Idirlíon Name[gl]=Internet Name[gn]=Kya Name[gu]=ઈન્ટરનેટ Name[gv]=Eddyr-voggyl Name[ha]=Intanet Name[he]=אינטרנט Name[hi]=इंटरनेट Name[hr]=Internet Name[hu]=Internet Name[hy]=Համացանց Name[id]=Internet Name[ig]=Ịntaneetị Name[io]=Interreto Name[is]=Internet Name[it]=Internet Name[ja]=インターネット Name[ka]=ინტერნეტი Name[kk]=Интернет Name[kn]=ಅಂತರಜಾಲ Name[ko]=인터넷 Name[ku]=Înternet Name[ky]=Интернет Name[lt]=Internetas Name[lv]=Internets Name[mai]=इंटरनेट Name[mg]=Internet Name[mk]=Интернет Name[ml]=ഇന്റര്‍നെറ്റ് Name[mn]=Интернэт Name[mr]=इंटरनेट Name[ms]=Internet Name[nb]=Internett Name[nds]=Innernet Name[ne]=इन्टरनेट Name[nl]=Internet Name[nn]=Internett Name[oc]=Internet Name[or]=ଇଣ୍ଟରନେଟ Name[pa]=ਇੰਟਰਨੈੱਟ Name[pl]=Internet Name[ps]=اېنټرنېټ Name[pt]=Internet Name[pt_BR]=Internet Name[ro]=Internet Name[ru]=Интернет Name[rw]=Interineti Name[si]=අන්තර්ජාලය Name[sk]=Internet Name[sl]=Splet Name[sq]=Internet Name[sr]=Интернет Name[sr@latin]=Internet Name[sv]=Internet Name[ta]=இணையம் Name[te]=మహాతలం Name[th]=อินเทอร์เน็ต Name[tr]=İnternet Name[ug]=تۇتاش تور Name[uk]=Інтернет Name[uz@cyrillic]=Интернет Name[vi]=Mạng Name[xh]=I-intanethi Name[yo]=Ítánẹ́̀ẹ̀tì Name[zh_CN]=互联网 Name[zh_HK]=互聯網 Name[zh_TW]=網際網路 Comment=Programs for Internet access such as web and email Comment[af]=Programme vir Internettoegang soos web en e-pos Comment[an]=Programas ta l'acceso a internet, tals como web y e-mail Comment[ar]=برامج للنّفاذ إلى الإنترنت مثل الوب و البريد الإلكتروني Comment[as]=ইন্টাৰনেট ব্যবহাৰৰ অনুপ্ৰয়োগ যেনে ওৱেব আৰু ই-মেইল অনুপ্ৰয়োগ Comment[ast]=Programes p'accesu a Internet como web y corréu Comment[be]=Праграмы для доступу ў Інтэрнэт (Сеціва, пошта і інш.) Comment[be@latin]=Prahramy dla internetu Comment[bg]=Програми за работа в Интернет, например е-поща и браузър Comment[bn]=ইন্টারনেট ব্যবহারের প্রোগ্রাম, যেমন ওয়েব বা ইমেইল Comment[bn_IN]=ইন্টারনেট ব্যবহারের প্রোগ্রাম যেমন ওয়েব ও ই-মেইল প্রোগ্রাম Comment[br]=Goulevioù evit tizhout ar c'henrouedad, posteliñ ha furcherezh Comment[ca]=Programes per a accedir a Internet, com la web i el correu Comment[ca@valencia]=Programes per a accedir a Internet, com la web i el correu Comment[crh]=İnternet irişimi içün programlar, kezici ve e-poçta kibi Comment[cs]=Programy pro přístup k Internetu, například WWW a e-mail Comment[cy]=Rhaglenni ar gyfer mynediad Rhyngrwyd megis y we ac e-bost Comment[da]=Programmer til internetadgang, såsom web og e-post Comment[de]=Programme um Internet-Dienste wie E-Mail oder das WWW zu nutzen Comment[dv]=ވެބް އަދި އީމެއިލް ފަދަ އިންޓާނެޓްގެ ބޭނުންހިފާ ޕްރޮގްރާމުތައް Comment[dz]=ཨིན་ཊར་ནེཊི་ལུ་ འཛུལ་སྤྱོད་འབད་ནིའི་ལས་རིམ་ དཔེར་ན་ ཝེབ་དང་གློག་འཕྲིན་ལྟ་བུ། Comment[el]=Προγράμματα πρόσβασης στο Διαδίκτυο όπως φυλλομετρητής και ηλεκ. αλληλογραφία Comment[en@shaw]=𐑐𐑮𐑴𐑜𐑮𐑨𐑥𐑟 𐑓𐑹 ·𐑦𐑯𐑑𐑼𐑯𐑧𐑑 𐑨𐑒𐑕𐑧𐑕 𐑕𐑳𐑗 𐑨𐑟 𐑢𐑧𐑚 𐑯 𐑰𐑥𐑱𐑤 Comment[en_CA]=Programs for Internet access, such as web and email Comment[en_GB]=Programs for Internet access such as web and email Comment[eo]=Programoj por retuzo, ekzemple por retlegado kaj retpoŝto Comment[es]=Programas para acceso a Internet, tales como web y correo-e Comment[et]=Internetprogrammid - näiteks veebi ja e-posti jaoks Comment[eu]=Interneteko programak (web arakatzaileak eta posta bezeroak adib.) Comment[fa]=برنامه‌هایی برای دست‌یابی به اینترنت مثل وب و پست الکترونیکی Comment[fi]=Internet-sovellukset kuten selain ja sähköposti Comment[fr]=Les programmes pour accéder à Internet tel que le Web ou la messagerie Comment[fur]=Programs par internet come web e mail Comment[fy]=Programma's foar Ynternet tagong sa as web en ynternet post Comment[ga]=Feidhmchláir do rochtain an Idirlín mar ghréasán agus ríomhphost Comment[gl]=Programas para o acceso á Internet como a web e o correo electrónico Comment[gn]=Kya Py Reike Awã Regua Comment[gu]=વેબ અને ઈમેઈલ જેવા ઈન્ટરનેટ વપરાશ માટેના કાર્યક્રમો Comment[gv]=Claaghyn son yn eddyrvoagle gorrish email as yn eddyr-voggyl Comment[ha]=Shiryoyi na samun hanyar Intanet zuwa wurare kamar yana da e-meli Comment[he]=תוכנות לגישה לאינטרנט כגון דפדפן ולקוח דואר Comment[hi]=इंटरनेट पहुँच के लिए वेब व ईमेल जैसे प्रोग्राम Comment[hr]=Programi za pristup Internetu, poput weba ili elektroničke pošte Comment[hu]=Programok az internet használatához (pl. web és e-mail) Comment[hy]=Համացանցում աշխատելու ծրագրեր, ինչպես վեպ զննիչը Comment[id]=Program untuk mengakses internet, seperti web dan surel Comment[ig]=Program maka ikikembanye ịntaneetị dịka web na Imeelụ Comment[is]=Forrit til að nálgast netið eins og vefinn og tölvupóst Comment[it]=Programmi per l'accesso a Internet come web ed email Comment[ja]=ウェブやメールなどインターネットにアクセスのためのプログラムです Comment[ka]=პროგრამები ინტერნეტისათვის, ვები და მეილი Comment[kk]=Интернетті қолдануға арналған бағдарламалар (пошта, браузер, т.б.) Comment[kn]=ಜಾಲ ಹಾಗು ಇಮೈಲಿನಂತಹ ಅಂತರಜಾಲವನ್ನು ನಿಲುಕಿಸಿಕೊಳ್ಳುವ ಪ್ರೊಗ್ರಾಂಗಳು Comment[ko]=웹과 전자메일 같은 인터넷에 접근 가능한 프로그램 Comment[ku]=Bernameyên wekî epeyam û gerokan yên ji bo pêwendiya înternetê Comment[ky]=Интернет менен иштөө программалары (почта, аралагыч ж.б.) Comment[lt]=Programos interneto prieigai (žiniatinklis, el. paštas) Comment[lv]=Programmas darbam internetā kā tīmekļa pārlūkošana un e-pasts Comment[mai]=इंटरनेटसँ जुड़बाक लेल वेब आ इमेल जहिना प्रोग्राम Comment[mg]=Rindran'asa ahafahana mifandray amin'ny Internet toy ny tranonkala sy ny mailaka Comment[mk]=Програми за пристап на интернет Comment[ml]=ഇന്റര്‍നെറ്റ് ലഭ്യതയ്ക്കാവശ്യമുളള പ്രോഗ്രാമുകളായ വെബും ഇമെയിലും Comment[mn]=Вэб болон э-шуудан гэх мэт интернэт хандалтын програмууд Comment[mr]=इंटरनेट वेब व ईमेल बघण्यासाठी प्रोग्राम Comment[ms]=Program untuk mengakses Internet seperti web dan emel Comment[nb]=Programmer for Internettaksess slik som nettleser og e-post Comment[nds]=Programme för Innernettogrip as Net un E-Post Comment[ne]=वेब र इमेल जस्तो इन्टरनेट पहुँचका लागि कार्यक्रम Comment[nl]=Toepassingen voor internettoegang zoals web en e-mail Comment[nn]=Program for bruk på Internett, som nettlesar og e-postklient Comment[oc]=Logicials per accedir a internet, coma lo web o los messatges electronics Comment[or]=ଇଣ୍ଟରନେଟ ଅଭିଗମ୍ୟତା ପାଇଁ ୱେବ ଏବଂ ଇ-ମେଲ ଭଳି ପ୍ରୋଗ୍ରାମଗୁଡ଼ିକ Comment[pa]=ਇੰਟਰਨੈੱਟ ਇਸਤੇਮਾਲ ਕਰਨ ਲਈ ਕਾਰਜ, ਜਿਵੇਂ ਕਿ ਵੈੱਬ ਤੇ ਈ-ਮੇਲ Comment[pl]=Programy umożliwiające dostęp do Internetu, jak strony WWW, czy e-mail Comment[ps]=د اېنټرنېټ د لاسرس لپاره کړنلارې لکه ګورت او برېښليک Comment[pt]=Aplicações para o acesso à Internet tais como web e email Comment[pt_BR]=Programas para acesso à Internet tais como web e e-mail Comment[ro]=Programe pentru acessul la Internet (navigare, mail etc.) Comment[ru]=Программы для работы с Интернетом (почта, браузер и т.п.) Comment[si]=අන්තර්ජාලය පිවිසුමට වියුණු සහ ඉලෙක්ට්‍රොනික තැපෑල වැනි කේතයන් Comment[sk]=Programy pre prístup k internetu, ako je www a e-mail Comment[sl]=Programi za dostop do spleta, kot sta brskalnik in elektronska pošta Comment[sq]=Programe për hyrje në Internet (si p.sh. rrjeti dhe posta elektronike) Comment[sr]=Програми за приступ Интернету као што је веб и е-пошта Comment[sr@latin]=Programi za pristup Internetu kao što je veb i e-pošta Comment[sv]=Program för internetåtkomst som till exempel webb och e-post Comment[ta]=வலைப்பின்னல் மற்றும் மின்னஞ்சல் போன்ற இணைய அணுகலுக்கான நிரல்கள் Comment[te]=ఇంటర్నెట్ తో సాంగత్యం ఏర్పరుచు కార్యక్రమాలు Comment[th]=โปรแกรมสำหรับเข้าอินเทอร์เน็ต เช่น เว็บและเมล Comment[tr]=Web ve e-posta gibi internete erişim amaçlı programlar Comment[ug]=تور ۋە ئېلخەتكە ئوخشاش تۇتاش تور زىيارەت پروگراممىلىرى Comment[uk]=Програми для роботи з Інтернет (пошта, веб-переглядач, тощо) Comment[uz@cyrillic]=Веб браузер ва эл.почта каби интернет билан ишлаш дастурлари Comment[vi]=Chương trình truy cập Mạng như trình duyệt và trình nhận/gửi thư điện tử Comment[xh]=Iinkqubo zokufikelela kwi-Intanethi ezinjenge-web ne-imeyile Comment[yo]=Àwọn àtòjọ-ètò fún ìráyè ítánẹ́ẹ̀tì bíi wẹ́ẹ̀bù àti lẹ́tà kọ̀ǹpútà Comment[zh_CN]=网络相关应用软件,如网络浏览和电子邮件管理 Comment[zh_HK]=如瀏覽網頁及收發電郵等網絡軟件 Comment[zh_TW]=如瀏覽網頁及收發電郵等網路軟體 Icon=applications-internet Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-utility-accessibility.directory0000664000175000017500000002045612625666430031314 0ustar fabiofabio[Desktop Entry] Name=Universal Access Name[af]=Universele toegang Name[an]=Acceso universal Name[ar]=الإتاحة Name[as]=সাৰ্বজনীন ব্যবহাৰাধিকাৰ Name[ast]=Accesu universal Name[be]=Унівэрсальны доступ Name[be@latin]=Universalny dostup Name[bg]=Универсален достъп Name[bn]=সর্বজনীন প্রবেশাধিকার Name[bn_IN]=সার্বজনীন ব্যবহারাধিকার Name[br]=Haezhiñ hollvedel Name[ca]=Accés universal Name[ca@valencia]=Accés universal Name[crh]=Alemiy İrişim Name[cs]=Zpřístupnění Name[da]=Universel adgang Name[de]=Barrierefreiheit Name[dv]=ހުރިހާދިމލަކުން ވަދެވޭ Name[dz]=འཛམ་གླིང་ཡོངས་ཁྱབ་འཛུལ་སྤྱོད་འབད་ནི Name[el]=Γενική πρόσβαση Name[en@shaw]=𐑿𐑯𐑩𐑝𐑻𐑕𐑩𐑤 𐑨𐑒𐑕𐑧𐑕 Name[en_GB]=Universal Access Name[eo]=Universala aliro Name[es]=Acceso universal Name[et]=Universaalne ligipääs Name[eu]=Sarbide unibertsala Name[fa]=دسترسی همگانی Name[fi]=Esteettömyys Name[fr]=Accès universel Name[fur]=Acess Universâl Name[fy]=Algemiene Tagong Name[ga]=Rochtain Uilíoch Name[gl]=Acceso universal Name[gu]=સાર્વત્રિક વપરાશ Name[he]=גישה אוניברסלית Name[hi]=सार्वभौमिक पहुँच Name[hr]=Univerzalni pristup Name[hu]=Akadálymentesítés Name[hy]=Ընդհանուր Մատչելիություն Name[id]=Akses Universal Name[io]=Universala Aceso Name[is]=Altækur aðgangur Name[it]=Accesso universale Name[ja]=ユニバーサル・アクセス Name[kk]=Арнайы мүмкіндіктер Name[kn]=ಜಾಗತಿಕ ನಿಲುಕಣೆ Name[ko]=보편적 접근성 Name[ku]=Gihîştina Gerdûnî Name[lt]=Universali prieiga Name[lv]=Universālā piekļuve Name[mai]=सार्वभौमिक पहुँच Name[mk]=Универзален пристап Name[ml]=സാര്‍വ്വജനികലഭ്യത Name[mn]=Саадгүй хандалт Name[mr]=जागतीक प्रवेश Name[nb]=Tilgjengelighet Name[nds]=Togrip in'n Groten un Ganzen Name[ne]=विश्वब्यापी पहुँच Name[nl]=Toegankelijkheid Name[nn]=Tilgjenge Name[oc]=Accès universal Name[or]=ସାର୍ବଜନିକ ଅଭିଗମ୍ୟତା Name[pa]=ਵਿਆਪਕ ਪਹੁੰਚ Name[pl]=Dostęp uniwersalny Name[ps]=نړېوال لاسرس Name[pt]=Acesso Universal Name[pt_BR]=Acessibilidade Name[ro]=Accesibilitate Name[ru]=Специальные возможности Name[si]=විශ්ව පිවිසුම Name[sk]=Univerzálny prístup Name[sl]=Splošni dostop Name[sq]=Hyrje universale Name[sr]=Универзални приступ Name[sr@latin]=Univerzalni pristup Name[sv]=Allmän åtkomst Name[ta]=உலகளாவிய அணுகல் Name[te]=విశ్వాంతర సాంగత్యం Name[th]=สิ่งอำนวยความสะดวก Name[tr]=Evrensel Erişim Name[ug]=ھەممىباب زىيارەت Name[uk]=Універсальний доступ Name[vi]=Truy cập hoàn toàn Name[zh_CN]=通用访问 Name[zh_HK]=無障礙功能 Name[zh_TW]=無障礙功能 Comment=Universal Access Settings Comment[af]=Verstelling van universele toegang Comment[an]=Preferencias d'acceso universal Comment[ar]=إعدادات الإتاحة Comment[as]=সাৰ্বজনীন ব্যবহাৰাধিকাৰ সংক্ৰান্ত সম্পৰ্কিত বৈশিষ্ট্য Comment[ast]=Preferencies del accesu universal Comment[be]=Настаўленьні унівэрсальнага доступу Comment[be@latin]=Nałady ŭniversalnaha dostupu Comment[bg]=Настройки на универсалния достъп Comment[bn]=সর্বজনীন প্রবেশাধিকার সেটিংস Comment[bn_IN]=সার্বজনীন ব্যবহারাধিকার সংক্রান্ত সম্পর্কিত বৈশিষ্ট্য Comment[br]=Dibarzhioù haezhiñ hollvedel Comment[ca]=Paràmetres d'accés universal Comment[ca@valencia]=Paràmetres d'accés universal Comment[crh]=Universal İrişim Tesbitleri Comment[cs]=Nastavení zpřístupnění Comment[da]=Indstillinger for universel adgang Comment[de]=Einstellungen zur Barrierefreiheit Comment[dv]=ވަނުމުގެ ސެޓިން ހެދުން Comment[dz]=འཛམ་གླིང་ཡོངས་ཁྱབ་འཛུལ་སྤྱོད་ཀྱི་གཞི་སྒྲིག་ཚུ Comment[el]=Ρυθμίσεις γενικής πρόσβασης Comment[en@shaw]=𐑿𐑯𐑩𐑝𐑻𐑕𐑩𐑤 𐑨𐑒𐑕𐑧𐑕 𐑕𐑧𐑑𐑦𐑙𐑟 Comment[en_GB]=Universal Access Settings Comment[eo]=Agordoj por universala aliro Comment[es]=Preferencias del acceso universal Comment[et]=Universaalse ligipääsu sätted Comment[eu]=Sarbide unibertsalaren ezarpenak Comment[fa]=تنظیمات دسترسی همگانی Comment[fi]=Esteettömän käytön asetukset Comment[fr]=Paramètres d'accès universel Comment[fur]=Impostazions Acess Universâl Comment[fy]=Algemiene Tagong Ynstellingen Comment[ga]=Socruithe Rochtana Uilíoch Comment[gl]=Configuracións de acceso universal Comment[gu]=સાર્વત્રિક વપરાશ સુયોજનો Comment[he]=הגדרות גישה אוניברסלית Comment[hi]=सार्वभौमिक पहुँच सेटिंग Comment[hr]=Postavke univerzalnog pristupa Comment[hu]=Akadálymentesítés beállításai Comment[hy]=Հնարավորությունների կարգավորումներ Comment[id]=Pengaturan Akses Universal Comment[io]=Situesi pri Universala Aceso Comment[is]=Stillingar fyrir altækan aðgang Comment[it]=Impostazioni accesso universale Comment[ja]=ユニバーサル・アクセスの設定を変更します Comment[kk]=Арнайы мүмкіндіктер баптаулары Comment[kn]=ಜಾಗತಿಕ ನಿಲುಕಣೆ ಸಿದ್ಧತೆಗಳು Comment[ko]=보편적 접근성 설정 Comment[ku]=Mîhengên Gihîştina Gerdûnî Comment[lt]=Universalios prieigos nustatymai Comment[lv]=Universālās piekļuves iestatījumi Comment[mai]=सार्वभौमिक पहुँच सेटिंग Comment[mk]=Поставувања за универзален пристап Comment[ml]=സാര്‍വ്വജനികലഭ്യതാ സജ്ജീകരണങ്ങള്‍ Comment[mn]=Саадгүй хандалтын тохиргоо Comment[mr]=जागतीक प्रवेश संयोजना Comment[nb]=Innstillinger for tilgjengelighet Comment[nds]=Togripinstellens in'n Groten un Ganzen Comment[ne]=विश्वब्यापी पहुँच सेटिङ Comment[nl]=Toegankelijkheidsinstellingen Comment[nn]=Innstillingar for tilgjenge Comment[or]=ସାର୍ବଜନିକ ଅଭିଗମ୍ୟତା ବିନ୍ଯାସ Comment[pa]=ਵਿਆਪਕ ਪਹੁੰਚ ਸਥਾਪਨ Comment[pl]=Ustawienia uniwersalnego dostępu Comment[ps]=د نړېوال لاسرس امستنې Comment[pt]=Definições de Acesso Universal Comment[pt_BR]=Configurações de acessibilidade Comment[ro]=Opțiuni de accesibilitate Comment[ru]=Параметры специальных возможностей Comment[si]=විශ්ව පිවිසුම් සැකසුම් Comment[sk]=Nastavenia univerzálneho prístupu Comment[sl]=Splošne nastavitve dostopa Comment[sq]=Rregullimet për hyrjen universale Comment[sr]=Подешавања универзалног приступа Comment[sr@latin]=Podešavanja univerzalnog pristupa Comment[sv]=Inställningar för allmän åtkomst Comment[ta]=உலகளாவிய அணுகல் அமைப்புகள் Comment[te]=విశ్వాంతర సాంగత్య అమరికలు Comment[th]=ตั้งค่าสิ่งอำนวยความสะดวก Comment[tr]=Evrensel Erişim Ayarları Comment[ug]=ھەممىباب زىيارەت تەڭشىكى Comment[uk]=Параметри універсального доступу Comment[vi]=Thiết lập Truy cập Hoàn toàn Comment[zh_CN]=通用访问设置 Comment[zh_HK]=無障礙功能設定值 Comment[zh_TW]=無障礙功能設定值 Type=Directory Icon=preferences-desktop-accessibility Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-settings.directory0000664000175000017500000001517012625666430026621 0ustar fabiofabio[Desktop Entry] Name=Preferences Name[af]=Voorkeure Name[an]=Preferencias Name[ar]=تفضيلات Name[as]=পছন্দ Name[ast]=Preferencies Name[be]=Перавагі Name[be@latin]=Nałady Name[bg]=Настройки Name[bn]=পছন্দতালিকা Name[bn_IN]=পছন্দ Name[br]=Gwellvezioù Name[ca]=Preferències Name[ca@valencia]=Preferències Name[crh]=Tercihler Name[cs]=Volby Name[cy]=Hoffterau Name[da]=Indstillinger Name[de]=Einstellungen Name[dv]=ބޭނުންވާ ގޮތްތަށް Name[dz]=དགའ་གདམ་ཚུ། Name[el]=Προτιμήσεις Name[en@shaw]=𐑐𐑮𐑧𐑓𐑼𐑩𐑯𐑕𐑩𐑟 Name[en_CA]=Preferences Name[en_GB]=Preferences Name[eo]=Agordoj Name[es]=Preferencias Name[et]=Eelistused Name[eu]=Hobespenak Name[fa]=ترجیحات Name[fi]=Asetukset Name[fr]=Préférences Name[fur]=Preferencis Name[fy]=Ynstellingen Name[ga]=Sainroghanna Name[gl]=Preferencias Name[gn]=Maranmi Reipota Name[gu]=પસંદગીઓ Name[gv]=Reighyn ed's Name[ha]=Fifiko Name[he]=העדפות Name[hi]=वरीयता Name[hr]=Postavke Name[hu]=Beállítások Name[hy]=Հատկություններ Name[id]=Preferensi Name[ig]=Nkarachọ Name[io]=Preferi Name[is]=Stillingar Name[it]=Preferenze Name[ja]=設定 Name[ka]=პარამეტრები Name[kk]=Баптаулар Name[kn]=ಆದ್ಯತೆಗಳು Name[ko]=기본 설정 Name[ku]=Vebijêrk Name[ky]=Ырастоолор Name[lt]=Nustatymai Name[lv]=Iestatījumi Name[mai]=वरीयतासभ Name[mg]=Safidy Name[mk]=Преференции Name[ml]=മുന്‍ഗണനകള്‍ Name[mn]=Тохиргоо Name[mr]=प्राधान्ये Name[ms]=Keutamaan Name[nb]=Brukervalg Name[nds]=Instellens Name[ne]=प्राथमिकता Name[nl]=Voorkeuren Name[nn]=Innstillingar Name[oc]=Preferéncias Name[or]=ପସନ୍ଦ Name[pa]=ਮੇਰੀ ਪਸੰਦ Name[pl]=Preferencje Name[ps]=غوراوي Name[pt]=Preferências Name[pt_BR]=Preferências Name[ro]=Preferințe Name[ru]=Параметры Name[rw]=Ibyahisemo Name[si]=අභිප්‍රේත Name[sk]=Predvoľby Name[sl]=Možnosti Name[sq]=Preferime Name[sr]=Поставке Name[sr@latin]=Postavke Name[sv]=Inställningar Name[ta]=விருப்பங்கள் Name[te]=అభీష్టాలు Name[th]=ปรับแต่งพื้นโต๊ะ Name[tr]=Tercihler Name[ug]=مايىللىق Name[uk]=Параметри Name[vi]=Tùy thích Name[xh]=Uluhlu lwezinto ezikhethwayo Name[yo]=Àwọn ìkúndùn Name[zh_CN]=首选项 Name[zh_HK]=偏好設定 Name[zh_TW]=偏好設定 Comment=Personal preferences Comment[af]=Persoonlike voorkeure Comment[an]=Preferencias presonals Comment[ar]=التفضيلات الشخصيّة Comment[as]=ব্যক্তিগত পছন্দ Comment[ast]=Preferencies personales Comment[be]=Асабістыя перавагі Comment[be@latin]=Asabistyja nałady Comment[bg]=Лични предпочитания Comment[bn]=ব্যক্তিগত পছন্দতালিকা Comment[bn_IN]=ব্যক্তিগত পছন্দ Comment[br]=Gwellvezioù hiniennel Comment[ca]=Preferències personals Comment[ca@valencia]=Preferències personals Comment[crh]=Şahsiy tercihler Comment[cs]=Osobní volby Comment[cy]=Hoffterau personol Comment[da]=Personlige indstillinger Comment[de]=Persönliche Einstellungen Comment[dv]=އަމިއްލަ ގޮތްތައްަ Comment[dz]=རང་དོན་དགའ་གདམ་ཚུ། Comment[el]=Προσωπικές προτιμήσεις Comment[en@shaw]=𐑐𐑻𐑕𐑩𐑯𐑩𐑤 𐑐𐑮𐑧𐑓𐑼𐑩𐑯𐑕𐑩𐑟 Comment[en_CA]=Personal preferences Comment[en_GB]=Personal preferences Comment[eo]=Personaj agordoj Comment[es]=Preferencias personales Comment[et]=Isiklikud eelistused Comment[eu]=Hobespen pertsonalak Comment[fa]=ترجیحات شخصی Comment[fi]=Henkilökohtaiset valinnat Comment[fr]=Préférences personnelles Comment[fur]=Preferencis personâls Comment[fy]=Persoanlike foarkarren Comment[ga]=Sainroghanna pearsanta Comment[gl]=Preferencias persoais Comment[gn]=Rowapy Jarowa Rowaa Comment[gu]=ખાનગી પસંદગીઓ Comment[gv]=Reighyn Persoonagh ed Comment[ha]=Fifiko na Kanka Comment[he]=העדפות אישיות Comment[hi]=निजी वरीयता Comment[hr]=Osobne postavke Comment[hu]=Személyes beállítások Comment[hy]=Անհատական նախընտրանքներ Comment[id]=Preferensi pribadi Comment[ig]=Nkarachọ nkeonwe Comment[io]=Personala preferi Comment[is]=Persónulegir valmöguleikar Comment[it]=Preferenze personali Comment[ja]=ユーザ専用の設定 Comment[ka]=პერსონალური პარამეტრები Comment[kk]=Жеке баптаулар Comment[kn]=ವೈಯಕ್ತಿಕ ಆದ್ಯತೆಗಳು Comment[ko]=개인 기본 설정 Comment[ku]=Vebijêrkên takekesî Comment[ky]=Өздүк жактыруулар Comment[lt]=Asmeniniai nustatymai Comment[lv]=Personīgie iestatījumi Comment[mai]=निज वरीयतासभ Comment[mg]=Safidy manokana Comment[mk]=Лични преференции Comment[ml]=സ്വകാര്യ മുന്‍ഗണനകള്‍ Comment[mn]=Хувийн тохиргоо Comment[mr]=व्यक्तिगत प्राधान्यता Comment[nb]=Personlige brukervalg Comment[nds]=Persönliche Instellens Comment[ne]=व्यक्तिगत प्राथमिकता Comment[nl]=Persoonlijke voorkeuren Comment[nn]=Personlege innstillingar Comment[oc]=Preferéncias personalas Comment[or]=ବ୍ଯକ୍ତିଗତ ପସନ୍ଦ Comment[pa]=ਨਿੱਜੀ ਪਸੰਦ Comment[pl]=Ustawienia osobiste Comment[ps]=وکړيز غوراوي Comment[pt]=Preferências pessoais Comment[pt_BR]=Preferências pessoais Comment[ro]=Preferințe personale Comment[ru]=Персональные предпочтения Comment[si]=පුද්ගලික අභිප්‍රේත Comment[sk]=Osobné predvoľby Comment[sl]=Osebne možnosti Comment[sq]=Preferime vetjake Comment[sr]=Личне поставке Comment[sr@latin]=Lične postavke Comment[sv]=Personliga inställningar Comment[ta]=தனிப்பயன் விருப்பங்கள் Comment[te]=వ్యక్తిగత అభీష్టాలు Comment[th]=ปรับแต่งพื้นโต๊ะส่วนตัว Comment[tr]=Kişisel tercihler Comment[ug]=شەخسىي مايىللىق Comment[uk]=Особисті уподобання Comment[vi]=Tùy thích cá nhân Comment[yo]=Àwọn ìkúndùn aládàáni Comment[zh_CN]=个人首选项 Comment[zh_HK]=個人偏好設定 Comment[zh_TW]=個人偏好設定 Icon=preferences-desktop Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-development.directory0000664000175000017500000002137012625666430027302 0ustar fabiofabio[Desktop Entry] Name=Programming Name[af]=Programmering Name[an]=Programacion Name[ar]=برمجة Name[as]=প্ৰোগ্ৰামিং Name[ast]=Programación Name[be]=Праграмаваньне Name[be@latin]=Prahramavańnie Name[bg]=Разработка Name[bn]=প্রোগ্রামিং Name[bn_IN]=প্রোগ্রামিং Name[br]=Gouleviñ Name[ca]=Programació Name[ca@valencia]=Programació Name[crh]=Programlav Name[cs]=Programování Name[cy]=Rhaglennu Name[da]=Programmering Name[de]=Entwicklung Name[dv]=ޕްރޮގްރާމިންގް Name[dz]=ལས་རིམ་བཟོ་བ། Name[el]=Προγραμματισμός Name[en@shaw]=𐑐𐑮𐑴𐑜𐑮𐑨𐑥𐑦𐑙 Name[en_CA]=Programming Name[en_GB]=Programming Name[eo]=Programado Name[es]=Programación Name[et]=Programmeerimine Name[eu]=Programazioa Name[fa]=برنامه‌سازی Name[fi]=Ohjelmointi Name[fr]=Programmation Name[fur]=Programazion Name[fy]=Programmeare Name[ga]=Ríomhchlárú Name[gl]=Programación Name[gn]=Jaja Poa Name[gu]=પ્રોગ્રામીંગ Name[ha]=Yin Shirin Aiki Name[he]=תכנות Name[hi]=प्रोग्रामिंग Name[hr]=Programiranje Name[hu]=Fejlesztés Name[hy]=Ծրագրավորում Name[id]=Pemrograman Name[ig]=Programịn Name[io]=Programado Name[is]=Forritun Name[it]=Programmazione Name[ja]=プログラミング Name[ka]=პროგრამირება Name[kk]=Бағдарламалау Name[kn]=ಪ್ರೊಗ್ರಾಮಿಂಗ್ Name[ko]=개발 Name[ku]=Bernamekirin Name[ky]=Программалоо Name[lt]=Programavimas Name[lv]=Programmēšana Name[mai]=प्रोग्रामिंग Name[mg]=Famolavolavolana Name[mk]=Програмирање Name[ml]=പ്രോഗ്രാമിങ് Name[mn]=Програмчилал Name[mr]=प्रोग्रामिंग Name[ms]=Pengaturcaraan Name[nb]=Utvikling Name[nds]=Programmeeren Name[ne]=प्रोग्रामिङ Name[nl]=Ontwikkeling Name[nn]=Utvikling Name[oc]=Desvolopament Name[or]=ପ୍ରୋଗ୍ରାମିଙ୍ଗ Name[pa]=ਪਰੋਗਰਾਮਿੰਗ Name[pl]=Programowanie Name[ps]=پروګرامونه Name[pt]=Desenvolvimento Name[pt_BR]=Desenvolvimento Name[ro]=Programare Name[ru]=Программирование Name[si]=කේතරචනය Name[sk]=Programovanie Name[sl]=Programiranje Name[sq]=Programim Name[sr]=Програмирање Name[sr@latin]=Programiranje Name[sv]=Programmering Name[ta]=நிரலாக்கம் Name[te]=కార్య రూపకరణ Name[th]=เขียนโปรแกรม Name[tr]=Programlama Name[ug]=پروگراممىچىلىق Name[uk]=Програмування Name[uz@cyrillic]=Дастурлаш Name[vi]=Lập trình Name[xh]=Ukwenziwa kweenkqubo Name[yo]=Ìṣàtòjọ-ètò Name[zh_CN]=编程 Name[zh_HK]=軟件開發 Name[zh_TW]=軟體開發 Comment=Tools for software development Comment[af]=Gereedskap vir sagtewareontwikkeling Comment[an]=Ainas ta lo desarrollo de software Comment[ar]=أدوات لتطوير البرامج Comment[as]=চালনাজ্ঞান উন্নয়নৰ সামগ্ৰী Comment[ast]=Ferramientes pa desendolcu de programes Comment[be]=Інструмэнты для распрацоўкі праграм Comment[be@latin]=Pryłady prahramavańnia Comment[bg]=Инструменти за разработка на софтуер Comment[bn]=সফটওয়্যার ডেভেলপমেন্টের যন্ত্রপাতি Comment[bn_IN]=সফ্টওয়্যার ডিভেলপমেন্টের সামগ্রী Comment[br]=Binvioù da ziorren ar meziantoù Comment[ca]=Eines per a desenvolupar programari Comment[ca@valencia]=Eines per a desenvolupar programari Comment[crh]=Yazılım inkişafı içün aletler Comment[cs]=Nástroje na vývoj softwaru Comment[cy]=Offer ar gyfer datblygu meddalwedd Comment[da]=Værktøj til programudvikling Comment[de]=Werkzeuge zur Software-Entwicklung Comment[dv]=ސޮފްޓްވެޔާރ ޑެވެލޮޕް ކުރުމުގެ ޓޫލްސްތައް Comment[dz]=མཉེན་ཆས་བཟོ་ནིའི་ལག་ཆས། Comment[el]=Εργαλεία για ανάπτυξη λογισμικού Comment[en@shaw]=𐑑𐑵𐑤𐑟 𐑓𐑹 𐑕𐑪𐑓𐑑𐑢𐑺 𐑛𐑦𐑝𐑧𐑤𐑩𐑐𐑥𐑩𐑯𐑑 Comment[en_CA]=Tools for software development Comment[en_GB]=Tools for software development Comment[eo]=Iloj por programado Comment[es]=Herramientas para el desarrollo del software Comment[et]=Tarkvaraarenduse tööriistad Comment[eu]=Softwarea garatzeko tresnak Comment[fa]=ابزارهای تولید نرم‌افزار Comment[fi]=Työkaluja sovelluskehitykseen Comment[fr]=Les outils de développement de logiciels Comment[fur]=Imprescj par il svilup dal software Comment[fy]=Programma's foar it untwikkelje fan programma's Comment[ga]=Uirlisí d'fhorbairt bogearraí Comment[gl]=Ferramentas para o desenvolvemento de software Comment[gn]=Jaja Poa Comment[gu]=સોફ્ટવેર વિકાસ સાધનો Comment[ha]=Kayan aiki na ci-gaban masarrafin kwamfyuta Comment[he]=כלים לפיתוח תוכנה Comment[hi]=सॉफ्टवेयर विकास के औज़ार Comment[hr]=Alati za izradu softvera Comment[hu]=Szoftverfejlesztési eszközök Comment[hy]=Ծրագրերի մշակման գործիքներ Comment[id]=Perkakas untuk pengembangan perangkat lunak Comment[ig]=Ngwaọrụ maka ntolite Sọftwịa Comment[is]=Þróunarverkfæri Comment[it]=Strumenti per lo sviluppo software Comment[ja]=ソフトウェア開発者向けのツールです Comment[ka]=პროგრამირების ხელსაწყოები Comment[kk]=Бағдарламалауға арналған құралдар Comment[kn]=ತಂತ್ರಾಂಶ ವಿಕಸನೆಗಾಗಿನ ಉಪಕರಣಗಳು Comment[ko]=소프트웨어 개발을 위한 도구 Comment[ku]=Amûrên ji bo pêşdebirina nivîsbariyê Comment[ky]=Программа иштеп чыгуу аспаптары Comment[lt]=Programinės įrangos kūrimo įrankiai Comment[lv]=Programmatūras izstrādes rīki Comment[mai]=साफ्टवेयर विकासक लेल अओजार Comment[mg]=Fitaovana ho an'ny mpamolavola rindran'asa Comment[mk]=Алатки за развој на софтвер Comment[ml]=സോഫ്റ്റ്‌വെയര്‍ വികസനത്തിനു് ആവശ്യമുളള പണിയായുധങ്ങള്‍ Comment[mn]=Програм хөгжүүлэлийн хэрэгслүүд Comment[mr]=सॉफ्टवेयर बनवण्यासाटीचे औजार Comment[ms]=Alatan untuk pembangunan perisian Comment[nb]=Verktøy for programvareutvikling Comment[nds]=Hölpprogrammen to'r Programmkonstruktschoon Comment[ne]=सफ्टवेर विकासका लागि उपकरण Comment[nl]=Gereedschap voor software-ontwikkeling Comment[nn]=Verktøy for programvareutvikling Comment[oc]=Espleches per desvolopar de logicials Comment[or]=ସଫ୍ଟୱେର ବିକାଶ ପାଇଁ ଉପକରଣ Comment[pa]=ਸਾਫਟਵੇਅਰ ਦੇ ਡਿਵੈਲਪਮੈਂਟ ਲਈ ਟੂਲ Comment[pl]=Narzędzia do tworzenia oprogramowania Comment[ps]=د ساوترو جوړولو لپاره توکي Comment[pt]=Ferramentas para o desenvolvimento de aplicações Comment[pt_BR]=Ferramentas para desenvolvimento de software Comment[ro]=Unelte pentru dezvoltare de software Comment[ru]=Средства для разработки программ Comment[si]=මෘදුකාංග සංවර්ධන මෙවලම් Comment[sk]=Nástroje pre vývoj softvéru Comment[sl]=Orodja za razvoj programske opreme Comment[sq]=Vegla për zhvillim programesh Comment[sr]=Алатке за развој софтвера Comment[sr@latin]=Alatke za razvoj softvera Comment[sv]=Verktyg för programutveckling Comment[ta]=மென்பொருள் உற்பத்திக்கான கருவிகள் Comment[te]=కార్యరూపకాలని తయారు చేయుటకు పనిముట్లు Comment[th]=เครื่องมือพัฒนาซอฟต์แวร์ Comment[tr]=Yazılım geliştirme için araçlar Comment[ug]=يۇمشاق دېتال ئىجادىيەت قوراللىرى Comment[uk]=Засоби розробки програм Comment[uz@cyrillic]=Дастурлаш учун воситалар Comment[vi]=Công cụ phát triển phần mềm Comment[xh]=Izixhobo zokuphuhlisa ubucukubhede bekhompyutha Comment[yo]=Àwọn irinṣẹ́ fún ìdàgbàsókè ẹ̀yà-ará kọ̀ǹpútà àìfojúrí Comment[zh_CN]=软件开发工具 Comment[zh_HK]=軟件開發工具 Comment[zh_TW]=軟體開發工具 Icon=applications-development Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-other.directory0000664000175000017500000002334012625666430026100 0ustar fabiofabio[Desktop Entry] Name=Other Name[af]=Ander Name[an]=Atras Name[ar]=أخرى Name[as]=অন্যান্য Name[ast]=Otres Name[be]=Іншыя Name[be@latin]=Inšyja Name[bg]=Други Name[bn]=অন্যান্য Name[bn_IN]=অন্যান্য Name[br]=Arloadoù all Name[ca]=Altres Name[ca@valencia]=Altres Name[crh]=Diger Name[cs]=Ostatní Name[cy]=Eraill Name[da]=Andre Name[de]=Sonstige Name[dv]=އެހެނިހެން Name[dz]=གཞན། Name[el]=Άλλα Name[en@shaw]=𐑳𐑞𐑼 Name[en_CA]=Other Name[en_GB]=Other Name[eo]=Alia Name[es]=Otras Name[et]=Muu Name[eu]=Bestelakoak Name[fa]=غیره Name[fi]=Muut Name[fr]=Autre Name[fur]=Altri Name[fy]=Oare programma's Name[ga]=Eile Name[gl]=Outras Name[gn]=Amboae Name[gu]=અન્ય Name[gv]=Elley Name[ha]=Waccan Name[he]=אחר Name[hi]=अन्य Name[hr]=Ostalo Name[hu]=Egyéb Name[hy]=Այլ Name[id]=Lainnya Name[ig]=Ọzọ Name[io]=Altra Name[is]=Annað Name[it]=Altro Name[ja]=その他 Name[ka]=სხვა Name[kk]=Басқалар Name[kn]=ಇತರೆ Name[ko]=기타 Name[ku]=Yên din Name[ky]=Башкалар Name[lt]=Kitos Name[lv]=Citas lietotnes Name[mai]=आन Name[mg]=Hafa Name[mk]=Други Name[ml]=മറ്റുളളവ Name[mn]=Бусад Name[mr]=अन्य Name[ms]=Lain-lain Name[nb]=Annet Name[nds]=Annere Name[ne]=अन्य Name[nl]=Overig Name[nn]=Anna Name[oc]=Autre Name[or]=ଅନ୍ଯାନ୍ଯ Name[pa]=ਹੋਰ Name[pl]=Inne Name[ps]=نور Name[pt]=Outras Name[pt_BR]=Outros Name[ro]=Diverse Name[ru]=Прочие Name[rw]=Ikindi Name[si]=වෙනත් Name[sk]=Ostatné Name[sl]=Ostalo Name[sq]=Tjetër Name[sr]=Остало Name[sr@latin]=Ostalo Name[sv]=Övrigt Name[ta]=மற்றவை Name[te]=ఇతర Name[th]=อื่นๆ Name[tr]=Diğer Name[ug]=باشقا Name[uk]=Інші Name[uz@cyrillic]=Бошқа Name[vi]=Khác Name[xh]=Ezinye Name[yo]=Òmíràn Name[zh_CN]=其它 Name[zh_HK]=其他 Name[zh_TW]=其他 Comment=Applications that did not fit in other categories Comment[af]=Toepassings wat nie in ander kategorieë pas nie Comment[an]=Aplicacions que no dentran en denguna atra categoria Comment[ar]=تطبيقات لا تندرج تحت الفئات الأخرى Comment[as]=অন্যান্য শ্ৰেণীত অন্তৰ্গত নকৰা অনুপ্ৰয়োগ Comment[ast]=Aplicaciones que nun encaxen n'otres estayes Comment[be]=Дастасаваньні, якія ня трапілі ў іншыя катэгорыі Comment[be@latin]=Prahramy, jakija nie ŭvajšli ŭ inšyja kategoryi Comment[bg]=Програми, които не принадлежат към друга категория Comment[bn]=অ্যাপ্লিকেশন যেগুলো অন্য কোন শ্রেণীভূক্ত নয় Comment[bn_IN]=অন্যান্য শ্রেণীর মধ্যে অন্তর্গত না করা অ্যাপ্লিকেশন Comment[br]=Arloadoù n'int ket stag ouzh ur rummad all ebet Comment[ca]=Aplicacions que no encaixaven en d'altres categories Comment[ca@valencia]=Aplicacions que no encaixaven en d'altres categories Comment[crh]=Diger ulamlarğa yaraşmağan uyğulamalar Comment[cs]=Aplikace nepatřící do jiných kategorií Comment[cy]=Rhaglenni nad ydynt yn ffitio yng nghategorïau eraill Comment[da]=Programmer som ikke passer i andre katagorier Comment[de]=Anwendungen welche in keine andere Kategorie eingeordnet werden können Comment[dv]=އެހެން ކެޓަގަރީތަކާ ނުގުޅޭ އެޕްލިކޭޝަންތަށް Comment[dz]=དབྱེ་ཁག་གཞན་ནང་ ཚུད་སྒྲིག་མེད་པའི་གློག་རིམ། Comment[el]=Εφαρμογές που δεν ταιριάζουν στις άλλες κατηγορίες Comment[en@shaw]=𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟 𐑞𐑨𐑑 𐑛𐑦𐑛 𐑯𐑪𐑑 𐑓𐑦𐑑 𐑦𐑯 𐑳𐑞𐑼 𐑒𐑨𐑑𐑩𐑜𐑪𐑮𐑰𐑟 Comment[en_CA]=Applications that do not fit in other categories Comment[en_GB]=Applications that did not fit in other categories Comment[eo]=Aplikaĵoj neagordaj aliajn kategoriojn Comment[es]=Aplicaciones que no entran en otras categorías Comment[et]=Rakendused, mis ei sobinud teistesse kategooriatesse Comment[eu]=Sailkatu ezin diren aplikazioak Comment[fa]=برنامه‌هایی که در مقولات دیگر جا نمی‌گیرند Comment[fi]=Muihin luokkiin sopimattomat sovellukset Comment[fr]=Les applications qui ne conviennent à aucune autre catégorie Comment[fur]=Aplicazions ch'a no jentrin ta chês altres categoriis Comment[fy]=Applicaties die net yn oare categorien passe Comment[ga]=Feidhmchláir nár oiriúnaigh i gcatagóirí eile Comment[gl]=Aplicativos que non se axustan a outras categorías Comment[gn]=Amboae Regua Jaekaa Comment[gu]=કાર્યક્રમો કે જે અન્ય વર્ગોમાં નહિં બંધબેસે Comment[gv]=Cha row yn claaghyn goll stiagh ayns Comment[ha]=Shiryoyin Ayuka waɗanda basu dace cikin wasu jinsi ba Comment[he]=יישומים שלא התאימו בקטגוריות אחרות Comment[hi]=अनुप्रयोग जो अन्य श्रेणी में सटीक नहीं बैठा Comment[hr]=Programi koji ne pristaju u druge kategorije Comment[hu]=Más kategóriákba be nem sorolható alkalmazások Comment[hy]=Ծրագրեր, որոնք այլ կատեգորիաներին չեն համապատասխանում Comment[id]=Aplikasi yang tidak termasuk dalam kategori manapun Comment[ig]=Usoro iheomume na-abanyeghị n'ọ́nụ́ ndị ọzọ Comment[is]=Forrit sem passa ekki í aðra flokka Comment[it]=Applicazioni che non rientrano in altre categorie Comment[ja]=他のカテゴリにあてはまらないアプリケーション Comment[ka]=პროგრამები რომლებიც არც ერთ კატეგორიაში არ შედიან Comment[kk]=Ешқандай санатқа жатпайтын қолданбалар Comment[kn]=ಬೇರೆ ವರ್ಗಗಳಿಗೆ ಸೇರದೇ ಇರುವಂತಹ ಅನ್ವಯಗಳು Comment[ko]=어떤 범주에도 해당되지 않는 프로그램 Comment[ku]=Sepanên ku nakevin kategoriyên din Comment[ky]=Эч бир категорияга туура келбеген иштемелер Comment[lt]=Programos, kurios netiko kitose kategorijose Comment[lv]=Lietotnes, kas neiederas nevienā citā kategorijā Comment[mai]=अनुप्रयोगसभ जे आन श्रेणीमे सटीक नहि छला Comment[mg]=Rindran'asa izay tsy tafiditra anaty sokajy hafa Comment[mk]=Апликации кои што не влегуваат во ниедна категорија Comment[ml]=മറ്റു് വിഭാഗങ്ങളില്‍ ഉള്‍‌പ്പെടാത്ത പ്രയോഗങ്ങള്‍ Comment[mn]=Бусад ангилалд тохирохгүй програмууд Comment[mr]=उपकरणं जे दुसरया कुठल्याही गटांत बसले नाही Comment[ms]=Aplikasi yang tak sesuai bagi kategori lain Comment[nb]=Programmer som ikke passer i andre kategorier Comment[nds]=Programme de keene Schieblade passen Comment[ne]=अन्य कोटिमा नमिलेका अनुप्रयोग Comment[nl]=Toepassingen die niet in een andere categorie ondergebracht kunnen worden Comment[nn]=Program som ikkje passar i andre kategoriar Comment[oc]=Logicials que dintran pas dins las autras categorias Comment[or]=ପ୍ରୟୋଗ ଯାହାକି ଅନ୍ଯ କୌଣସି ଶ୍ରେଣୀ ସହିତ ଖାପ ଖାଇଲା ନାହିଁ Comment[pa]=ਕਾਰਜ, ਜੋ ਕਿ ਕਿਸੇ ਵਰਗ ਵਿੱਚ ਨਹੀਂ ਆਉਦੇ। Comment[pl]=Programy niepasujące do innych kategorii Comment[ps]=هغه کاريالونه چې په نورو ټولېو کې نه راځي Comment[pt]=Aplicações que não se enquadravam em nenhuma outra categoria Comment[pt_BR]=Aplicativos que não se encaixam em outras categorias Comment[ro]=Aplicații ce nu se încadrează în alte categorii Comment[ru]=Приложения, не подпадающие ни под какие категории Comment[si]=වෙනත් ප්‍රභේද වලට අයත් නොවු යෙදුම් Comment[sk]=Aplikácie, ktoré nezapadajú do ostatných kategórií Comment[sl]=Programi, ki ne sodijo v druge kategorije Comment[sq]=Programe që nuk gjejnë vend në kategori të tjera Comment[sr]=Програми који не припадају осталим категоријама Comment[sr@latin]=Programi koji ne pripadaju ostalim kategorijama Comment[sv]=Program som inte passar in i någon annan kategori Comment[ta]=இதர இனங்களில் பொருந்தாத பயன்பாடுகள் Comment[te]=ఇతర విభాగాలలో ఇమడని కార్యక్షేత్రాలు Comment[th]=โปรแกรมที่ไม่อยู่ในหมวดอื่นๆ Comment[tr]=Diğer sınıflandırmalara girmeyen uygulamalar Comment[ug]=باشقا تۈرلەرگە تەۋە بولمىغان پروگراممىلار Comment[uk]=Програми, що не належать до жодної категорії Comment[uz@cyrillic]=Ҳеч қайси туркумга кирмайдиган дастурлар Comment[vi]=Chương trình chưa phân loại Comment[xh]=Iinkqubo ezingangeniyo kwezinye iindidi Comment[yo]=Àwọn ìsàmúlò-ètò tí kò bá àwọn ọ̀wọ́ mìíràn mu Comment[zh_CN]=无法分入其它类别的应用程序 Comment[zh_HK]=不符合其它分類的軟件 Comment[zh_TW]=不符合其它分類的軟體 Icon=applications-other Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-system-tools.directory0000664000175000017500000002332512625666430027444 0ustar fabiofabio[Desktop Entry] Name=System Tools Name[af]=Stelselgereedskap Name[an]=Ainas d'o sistema Name[ar]=أدوات النّظام Name[as]=ব্যৱস্থাপ্ৰণালীৰ বিভিন্ন সৰঞ্জাম Name[ast]=Ferramientes del Sistema Name[be]=Сыстэмныя інструмэнты Name[be@latin]=Systemnaje pryładździe Name[bg]=Системни инструменти Name[bn]=সিস্টেম টুল Name[bn_IN]=সিস্টেমের বিভিন্ন সরঞ্জাম Name[br]=Binvioù reizhiad Name[ca]=Eines del sistema Name[ca@valencia]=Eines del sistema Name[crh]=Sistem Aletleri Name[cs]=Systémové nástroje Name[cy]=Offer System Name[da]=Systemværktøjer Name[de]=Systemwerkzeuge Name[dv]=ސިސްޓަމް ޓޫލްތައް Name[dz]=རིམ་ལུགས་ལག་ཆས། Name[el]=Εργαλεία συστήματος Name[en@shaw]=𐑕𐑦𐑕𐑑𐑩𐑥 𐑑𐑵𐑤𐑟 Name[en_CA]=System Tools Name[en_GB]=System Tools Name[eo]=Sistemaj iloj Name[es]=Herramientas del sistema Name[et]=Süsteemi tööriistad Name[eu]=Sistemaren tresnak Name[fa]=ابزارهای سیستم Name[fi]=Järjestelmätyökalut Name[fr]=Outils système Name[fur]=Imprescj di sisteme Name[fy]=Systeem Tapassingen Name[ga]=Uirlisí Córais Name[gl]=Ferramentas do sistema Name[gu]=સિસ્ટમ સાધનો Name[gv]=Claaghyn system Name[ha]=Kayan Aikin Na'ura Name[he]=כלי מערכת Name[hi]=तंत्र औज़ार Name[hr]=Alati sustava Name[hu]=Rendszereszközök Name[hy]=Համակարգի գործիքներ Name[id]=Perkakas Sistem Name[ig]=Ngwaọrụ sistem Name[io]=Sistemala Utensili Name[is]=Kerfistól Name[it]=Strumenti di sistema Name[ja]=システムツール Name[ka]=სისტემური ხელსაწყოები Name[kk]=Жүйелік саймандар Name[kn]=ವ್ಯವಸ್ಥೆಯ ಉಪಕರಣಗಳು Name[ko]=시스템 도구 Name[ku]=Amûrên Pergalê Name[ky]=Системалык аспаптар Name[lt]=Sistemos įrankiai Name[lv]=Sistēma rīki Name[mai]=सिस्टम अओजार Name[mg]=Fitaovan'ny rafitra Name[mk]=Системски алатки Name[ml]=സിസ്റ്റത്തിലെ പണിയായുധങ്ങള്‍ Name[mn]=Системийн хэрэгслүүд Name[mr]=यंत्रणे संबंधित औजार Name[ms]=Alatan Sistem Name[nb]=Systemverktøy Name[nds]=Systemprogramme Name[ne]=प्रणाली उपकरण Name[nl]=Systeemgereedschap Name[nn]=Systemverktøy Name[oc]=Espleches sistèma Name[or]=ତନ୍ତ୍ର ଉପକରଣ Name[pa]=ਸਿਸਟਮ ਟੂਲ Name[pl]=Narzędzia systemowe Name[ps]=غونډال توکي Name[pt]=Ferramentas de Sistema Name[pt_BR]=Sistema Name[ro]=Utilitare de sistem Name[ru]=Системные Name[si]=පද්දති මෙවලම් Name[sk]=Systémové nástroje Name[sl]=Sistemska orodja Name[sq]=Vegla Sistemi Name[sr]=Системске алатке Name[sr@latin]=Sistemske alatke Name[sv]=Systemverktyg Name[ta]=கணிப்பொறிக் கருவிகள் Name[te]=వ్యవస్థ పనిముట్లు Name[th]=เครื่องมือระบบ Name[tr]=Sistem Araçları Name[ug]=سىستېما قوراللىرى Name[uk]=Системні утиліти Name[uz@cyrillic]=Тизим воситалари Name[vi]=Công cụ hệ thống Name[xh]=Izixhobo Zeenkqubo Name[yo]=Àwọn Irinṣẹ́ Ètò Kọ̀ǹpútà Name[zh_CN]=系统工具 Name[zh_HK]=系統工具 Name[zh_TW]=系統工具 Comment=System configuration and monitoring Comment[af]=Stelselopstelling en monitering Comment[an]=Configuracion y monitorizacion d'o sistema Comment[ar]=إعداد و مراقبة النّظام Comment[as]=ব্যৱস্থাপ্ৰণালীৰ বৈশিষ্ট্যৰ বিন্যাস এবং নিৰীক্ষণ Comment[ast]=Configuración y monitorización del sistema Comment[be]=Наладка сыстэмы і маніторынг Comment[be@latin]=Manitorynh i kanfihuracyja systemy Comment[bg]=Следене и настройване на системата Comment[bn]=সিস্টেম কনফিগারেশন এবং মনিটরিং Comment[bn_IN]=সিস্টেমের বৈশিষ্ট্য কনফিগারেশন এবং নিরীক্ষণ Comment[br]=Neuziadur hag eveshaerezh ar reizhiad Comment[ca]=Configuració i supervisió del sistema Comment[ca@valencia]=Configuració i supervisió del sistema Comment[crh]=Sistem ayarlaması ve közetimi Comment[cs]=Nastavení a sledování systému Comment[cy]=Cyflunio a monitro system Comment[da]=Systemkonfiguration og -overvågning Comment[de]=Systemkonfiguration und -überwachung Comment[dv]=ސިސްޓަމް ކޮންފިގަރ ކުރުމާއި މޮނިޓަރ ކުރުން Comment[dz]=རིམ་ལུགས་རིམ་སྒྲིག་དང་ལྟ་རྟོག། Comment[el]=Ρύθμιση και έλεγχος συστήματος Comment[en@shaw]=𐑕𐑦𐑕𐑑𐑩𐑥 𐑒𐑩𐑯𐑓𐑦𐑜𐑘𐑼𐑱𐑖𐑩𐑯 𐑯 𐑥𐑭𐑯𐑩𐑑𐑻𐑦𐑙 Comment[en_CA]=System configuration and monitoring Comment[en_GB]=System configuration and monitoring Comment[eo]=Agordi kaj kontroli sistemon Comment[es]=Configuración y monitorización del sistema Comment[et]=Süsteemi seadistamine ja jälgimine Comment[eu]=Sistemaren konfigurazioa eta monitorizazioa Comment[fa]=پیکربندی و پایشگری سیستم Comment[fi]=Järjestelmäasetukset ja -seuranta Comment[fr]=Configuration et surveillance système Comment[fur]=Configurazion e monitoragjo di sisteme Comment[fy]=Systeem konfiguraasje en kontrolearje Comment[ga]=Cumraíocht agus monatóireacht an chórais Comment[gl]=Configuración e monitorización do sistema Comment[gu]=સિસ્ટમ રૂપરેખાંકન અને મોનિટરીંગ Comment[gv]=Caghlaaghyn System as Comment[ha]=Canza tsari da kulawan na'ura Comment[he]=הגדרה וניטור של המערכת Comment[hi]=तंत्र विन्यास व निरीक्षण Comment[hr]=Konfiguracija i nadgledanje sustava Comment[hu]=Rendszerbeállítás és megfigyelés Comment[hy]=Համակարգի կարգավորում և վերահսկում Comment[id]=Konfigurasi serta pengawasan sistem Comment[ig]=Nhazi na ilereanya nke sistem Comment[is]=Kerfisstillingar og vöktun Comment[it]=Configurazione e monitoraggio del sistema Comment[ja]=システムの設定と監視を行うプログラムです Comment[ka]=სისტემის კონფიგურაცია და მონიტორინგი Comment[kk]=Жүйені баптау мен бақылау Comment[kn]=ವ್ಯವಸ್ಥೆಯ ಸಂರಚನೆ ಹಾಗು ಪರಿವೀಕ್ಷಣೆ Comment[ko]=시스템 설정과 감시 Comment[ku]=Avakirin û şopandina pergalê Comment[ky]=Системаны конфигурациялоо жана аны байкоо аспаптары Comment[lt]=Sistemos konfigūravimas ir stebėjimas Comment[lv]=Sistēmas konfigurācija un pārraudzība Comment[mai]=सिस्टम बिन्यास आ निरीक्षण Comment[mg]=Fanefena sy fanaraha-maso ny rafitra Comment[mk]=Конфигурација и надгледување на системот Comment[ml]=സിസ്റ്റത്തിന്റെ ക്രമീകരണവും നിരീക്ഷണവും Comment[mn]=Системийн тохиргоо болон хяналт Comment[mr]=यंत्रणेचे सुसुत्रीकरण व संचलन Comment[ms]=Konfigurasi dan monitor sistem Comment[nb]=Systemkonfigurasjon og overvåking Comment[nds]=Systemkonfiguratschoon un Oppassens Comment[ne]=प्रणाली कन्फिगरेसन र अनुगमन Comment[nl]=Systeemconfiguratie en -controle Comment[nn]=Oppsett og overvaking av systemet Comment[oc]=Configuracion e susvelhança del sistèma Comment[or]=ତନ୍ତ୍ର ବିନ୍ଯାସ ଏବଂ ନିରୀକ୍ଷଣ Comment[pa]=ਸਿਸਟਮ ਸੰਰਚਨਾ ਅਤੇ ਨਿਗਰਾਨੀ Comment[pl]=Monitorowanie i konfiguracja systemu Comment[ps]=د غونډال سازونه او ليدنه Comment[pt]=Configuração de sistema e monitorização Comment[pt_BR]=Configuração e monitoramento do sistema Comment[ro]=Configurare și monitorizare a sistemului Comment[ru]=Средства конфигурации и мониторинга системы Comment[si]=පද්දති සුසර කිරිම සහ නිරික්ෂණය Comment[sk]=Systémová konfigurácia a sledovanie Comment[sl]=Nastavitve in nadziranje sistema Comment[sq]=Konfigurim dhe vëzhgim sistemi Comment[sr]=Подешавање и праћење система Comment[sr@latin]=Podešavanje i praćenje sistema Comment[sv]=Systemkonfiguration och systemövervakning Comment[ta]=கணிப்பொறி அமைப்பு மற்றும் கண்காணித்தல் Comment[te]=వ్యవస్థ రూపకరణ మరియు తనిఖీ Comment[th]=ตั้งค่าและเฝ้าดูระบบ Comment[tr]=Sistem yapılandırma ve izleme Comment[ug]=سىستېما سەپلەش ۋە كۆزىتىش Comment[uk]=Засоби налаштовування та контролю системи Comment[uz@cyrillic]=Тизимни мослаш ва кузатиш Comment[vi]=Cấu hình và theo dõi hệ thống Comment[xh]=Umiselo-nkqubo lwendlela emisiweyo kunye nokuhlola Comment[yo]=Ìṣàmójútó àti àtòpọ̀ ètò kọ̀ǹpútà Comment[zh_CN]=系统配置和监视 Comment[zh_HK]=系統配置及監察 Comment[zh_TW]=系統配置及監察 Icon=applications-system Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-internet-and-network.directory0000664000175000017500000002030612625666430031035 0ustar fabiofabio[Desktop Entry] Name=Internet and Network Name[af]=Internet en netwerk Name[an]=Internet y rete Name[ar]=الإنترنت والشبكة Name[as]=ইন্টাৰনেট ও নেটৱৰ্ক Name[ast]=Internet y rede Name[be]=Інтэрнэт і сетка Name[be@latin]=Internet i sietka Name[bg]=Интернет и мрежа Name[bn]=ইন্টারনেট ও নেটওয়ার্ক Name[bn_IN]=ইন্টারনেট ও নেটওয়ার্ক Name[br]=Kenrouedad ha Rouedad Name[ca]=Internet i xarxa Name[ca@valencia]=Internet i xarxa Name[crh]=İnternet ve Ağ Name[cs]=Internet a síť Name[da]=Internet og netværk Name[de]=Internet und Netzwerk Name[dv]=އިންޓަރނެޓް އަދި ނެޓްވޯރކް Name[dz]=ཨིན་ཊར་ནེཊི་དང་ཡོངས་འབྲེལ་ Name[el]=Διαδίκτυο και δίκτυο Name[en@shaw]=·𐑦𐑯𐑑𐑼𐑯𐑧𐑑 𐑯 𐑯𐑧𐑑𐑢𐑻𐑒 Name[en_GB]=Internet and Network Name[eo]=Interreto kaj reto Name[es]=Internet y red Name[et]=Internet ja võrk Name[eu]=Internet eta sarea Name[fa]=اینترنت و شبکه Name[fi]=Internet ja verkko Name[fr]=Internet et réseau Name[fur]=Internet e Rêt Name[fy]=Ynternet en Netwurk Name[ga]=Idirlíon agus Líonra Name[gl]=Internet e redes Name[gu]=ઈન્ટરનેટ અને નેટવર્ક Name[gv]=Eddyr-voggyl as toilley reddyn Name[he]=אינטרט ורשת Name[hi]=इंटरनेट व संजाल Name[hr]=Internet i mreža Name[hu]=Internet és hálózat Name[hy]=Ինտերնետ և Ցանց Name[id]=Internet dan Jaringan Name[io]=Interreto e Reto Name[is]=Internet og netkerfi Name[it]=Internet e rete Name[ja]=インターネットとネットワーク Name[kk]=Интернет пен желі Name[kn]=ಅಂತರಜಾಲ ಹಾಗು ಜಾಲಬಂಧ Name[ko]=인터넷과 네트워크 Name[ku]=Înternet û Tor Name[lt]=Internetas ir tinklas Name[lv]=Internets un tīkli Name[mai]=इंटरनेट आ संजाल Name[mk]=Интернет и мрежа Name[ml]=ഇന്റര്‍നെറ്റും ശൃംഖലയും Name[mn]=Интернет болон сүлжээ Name[mr]=महाजाळ व संजाळ Name[nb]=Internett og nettverk Name[nds]=Innernet un Netwark Name[ne]=इन्टरनेट र सञ्जाल Name[nl]=Internet en netwerk Name[nn]=Internett og nettverk Name[oc]=Internet e ret Name[or]=ଇନ୍ଟରନେଟ ଏବଂ ନେଟୱର୍କ Name[pa]=ਇੰਟਰਨੈੱਟ ਅਤੇ ਨੈੱਟਵਰਕ Name[pl]=Internet i sieć Name[ps]=اېنټرنېټ او ځال Name[pt]=Internet e Rede Name[pt_BR]=Internet e rede Name[ro]=Internet și rețea Name[ru]=Интернет и сеть Name[si]=අන්තර්ජාලය සහ ජාලය Name[sk]=Internet a sieť Name[sl]=Splet in omrežje Name[sq]=Internet dhe Rrjet Name[sr]=Интернет и мрежа Name[sr@latin]=Internet i mreža Name[sv]=Internet och nätverk Name[ta]=இணையம் மற்றும் வலையமைப்பு Name[te]=ఇంటర్నెట్ మరియు నెట్వర్క్ Name[th]=อินเทอร์เน็ตและเครือข่าย Name[tr]=İnternet ve Ağ Name[ug]=تۇتاش تور ۋە تور Name[uk]=Інтернет та мережа Name[uz@cyrillic]=Интернет ва тармоқ Name[vi]=Internet và mạng Name[zh_CN]=互联网和网络 Name[zh_HK]=互聯網及網絡 Name[zh_TW]=網際網路及網路 Comment=Network-related settings Comment[af]=Netwerkverwante instellings Comment[an]=Propiedatz arredol d'o rete Comment[ar]=الإعدادات المتعلّقة بالشبكة Comment[as]=নেটৱৰ্ক সংক্ৰান্ত বৈশিষ্ট্য Comment[ast]=Opciones rellacionaes col trabayu en rede Comment[be]=Настаўленьні сеткі Comment[be@latin]=Sietkavyja nałady Comment[bg]=Настройки за мрежата Comment[bn]=নেটওয়ার্ক সম্পর্কিত সেটিংস Comment[bn_IN]=নেটওয়ার্ক সংক্রান্ত বৈশিষ্ট্য Comment[br]=Arventennoù ar rouedad Comment[ca]=Paràmetres de xarxa Comment[ca@valencia]=Paràmetres de xarxa Comment[crh]=Ağ-alâqalı tesbitler Comment[cs]=Nastavení související se sítí Comment[da]=Netværksrelaterede indstillinger Comment[de]=Netzwerkbezogene Einstellungen Comment[dv]=ނެޓްވޯރކާގުޅޭ ސެޓިންގްތައް Comment[dz]=ཡོང་འབྲེལ་དང་འབྲེལ་བའི་སྒྲིག་སྟངས་ Comment[el]=Ρυθμίσεις για το δίκτυο Comment[en@shaw]=𐑯𐑧𐑑𐑢𐑻𐑒-𐑮𐑦𐑤𐑱𐑑𐑩𐑛 𐑕𐑧𐑑𐑦𐑙𐑟 Comment[en_GB]=Network-related settings Comment[eo]=Retrilata agordaro Comment[es]=Propiedades relacionadas con la red Comment[et]=Võrguga seotud sätted Comment[eu]=Sareko ezarpenak Comment[fa]=تنظیمات مربوط به شبکه Comment[fi]=Verkkoon liittyvät asetukset Comment[fr]=Paramètres concernant le réseau Comment[fur]=Impostazions de rêt Comment[fy]=Netwurk-relativearre ynstellings Comment[ga]=Socruithe líonra-gaolta Comment[gl]=Configuracións relacionadas coa rede Comment[gu]=નેટવર્ક-સંબંધિત સુયોજનો Comment[gv]=Reighyn son eddyrvoagle as toilley reddyn Comment[he]=הגדרות רשת Comment[hi]=संजाल संबंधित सेटिंग Comment[hr]=Mrežne postavke Comment[hu]=Hálózattal kapcsolatos beállítások Comment[hy]=Ցանցային հատկություններ Comment[id]=Pengaturan jaringan Comment[io]=Retala setuesi Comment[is]=Netkerfistengdar stillingar Comment[it]=Impostazioni relative alla rete Comment[ja]=ネットワークに関連する設定を行います Comment[kk]=Желілік баптаулар Comment[kn]=ಜಾಲಬಂಧ-ಸಂಬಂಧಿತ ಸಿದ್ಧತೆಗಳು Comment[ko]=네트워크와 관련된 설정을 합니다 Comment[ku]=Mîhengên têkildarî torê Comment[lt]=Su tinklu susiję nustatymai Comment[lv]=Ar tīklu saistīti iestatījumi Comment[mai]=संजाल संबंधित सेटिंग Comment[mk]=Поставувања кои се однесуваат на мрежа Comment[ml]=ശൃംഖലയുമായി ബന്ധപ്പെട്ട സജ്ജീകരണങ്ങള്‍ Comment[mn]=Сүлжээтэй холбоотой тохиргоо Comment[mr]=संजाळ-संबंधी संयोजना Comment[nb]=Nettverksrelaterte innstillinger Comment[nds]=Netwarksinstellens Comment[ne]=सञ्जाल सम्बन्धि सेटिङ Comment[nl]=Netwerkgerelateerde instellingen Comment[nn]=Nettverksrelaterte innstillingar Comment[oc]=Paramètres en relacion amb la ret Comment[or]=ନେଟୱର୍କ ସମ୍ପର୍କୀୟ ବିନ୍ୟାସ Comment[pa]=ਨੈੱਟਵਰਕ ਨਾਲ ਸਬੰਧਤ ਸਥਾਪਨ Comment[pl]=Ustawienia związane z siecią Comment[ps]=ځال پورې تړلې امستنې Comment[pt]=Definições relacionadas com a rede Comment[pt_BR]=Configurações de rede Comment[ro]=Opțiuni de rețea Comment[ru]=Параметры, относящиеся к настройке сети Comment[si]=ජාලය සම්බන්ද සැකසුම් Comment[sk]=Nastavenia súvisiace so sieťou Comment[sl]=Nastavitve omrežja Comment[sq]=Rregullime lidhur me Rrjetin Comment[sr]=Мрежна подешавања Comment[sr@latin]=Mrežna podešavanja Comment[sv]=Nätverksrelaterade inställningar Comment[ta]=வலையமைப்பு சார்ந்த வடிவமைப்புகள் Comment[te]=నెట్వర్క్ బంధిత అమరికలు Comment[th]=ตั้งค่าเกี่ยวกับเครือข่าย Comment[tr]=Ağ ile ilgili ayarlar Comment[ug]=تورغا مۇناسىۋەتلىك تەڭشەك Comment[uk]=Параметри, що відносяться до налаштовування мережі Comment[uz@cyrillic]=Тармоққа оид мосламалар Comment[vi]=Thiết lập mạng Comment[zh_CN]=网络相关的设置 Comment[zh_HK]=網絡相關設定 Comment[zh_TW]=網路相關設定 Icon=preferences-system-network Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-game.directory0000664000175000017500000001414612625666430025674 0ustar fabiofabio[Desktop Entry] Name=Games Name[af]=Speletjies Name[an]=Chuegos Name[ar]=ألعاب Name[as]=খেলা Name[ast]=Xuegos Name[be]=Гульні Name[be@latin]=Hulni Name[bg]=Игри Name[bn]=খেলা Name[bn_IN]=খেলা Name[br]=C'hoarioù Name[ca]=Jocs Name[ca@valencia]=Jocs Name[crh]=Oyunlar Name[cs]=Hry Name[cy]=Gemau Name[da]=Spil Name[de]=Spiele Name[dv]=ގޭމްސް Name[dz]=རྩེད་རིགས། Name[el]=Παιχνίδια Name[en@shaw]=𐑜𐑱𐑥𐑟 Name[en_CA]=Games Name[en_GB]=Games Name[eo]=Ludoj Name[es]=Juegos Name[et]=Mängud Name[eu]=Jokoak Name[fa]=بازی‌ها Name[fi]=Pelit Name[fr]=Jeux Name[fur]=Zûcs Name[fy]=Spultsjes Name[ga]=Cluichí Name[gl]=Xogos Name[gn]=Jaugaa Name[gu]=રમતો Name[gv]=Gammanyn Name[ha]=Wasanni Name[he]=משחקים Name[hi]=खेल Name[hr]=Igre Name[hu]=Játékok Name[hy]=Խաղեր Name[id]=Permainan Name[ig]=Egwuregwu Name[io]=Ludi Name[is]=Leikir Name[it]=Giochi Name[ja]=ゲーム Name[ka]=თამაშები Name[kk]=Ойындар Name[kn]=ಆಟಗಳು Name[ko]=게임 Name[ku]=Lîstik Name[ky]=Оюндар Name[lt]=Žaidimai Name[lv]=Spēles Name[mai]=खेल Name[mg]=Lalao Name[mk]=Игри Name[ml]=കളികള്‍ Name[mn]=Тоглоом Name[mr]=खेळ Name[ms]=Permainan Name[nb]=Spill Name[nds]=Speelen Name[ne]=खेल Name[nl]=Spelletjes Name[nn]=Spel Name[oc]=Jòcs Name[or]=ଖେଳ Name[pa]=ਖੇਡਾਂ Name[pl]=Gry Name[ps]=لوبې Name[pt]=Jogos Name[pt_BR]=Jogos Name[ro]=Jocuri Name[ru]=Игры Name[si]=ක්‍රිඩා Name[sk]=Hry Name[sl]=Igre Name[sq]=Lojra Name[sr]=Игре Name[sr@latin]=Igre Name[sv]=Spel Name[ta]=விளையாட்டுகள் Name[te]=ఆటలు Name[th]=เกม Name[tr]=Oyunlar Name[ug]=ئويۇنلار Name[uk]=Ігри Name[uz@cyrillic]=Ўйинлар Name[vi]=Trò chơi Name[xh]=Imidlalo Name[yo]=Àwọn Ayò Name[zh_CN]=游戏 Name[zh_HK]=遊戲 Name[zh_TW]=遊戲 Comment=Games and amusements Comment[af]=Speletjies en vermaak Comment[an]=Chuegos y distraccions Comment[ar]=الألعاب و التسلية Comment[as]=খেলা ও বিনোদন Comment[ast]=Xuegos y entretenimientu Comment[be]=Гульні і забавы Comment[be@latin]=Hulni j zabavy Comment[bg]=Игри и забавления Comment[bn]=খেলা ও বিনোদন Comment[bn_IN]=খেলা ও বিনোদন Comment[br]=C'hoarioù ha diduelloù Comment[ca]=Jocs i entreteniments Comment[ca@valencia]=Jocs i entreteniments Comment[crh]=Oyunlar ve eglenceler Comment[cs]=Hry a zábava Comment[cy]=Gemau a difyrrwch Comment[da]=Spil og underholdning Comment[de]=Spiel und Spaß Comment[dv]=ގޭމުތަކާއި މުނިފޫހިފިލުވުންތައް Comment[dz]=རྩེད་རིགས་དང་ དགོད་བྲ་ཚུ། Comment[el]=Παιχνίδια και διασκέδαση Comment[en@shaw]=𐑜𐑱𐑥𐑟 𐑯 𐑨𐑥𐑿𐑟𐑥𐑩𐑯𐑑𐑕 Comment[en_CA]=Games and amusements Comment[en_GB]=Games and amusements Comment[eo]=Ludoj kaj amuzoj Comment[es]=Juegos y distracciones Comment[et]=Mängud ja meelelahutus Comment[eu]=Jokoak eta denbora-pasak Comment[fa]=بازی و سرگرمی Comment[fi]=Pelit ja viihde Comment[fr]=Jeux et divertissements Comment[fur]=Zûcs e golosets Comment[fy]=Spultsjes en Ferdiverdaasje Comment[ga]=Cluichí agus siamsaíochtaí Comment[gl]=Xogos e pasatempos Comment[gn]=Jaugaty Comment[gu]=રમતો અને મનોરંજકો Comment[gv]=Gammanyn as gaihyn Comment[ha]=Wasanni da kayan da da dariya Comment[he]=משחקים ושעשועים Comment[hi]=खेल व मनोरंजन Comment[hr]=Igre i zabava Comment[hu]=Játék és szórakozás Comment[hy]=Խաղեր և զվարճություններ Comment[id]=Permainan dan hiburan Comment[ig]=Egwuregwu na obiụtọ Comment[io]=Ludi ed amuzi Comment[is]=Leikir og skemmtun Comment[it]=Giochi e passatempi Comment[ja]=気晴しにゲームをどうぞ Comment[ka]=თამაშები და სხვა Comment[kk]=Ойын-сауық Comment[kn]=ಆಟಗಳು ಹಾಗು ರಂಜನೆಗಳು Comment[ko]=게임 메뉴 Comment[ku]=Lîstik û demxweşî Comment[ky]=Оюндар жана эс алуу Comment[lt]=Žaidimai ir pramogos Comment[lv]=Spēles un izklaides programmas Comment[mai]=खेल आ मनोरंजन Comment[mg]=lalao sy fialam-boly Comment[mk]=Игри и забава Comment[ml]=കളികളും വിനോദങ്ങളും Comment[mn]=Тоглоом, зугаа цэнгэл Comment[mr]=खेळ व मनोरंजन Comment[ms]=Permainan dan hiburan Comment[nb]=Spill og underholdning Comment[nds]=Speel un Spoos Comment[ne]=खेल र मनोरञ्जन Comment[nl]=Spelletjes en amusement Comment[nn]=Spel og underhalding Comment[oc]=Jòcs e divertiments Comment[or]=ଖେଳ ଏବଂ ମନୋରଞ୍ଜନ Comment[pa]=ਖੇਡਾਂ ਅਤੇ ਮਨੋਰੰਜਨ Comment[pl]=Gry i rozrywka Comment[ps]=لوبې او مهالتيري Comment[pt]=Jogos e passatempos Comment[pt_BR]=Jogos e diversões Comment[ro]=Jocuri și divertisment Comment[ru]=Игры и развлечения Comment[si]=ක්‍රිඩා සහ විනොදාශ්වාද Comment[sk]=Hry a zábava Comment[sl]=Igre in zabava Comment[sq]=Lojra dhe argëtime Comment[sr]=Игре и забава Comment[sr@latin]=Igre i zabava Comment[sv]=Spel och underhållning Comment[ta]=விளையாட்டுகள் மற்றும் பொழுதுப்போக்குகள் Comment[te]=ఆటలు మరియు వినోదకాలు Comment[th]=เกมและความบันเทิง Comment[tr]=Oyun ve eğlencelikler Comment[ug]=ئويۇن ۋە كۆڭۈل ئېچىش پروگراممىلىرى Comment[uk]=Ігри та розваги Comment[uz@cyrillic]=Ўйинлар ва кўнгилочар дастурлар Comment[vi]=Trò chơi và giải trí Comment[xh]=Imidlalo nokuzihlekisa Comment[yo]=Àwọn ayò àti àwọn eré-àṣenọjú Comment[zh_CN]=游戏和娱乐 Comment[zh_HK]=遊戲及娛樂 Comment[zh_TW]=遊戲及娛樂 Icon=applications-games Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-personal.directory0000664000175000017500000001350012625666430026577 0ustar fabiofabio[Desktop Entry] # Translators: this is Personal as in "Personal settings" Name=Personal Name[af]=Persoonlik Name[an]=Presonal Name[ar]=شخصي Name[as]=ব্যক্তিগত Name[ast]=Personal Name[be]=Пэрсанальныя Name[be@latin]=Asabistaje Name[bg]=Лични Name[bn]=ব্যক্তিগত Name[bn_IN]=ব্যক্তিগত Name[br]=Personel Name[ca]=Personal Name[ca@valencia]=Personal Name[crh]=Şahsiy Name[cs]=Osobní Name[da]=Personlig Name[de]=Persönlich Name[dv]=އަމިއްލަަ Name[dz]=རང་དོན་ Name[el]=Προσωπικά Name[en@shaw]=𐑐𐑻𐑕𐑩𐑯𐑩𐑤 Name[en_GB]=Personal Name[eo]=Persona Name[es]=Personal Name[et]=Isiklik Name[eu]=Pertsonala Name[fa]=شخصی Name[fi]=Henkilökohtaiset Name[fr]=Personnel Name[fur]=Personâl Name[fy]=Persoanlik Name[ga]=Pearsanta Name[gl]=Persoal Name[gu]=વ્યક્તિગત Name[gv]=Persoonagh Name[he]=אישי Name[hi]=निजी Name[hr]=Osobno Name[hu]=Személyes Name[hy]=Անձնական Name[id]=Pribadi Name[io]=Personala Name[is]=Persónulegt Name[it]=Personale Name[ja]=ユーザ向け Name[kk]=Жеке Name[kn]=ವೈಯಕ್ತಿಕ Name[ko]=개인 Name[ku]=Takekesî Name[lt]=Asmeniniai Name[lv]=Personīgie Name[mai]=निजी Name[mk]=Лично Name[ml]=സ്വകാര്യം Name[mn]=Хувийн Name[mr]=व्यक्तिगत Name[nb]=Personlig Name[nds]=Persönlich Name[ne]=व्यक्तिगत Name[nl]=Persoonlijk Name[nn]=Personleg Name[oc]=Personal Name[or]=ବ୍ୟକ୍ତିଗତ Name[pa]=ਨਿੱਜੀ Name[pl]=Osobiste Name[ps]=وګړيز Name[pt]=Pessoal Name[pt_BR]=Pessoal Name[ro]=Personal Name[ru]=Персональные Name[si]=පුද්ගලික Name[sk]=Osobné Name[sl]=Osebno Name[sq]=Vetjake Name[sr]=Лично Name[sr@latin]=Lično Name[sv]=Personligt Name[ta]=தனிப்பயன் Name[te]=వ్యక్తిగత Name[th]=ส่วนบุคคล Name[tr]=Kişisel Name[ug]=شەخسىي Name[uk]=Особисті Name[uz@cyrillic]=Шахсий Name[vi]=Cá nhân Name[zh_CN]=个人 Name[zh_HK]=個人 Name[zh_TW]=個人 Comment=Personal settings Comment[af]=Persoonlike instellings Comment[an]=Peferencias presonals Comment[ar]=الإعدادات الشخصيّة Comment[as]=ব্যক্তিগত বৈশিষ্ট্য Comment[ast]=Preferencies personales Comment[be]=Пэрсанальныя настаўленьні Comment[be@latin]=Asabistyja nałady Comment[bg]=Лични настройки Comment[bn]=ব্যক্তিগত সেটিংস Comment[bn_IN]=ব্যক্তিগত বৈশিষ্ট্য Comment[br]=Arventennoù personel Comment[ca]=Paràmetres personals Comment[ca@valencia]=Paràmetres personals Comment[crh]=Şahsiy ayarlar Comment[cs]=Osobní nastavení Comment[da]=Personlige indstillinger Comment[de]=Persönliche Vorlieben und Einstellungen Comment[dv]=އަމިއްލަ ސެޓިންތައް Comment[dz]=རང་དོན་སྒྲིག་སྟངས་ Comment[el]=Προσωπικές ρυθμίσεις Comment[en@shaw]=𐑐𐑻𐑕𐑩𐑯𐑩𐑤 𐑕𐑧𐑑𐑦𐑙𐑟 Comment[en_GB]=Personal settings Comment[eo]=Persona agordaro Comment[es]=Preferencias personales Comment[et]=Isiklikud sätted Comment[eu]=Ezarpen pertsonalak Comment[fa]=تنظیمات شخصی Comment[fi]=Henkilökohtaiset asetukset Comment[fr]=Paramètres personnels Comment[fur]=Impostazions personâls Comment[fy]=Persoanlike Ynstellingen Comment[ga]=Socruithe pearsanta Comment[gl]=Configuracións persoais Comment[gu]=વ્યક્તિગત સુયોજનો Comment[gv]=Reighyn persoonagh Comment[he]=הגדרות אישיות Comment[hi]=निजी सेटिंग Comment[hr]=Osobne postavke Comment[hu]=Személyes beállítások Comment[hy]=Հատկություններ Comment[id]=Pengaturan pribadi Comment[io]=Personala situesi Comment[is]=Persónulegar stillingar Comment[it]=Impostazioni personali Comment[ja]=ユーザ向けの設定を変更します Comment[kk]=Жеке баптаулар Comment[kn]=ವೈಯಕ್ತಿಕ ಸಿದ್ಧತೆಗಳು Comment[ko]=개인 설정 Comment[ku]=Mîhengên takekesî Comment[lt]=Asmeniniai nustatymai Comment[lv]=Personīgie iestatījumi Comment[mai]=निजी सेटिंग Comment[mk]=Лични поставувања Comment[ml]=സ്വകാര്യ സജ്ജീകരണങ്ങള്‍ Comment[mn]=Хувийн тохиргоо Comment[mr]=व्यक्तिगत संयोजना Comment[nb]=Personlige innstillinger Comment[nds]=Persönliche Instellens Comment[ne]=व्यक्तिगत सेटिङ Comment[nl]=Persoonlijke voorkeuren Comment[nn]=Personlege innstillingar Comment[oc]=Paramètres personals Comment[or]=ବ୍ଯକ୍ତିଗତ ବିନ୍ଯାସ Comment[pa]=ਨਿੱਜੀ ਸੈਟਿੰਗ Comment[pl]=Ustawienia osobiste Comment[ps]=وګړيزې امستنې Comment[pt]=Definições pessoais Comment[pt_BR]=Configurações pessoais Comment[ro]=Opțiuni personale Comment[ru]=Персональные параметры Comment[si]=පුද්ගලික සැකසුම් Comment[sk]=Osobné nastavenia Comment[sl]=Osebne nastavitve Comment[sq]=Rregullime vetjake Comment[sr]=Лична подешавања Comment[sr@latin]=Lična podešavanja Comment[sv]=Personliga inställningar Comment[ta]=தனிப்பயன் அமைப்புகள் Comment[te]=వ్యక్తిగత అమరికలు Comment[th]=ตั้งค่าส่วนบุคคล Comment[tr]=Kişisel ayarlar Comment[ug]=شەخسىي تەڭشەكلەر Comment[uk]=Персональні параметри Comment[uz@cyrillic]=Шахсий мосламалар Comment[vi]=Thiết lập cá nhân Comment[zh_CN]=个人设置 Comment[zh_HK]=個人設定 Comment[zh_TW]=個人設定 Icon=preferences-desktop-personal Type=Directory Cinnamon-2.8.6/files/usr/share/desktop-directories/cinnamon-menu-system.directory0000664000175000017500000002251412625666430027247 0ustar fabiofabio[Desktop Entry] Name=System Name[af]=Stelsel Name[an]=Sistema Name[ar]=نظام Name[as]=ব্যৱস্থাপ্ৰণালী Name[ast]=Sistema Name[be]=Сыстэма Name[be@latin]=Systema Name[bg]=Система Name[bn]=সিস্টেম Name[bn_IN]=সিস্টেম Name[br]=Reizhiad Name[ca]=Sistema Name[ca@valencia]=Sistema Name[crh]=Sistem Name[cs]=Systém Name[da]=System Name[de]=System Name[dv]=ސިސްޓަމް Name[dz]=རིམ་ལུགས་ Name[el]=Σύστημα Name[en@shaw]=𐑕𐑦𐑕𐑑𐑩𐑥 Name[en_GB]=System Name[eo]=Sistemo Name[es]=Sistema Name[et]=Süsteem Name[eu]=Sistema Name[fa]=سیستم Name[fi]=Järjestelmä Name[fr]=Système Name[fur]=Sisteme Name[fy]=Systeem Name[ga]=Córas Name[gl]=Sistema Name[gu]=સિસ્ટમ Name[gv]=System Name[he]=מערכת Name[hi]=तंत्र Name[hr]=Sustav Name[hu]=Rendszer Name[hy]=Համակարգ Name[id]=Sistem Name[io]=Sistemo Name[is]=Kerfið Name[it]=Sistema Name[ja]=システム Name[kk]=Жүйелік Name[kn]=ವ್ಯವಸ್ಥೆ Name[ko]=시스템 Name[ku]=Pergal Name[lt]=Sistema Name[lv]=Sistēma Name[mai]=तंत्र Name[mk]=Систем Name[ml]=സിസ്റ്റം Name[mn]=Систем Name[mr]=प्रणाली Name[nb]=System Name[nds]=System Name[ne]=प्रणाली Name[nl]=Systeem Name[nn]=System Name[oc]=Sistèma Name[or]=ତନ୍ତ୍ର Name[pa]=ਸਿਸਟਮ Name[pl]=System Name[ps]=غونډال Name[pt]=Sistema Name[pt_BR]=Sistema Name[ro]=Sistem Name[ru]=Система Name[si]=පද්දති Name[sk]=Systém Name[sl]=Sistem Name[sq]=Sistemi Name[sr]=Систем Name[sr@latin]=Sistem Name[sv]=System Name[ta]=கணிப்பொறி Name[te]=వ్యవస్థ పనిముట్లు Name[th]=ระบบ Name[tr]=Sistem Name[ug]=سىستېما Name[uk]=Системна Name[uz@cyrillic]=Тизим Name[vi]=Hệ thống Name[zh_CN]=系统 Name[zh_HK]=系統 Name[zh_TW]=系統 Comment=Personal preferences and administration settings Comment[af]=Persoonlike voorkeure en administrasie-instellings Comment[an]=Preferencias presonals y opcions d'almenistracion Comment[ar]=التفضيلات الشخصيّة و إعدادات الإدارة Comment[as]=ব্যক্তিগত পছন্দ ও প্ৰশাসনিক বৈশিষ্ট্য Comment[ast]=Preferencies personales y parámetros d'alministración Comment[be]=Прыватныя перавагі і адміністрацыйныя настаўленьні Comment[be@latin]=Asabistyja i administracyjnyja nałady Comment[bg]=Лични предпочитания и административни настройки Comment[bn]=ব্যক্তিগত পছন্দতালিকা এবং অ্যাডমিনস্ট্রেশন বৈশিষ্ট্যাবলী Comment[bn_IN]=ব্যক্তিগত পছন্দ ও প্রশাসনিক বৈশিষ্ট্য Comment[br]=Dibarzhioù hiniennel ha arventennoù merañ Comment[ca]=Preferències personals i paràmetres d'administració Comment[ca@valencia]=Preferències personals i paràmetres d'administració Comment[crh]=Şahsiy tercihler ve idare tesbitleri Comment[cs]=Osobní volby a nastavení správy Comment[cy]=Hoffterau personol a gosodiadau gweinyddol Comment[da]=Personlige indstillinger og administrationskonfiguration Comment[de]=Persönliche Anpassungen und Administratoreinstellungen Comment[dv]=އަމިއްލަ ޕުރިފަރަންސް އަދި އެޑުމިނިސްޓުރެޓަރ ސެޓިން Comment[dz]=རང་དོན་དགའ་གདམ་དང་ བདག་སྐྱོང་གི་གཞི་སྒྲིག་ཚུ། Comment[el]=Προσωπικές προτιμήσεις και ρυθμίσεις διαχείρισης Comment[en@shaw]=𐑐𐑻𐑕𐑩𐑯𐑩𐑤 𐑐𐑮𐑧𐑓𐑼𐑩𐑯𐑕𐑩𐑟 𐑯 𐑩𐑛𐑥𐑦𐑯𐑦𐑕𐑑𐑮𐑱𐑖𐑩𐑯 𐑕𐑧𐑑𐑦𐑙𐑟 Comment[en_CA]=Personal preferences and administration settings Comment[en_GB]=Personal preferences and administration settings Comment[eo]=Personaj preferoj kaj administraj agordoj Comment[es]=Propiedades personales y opciones de administración Comment[et]=Isiklikud eelistused ja haldussätted Comment[eu]=Hobespen pertsonalak eta administrazio-ezarpenak Comment[fa]=ترجیحات شخصی و تنظیمات مدیریتی Comment[fi]=Henkilökohtaiset ja järjestelmän asetukset Comment[fr]=Préférences personnelles et paramètres du système Comment[fur]=Preferencis personâls e impostazions di aministrazion Comment[fy]=Persoanlike foarkarren en administraasje ynstellings Comment[ga]=Sainroghanna pearsanta agus socruithe riaracháin Comment[gl]=Preferencias persoais e configuracións de administración Comment[gn]=Mba'emon Nhamoia Comment[gu]=ખાનગી પસંદગીઓ અને સંચાલન સુયોજનો Comment[gv]=Reighyn persoonagh as Reighyn reirey Comment[ha]=Kayan daidaita fifkonka da hukumanka Comment[he]=העדפות אישיות והגדרות ניהול Comment[hi]=निजी वरीयता व प्रशासन सेटिंग Comment[hr]=Osobne postavke i administracijske postavke Comment[hu]=Személyes és adminisztrációs beállítások Comment[hy]=Անհատական և կառավարման կարգավորումներ Comment[id]=Preferensi pribadi dan pengaturan administrasi Comment[ig]=Nkarachọ nkeonwe na ọdịdị Nhazi Comment[io]=Personala preferi ed situesi pri administrado Comment[is]=Persónulegir valmöguleikar og stjórnunar stillingar Comment[it]=Preferenze personali e impostazioni di amministrazione Comment[ja]=個人設定と管理者向けの設定ツールです Comment[ka]=საკუთარი პარამეტრები და ადმინისტრაცია Comment[kk]=Жеке мен әкімшілік баптаулар Comment[kn]=ವೈಯಕ್ತಿಕ ಆದ್ಯತೆಗಳು ಹಾಗು ನಿರ್ವಹಣಾ ಸಿದ್ಧತೆಗಳು Comment[ko]=개인 기본 설정과 관리자 설정 Comment[ku]=Vebijêrkên takekesî û mîhengên rêveberiyê Comment[ky]=Өздүк жактыруулар жана административдик параметрлер Comment[lt]=Asmeniniai ir administravimo nustatymai Comment[lv]=Personīgie un administrācijas iestatījumi Comment[mai]=निज वरीयता आ प्रशासनिक जमावट Comment[mg]=Safidy manokana sy fandrindrana ny fomba fitantanana Comment[mk]=Лични преференции и администраторски поставувања Comment[ml]=സ്വകാര്യ മുന്‍ഗണനകളും ഭരണ സജ്ജീകരണങ്ങളും Comment[mn]=Хувийн тохиргоо болон удирдлагын тохируулгууд Comment[mr]=व्यक्तिगत प्राधान्यता व प्रशासकीय रचना Comment[nb]=Personlige brukervalg og administrative innstillinger Comment[nds]=Persönliche Vorleeben un administrative Instellens Comment[ne]=व्यक्तिगत प्राथमिकता र प्रशासकिय सेटिङ Comment[nl]=Persoonlijke voorkeuren en instellingen Comment[nn]=Personlege og administrative innstillingar Comment[oc]=Preferéncias personalas e paramètres del sistèma Comment[or]=ବ୍ଯକ୍ତିଗତ ପସନ୍ଦ ଏବଂ ପ୍ରଶାସନିକ ବିନ୍ଯାସ Comment[pa]=ਨਿੱਜੀ ਪਸੰਦ ਅਤੇ ਪਰਸ਼ਾਸਨੀ ਸਥਾਪਨ Comment[pl]=Preferencje osobiste i ustawienia administracyjne Comment[ps]=وګړيز غوراوي او د پازوالنې امستنې Comment[pt]=Preferências pessoais e definições de administração Comment[pt_BR]=Preferências pessoais e configurações de administração Comment[ro]=Preferințe personale și configurări administrative Comment[ru]=Личные предпочтения и административные параметры Comment[si]=පුත්ගලික අභිප්‍රේත සහ පාලන සැකසුම් Comment[sk]=Osobné predvoľby a správcovské nastavenia Comment[sl]=Osebne možnosti in skrbniške nastavitve Comment[sq]=Preferimet vetjake dhe rregullimet administrative Comment[sr]=Личне поставке и администраторска подешавања Comment[sr@latin]=Lične postavke i administratorska podešavanja Comment[sv]=Personliga inställningar och administrationsinställningar Comment[ta]=தனிநபர் விருப்பங்கள் மற்றும் நிர்வாக அமைப்புகள் Comment[te]=వ్యక్తిగత అభీష్టాలు మరియు Comment[th]=ปรับแต่งพื้นโต๊ะส่วนตัวและตั้งค่าระบบ Comment[tr]=Kişisel tercihler ve yönetim ayarları Comment[ug]=شەخسىي مايىللىق ۋە باشقۇرۇش تەڭشىكى Comment[uk]=Особисті параметри та адміністративні параметри Comment[vi]=Thiết lập quản lý và Tùy thích cá nhân Comment[yo]=Àwọn ààtò ìṣàkóso àti àwọn ìkúndùn aládàáni Comment[zh_CN]=个人首选项和管理设置 Comment[zh_HK]=個人偏好及管理設定 Comment[zh_TW]=個人偏好及管理設定 Icon=preferences-other Type=Directory Cinnamon-2.8.6/files/usr/share/dbus-1/0000775000175000017500000000000012625666430016337 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/dbus-1/services/0000775000175000017500000000000012625666430020162 5ustar fabiofabioCinnamon-2.8.6/files/usr/share/dbus-1/services/org.Cinnamon.Melange.service0000664000175000017500000000012712625666430025443 0ustar fabiofabio[D-BUS Service] Name=org.Cinnamon.Melange Exec=/usr/bin/cinnamon-looking-glass daemon Cinnamon-2.8.6/files/usr/share/dbus-1/services/org.Cinnamon.Slideshow.service0000664000175000017500000000011412625666430026030 0ustar fabiofabio[D-BUS Service] Name=org.Cinnamon.Slideshow Exec=/usr/bin/cinnamon-slideshowCinnamon-2.8.6/files/Makefile.in0000664000175000017500000000027212625666430015377 0ustar fabiofabioall: clean: distclean: maintainer-clean: install: find -mindepth 1 -maxdepth 1 -type d -exec cp -R {} $(DESTDIR)/ \; uninstall: find -mindepth 1 -type f -exec rm $(DESTDIR)/{} \; Cinnamon-2.8.6/files/etc/0000775000175000017500000000000012625666430014104 5ustar fabiofabioCinnamon-2.8.6/files/etc/xdg/0000775000175000017500000000000012625666430014666 5ustar fabiofabioCinnamon-2.8.6/files/etc/xdg/menus/0000775000175000017500000000000012625666430016015 5ustar fabiofabioCinnamon-2.8.6/files/etc/xdg/menus/cinnamon-applications.menu0000664000175000017500000001210212625666430023165 0ustar fabiofabio

Applications cinnamon-menu-applications.directory /etc/X11/applnk /usr/share/cinnamon/apps Accessories cinnamon-utility.directory Utility Accessibility System Universal Access cinnamon-utility-accessibility.directory Accessibility Settings Development cinnamon-development.directory Development emacs.desktop Education cinnamon-education.directory Education Games cinnamon-game.directory Game Graphics cinnamon-graphics.directory Graphics Internet cinnamon-network.directory Network Multimedia cinnamon-audio-video.directory AudioVideo Office cinnamon-office.directory Office System cinnamon-system-tools.directory Other cinnamon-other.directory Core Settings Screensaver wine-wine wine-wine.directory Wine Preferences cinnamon-settings.directory Settings System Administration cinnamon-settings-system.directory Settings System System Settings Accessories Education Games Graphics Internet Office Other Development Multimedia System Universal Access wine-wine Preferences Administration Cinnamon-2.8.6/files/etc/xdg/menus/cinnamon-applications-merged0000777000175000017500000000000012625666430027334 2applications-mergedustar fabiofabioCinnamon-2.8.6/tools/0000775000175000017500000000000012625666430013367 5ustar fabiofabioCinnamon-2.8.6/tools/check-for-missing.py0000775000175000017500000000136212625666430017256 0ustar fabiofabio#!/usr/bin/python2 # # This is a simple script that we use to check for files in git # and not in the distribution. It was previously written in cinnamon # and inlined in the Makefile.am, but 'git ls-files --exclude=' # was changed to no longer do anything useful, which made that # too challenging to be worthwhile. import fnmatch, os, subprocess, sys srcdir=sys.argv[1] distdir=sys.argv[2] excludes=sys.argv[3:] os.chdir(srcdir) status=0 for f in subprocess.Popen(["git", "ls-files"], stdout=subprocess.PIPE).stdout: f = f.strip() if (not os.path.exists(os.path.join(distdir, f)) and not any((fnmatch.fnmatch(f, p) for p in excludes))): print "File missing from distribution:", f status=1 sys.exit(status) Cinnamon-2.8.6/generate_additional_files.py0000775000175000017500000000172012625666430017750 0ustar fabiofabio#!/usr/bin/python2 DOMAIN = "cinnamon" PATH = "/usr/share/locale" import os, gettext, sys sys.path.append('/usr/lib/linuxmint/common') import additionalfiles os.environ['LANG'] = "en_US.UTF-8" gettext.install(DOMAIN, PATH) prefix = """[Desktop Entry] Exec=cinnamon-settings Icon=preferences-system Terminal=false Type=Application Categories=Settings; StartupNotify=false OnlyShowIn=X-Cinnamon; Keywords=Preferences;Settings; """ additionalfiles.generate(DOMAIN, PATH, "files/usr/share/applications/cinnamon-settings.desktop", prefix, _("System Settings"), _("Control Center"), "") prefix = """[Desktop Entry] Exec=cinnamon-settings-users Icon=system-users Terminal=false Type=Application Categories=System;Settings; StartupNotify=false OnlyShowIn=X-Cinnamon; Keywords=Preferences;Settings; """ additionalfiles.generate(DOMAIN, PATH, "files/usr/share/applications/cinnamon-settings-users.desktop", prefix, _("Users and Groups"), _("Add or remove users and groups"), "") Cinnamon-2.8.6/.project0000664000175000017500000000164012625666430013677 0ustar fabiofabio gnome-shell org.eclipse.wst.jsdt.core.javascriptValidator org.eclipse.linuxtools.cdt.autotools.genmakebuilder org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.managedbuilder.core.ScannerConfigNature org.eclipse.linuxtools.cdt.autotools.autotoolsNature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.core.cnature org.eclipse.wst.jsdt.core.jsNature Cinnamon-2.8.6/docs/0000775000175000017500000000000012625666430013157 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/0000775000175000017500000000000012625666430020253 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/apt@cinnamon.org/0000775000175000017500000000000012625666430023450 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/apt@cinnamon.org/metadata.json0000664000175000017500000000017712625666430026130 0ustar fabiofabio{ "description": "Provides packages search results", "uuid": "apt@cinnamon.org", "name": "APT search provider" } Cinnamon-2.8.6/docs/search-providers-examples/apt@cinnamon.org/search_provider.js0000664000175000017500000000156012625666430027167 0ustar fabiofabioconst Util = imports.misc.util; const Cinnamon = imports.gi.Cinnamon; const SearchProviderManager = imports.ui.searchProviderManager; const Lang = imports.lang; var default_icon_app = Cinnamon.AppSystem.get_default().lookup_app("mintinstall.desktop"); var current_pattern; function dbus_push_results(results, pattern) { if (pattern == current_pattern) { results = JSON.parse(results); for (var i in results) { results[i].icon_app = default_icon_app; } send_results(results); } } function perform_search(pattern) { current_pattern = pattern; Util.spawn_async(['python', SearchProviderManager.extensionMeta['apt@cinnamon.org'].path + '/search_provider.py', pattern], Lang.bind(this, dbus_push_results, pattern)); } function on_result_selected(result){ Util.spawn(['xdg-open', 'apt://' + result.id]); } Cinnamon-2.8.6/docs/search-providers-examples/apt@cinnamon.org/search_provider.py0000664000175000017500000000101412625666430027175 0ustar fabiofabio# -*- coding=utf-8 -*- import subprocess import sys import gettext import json gettext.install("cinnamon", "/usr/share/locale") if __name__ == "__main__": results = [] packages = subprocess.check_output(["apt-cache", "search"] + sys.argv[1].split(" ")).splitlines()[:10] for p in packages: i = p.index(" - ") name = p[:i] description = p[i+3:] results.append({'id': name, 'label': _("Install package : ") + name, 'description': description}) print json.dumps(results) Cinnamon-2.8.6/docs/search-providers-examples/yahoo@cinnamon.org/0000775000175000017500000000000012625666430024003 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/yahoo@cinnamon.org/metadata.json0000664000175000017500000000051312625666430026455 0ustar fabiofabio{ "description": "Yahoo", "uuid": "yahoo@cinnamon.org", "name": "Yahoo", "supported_locales": ["en"], "locale_data": { "yahoo_url": { "en": "http://us.yhs4.search.yahoo.com/yhs/search?p=%s&fr=sfp&fr2=&type=__alt__ddc_linuxmint_com&hspart=ddc&hsimp=yhs-linuxmint&iscqry=" } } } Cinnamon-2.8.6/docs/search-providers-examples/yahoo@cinnamon.org/search_provider.js0000664000175000017500000000155512625666430027526 0ustar fabiofabioconst Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const St = imports.gi.St; const Util = imports.misc.util; function perform_search(pattern){ push_results(pattern); } function push_results(results){ var file = Gio.file_new_for_path('/usr/share/icons/Mint-X/apps/22/yahoo.png'); var gicon = new Gio.FileIcon({ file: file }); var myicon = new St.Icon({gicon: gicon, icon_size: 22, icon_type: St.IconType.FULLCOLOR, reactive: true, track_hover: true, style_class: 'applet-icon'}); var pattern = results.replace(" ", "+"); var results_array = new Array(); results_array.push({ id: pattern, label: _("Search '" + results + "'"), icon: myicon }); send_results(results_array); } function on_result_selected(result){ Util.spawn(['xdg-open', get_locale_string("yahoo_url").replace("%s", result.id)]); } Cinnamon-2.8.6/docs/search-providers-examples/chromium-history@cinnamon.org/0000775000175000017500000000000012625666430026206 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/chromium-history@cinnamon.org/metadata.json0000664000175000017500000000017112625666430030660 0ustar fabiofabio{ "description": "Chromium history", "uuid": "chromium-history@cinnamon.org", "name": "Chromium history" } Cinnamon-2.8.6/docs/search-providers-examples/chromium-history@cinnamon.org/search_provider.js0000664000175000017500000000212112625666430031717 0ustar fabiofabioconst Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Util = imports.misc.util; const Cinnamon = imports.gi.Cinnamon; const SearchProviderManager = imports.ui.searchProviderManager; const MAX_SEARCH_RESULTS = 10; var default_icon_app = Cinnamon.AppSystem.get_default().lookup_app("chromium-browser.desktop"); var current_pattern; function dbus_push_results(results, pattern) { var basename; if (pattern == current_pattern) { results = JSON.parse(results); for (var i in results) { if (!results[i].icon_filename) { results[i].icon_app = default_icon_app; } } send_results(results); } } function perform_search(pattern){ current_pattern = pattern; Util.spawn_async(['python', SearchProviderManager.extensionMeta['chromium-history@cinnamon.org'].path + '/search_provider.py', pattern], Lang.bind(this, dbus_push_results, pattern)); } function on_result_selected(result){ Util.spawn(['xdg-open', result.url]); } Cinnamon-2.8.6/docs/search-providers-examples/chromium-history@cinnamon.org/search_provider.py0000664000175000017500000000541012625666430031737 0ustar fabiofabio#! /usr/bin/python2 # -*- coding=utf-8 -*- import os import sys import tempfile import shutil import sqlite3 import json import urlparse import subprocess FAVICON_CACHE_DIR = os.path.join(os.path.split(__file__)[0], "favicon_cache") if __name__ == "__main__": if len(sys.argv) > 1: fd, temp_filename = tempfile.mkstemp() os.close(fd) shutil.copyfile(os.path.join(os.getenv("HOME"), ".config", "chromium", "Default", "History"), temp_filename) conn = sqlite3.Connection(temp_filename) cur = conn.cursor() words = [] for i in sys.argv[1:]: words += i.split() query = "SELECT url, title FROM urls WHERE " + " AND ".join(len(words) * ["(url LIKE ? OR title LIKE ?)"]) + " ORDER BY last_visit_time DESC, visit_count DESC LIMIT 10" params = [] for word in words: params.append("%" + word + "%") params.append("%" + word + "%") cur.execute(query, tuple(params)) results = [] domains_list = [] for url, title in cur.fetchall(): url_parsed = urlparse.urlparse(url) domain = url_parsed.scheme + '://' + url_parsed.netloc if not domain in domains_list: domains_list.append(domain) if url and title: results.append({ "id": url, "url": url, "domain": domain, "description": url, "label": title }) cur.close() os.unlink(temp_filename) if not os.path.exists(FAVICON_CACHE_DIR): os.mkdir(FAVICON_CACHE_DIR) fd, temp_filename = tempfile.mkstemp() os.close(fd) shutil.copyfile(os.path.join(os.getenv("HOME"), ".config", "chromium", "Default", "Favicons"), temp_filename) conn = sqlite3.Connection(temp_filename) cur = conn.cursor() domains_to_favicons = {} for domain in domains_list: cur.execute("SELECT id, url FROM favicons WHERE url LIKE ?", [domain + "%"]) for favicon_id, url in cur.fetchall(): filename = os.path.join(FAVICON_CACHE_DIR, str(favicon_id)) if not os.path.exists(filename): subprocess.check_call(['wget', '-O', filename, url]) if os.path.exists(filename): domains_to_favicons[domain] = filename cur.close() os.unlink(temp_filename) for i in range(len(results)): if results[i]['domain'] in domains_to_favicons: results[i]['icon_filename'] = domains_to_favicons[results[i]['domain']] print json.dumps(results) Cinnamon-2.8.6/docs/search-providers-examples/calc@cinnamon.org/0000775000175000017500000000000012625666430023566 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/calc@cinnamon.org/metadata.json0000664000175000017500000000021312625666430026235 0ustar fabiofabio{ "description": "Solves basic mathematical operations", "uuid": "calc@cinnamon.org", "name": "Calculator Search Provider" } Cinnamon-2.8.6/docs/search-providers-examples/calc@cinnamon.org/search_provider.js0000664000175000017500000000212412625666430027302 0ustar fabiofabioconst Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const St = imports.gi.St; const Util = imports.misc.util; const Cinnamon = imports.gi.Cinnamon; function perform_search(pattern){ push_results(pattern); } function push_results(results){ var default_icon_app = Cinnamon.AppSystem.get_default().lookup_app("gcalctool.desktop"); var pattern = results var results_array = new Array(); //First Regex tests for numbers and operators, second makes sure it does not end with an operator as that is not a valid operation if ((/^[0-9.+*/()-]+$/.test(pattern)) && !(/[.+*/(-]+$/.test(pattern))) { let solution = 0; try { solution = eval(pattern); }catch(err){} let awns = (Math.round(solution * 10000) / 10000).toString(); if (!(awns == pattern)) { results_array.push({ id: awns, label: _("Solution: " + awns), icon_app: default_icon_app }); send_results(results_array); } } } function on_result_selected(result){} Cinnamon-2.8.6/docs/search-providers-examples/wikipedia@cinnamon.org/0000775000175000017500000000000012625666430024632 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/wikipedia@cinnamon.org/metadata.json0000664000175000017500000000045512625666430027311 0ustar fabiofabio{ "description": "Wikipedia", "uuid": "wikipedia@cinnamon.org", "name": "Wikipedia", "supported_locales": ["en"], "locale_data": { "wikipedia_url": { "en": "https://en.wikipedia.org/w/api.php?action=opensearch&search=%s&format=json&limit=5" } } } Cinnamon-2.8.6/docs/search-providers-examples/wikipedia@cinnamon.org/search_provider.js0000664000175000017500000000441712625666430030355 0ustar fabiofabioconst Mainloop = imports.mainloop; const Cinnamon = imports.gi.Cinnamon; const Gio = imports.gi.Gio; const St = imports.gi.St; const Util = imports.misc.util; const Lang = imports.lang; const Soup = imports.gi.Soup; const _httpSession = new Soup.SessionAsync(); Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault()); var results_cache = {}; var last_search; var wikipedia_icon_file = Gio.file_new_for_path('/usr/share/icons/Mint-X/apps/22/wikipedia.png'); var wikipedia_gicon = new Gio.FileIcon({ file: wikipedia_icon_file }); function perform_search(pattern){ try{ if (results_cache[pattern]) { last_search = ''; push_results(results_cache[pattern]); }else{ last_search = pattern; let message = Soup.Message.new('GET', get_locale_string("wikipedia_url").replace("%s", pattern)); _httpSession.queue_message(message, Lang.bind(this, function (session, message, p) { try{ if (p != last_search) return; if( message.status_code == 200) { var results = JSON.parse(message.response_body.data.toString()); let result_titles = results[1]; let result_descriptions = results[2]; let result_urls = results[3]; results_cache[p] = new Array(); for (var i = 0; i < result_urls.length; i++) { results_cache[p].push({ id: result_urls[i], url: result_urls[i], label: result_titles[i], description: result_descriptions[i] }); } push_results(results_cache[p]); } else { global.logWarning("Error retrieving address " + url + ". Status: " + message.status_code + ": " + message.reason_phrase); } }catch(e){global.logError(e);} }, pattern)); } }catch(e){global.logError(e);} } function push_results(results){ for (var i in results) { results[i].icon = new St.Icon({gicon: wikipedia_gicon, icon_size: 22, icon_type: St.IconType.FULLCOLOR, reactive: true, track_hover: true, style_class: 'applet-icon'}); } send_results(results); } function on_result_selected(result){ Util.spawn(['xdg-open', result.url]); } Cinnamon-2.8.6/docs/search-providers-examples/trackerprovider@cinnamon.org/0000775000175000017500000000000012625666430026072 5ustar fabiofabioCinnamon-2.8.6/docs/search-providers-examples/trackerprovider@cinnamon.org/metadata.json0000664000175000017500000000017212625666430030545 0ustar fabiofabio{ "description": "Search using tracker", "uuid": "trackerprovider@cinnamon.org", "name": "Tracker search" } Cinnamon-2.8.6/docs/search-providers-examples/trackerprovider@cinnamon.org/search_provider.js0000664000175000017500000000466712625666430031624 0ustar fabiofabioconst Lang = imports.lang; const Util = imports.misc.util; const Cinnamon = imports.gi.Cinnamon; const SearchProviderManager = imports.ui.searchProviderManager; var current_pattern; function dbus_push_results(results, pattern) { var basename; var final_results = new Array(); if (pattern == current_pattern) { try{ results = JSON.parse(results); for (var i in results) { switch (results[i].type) { case "music": basename = results[i]["url"].split("/"); basename = decodeURIComponent(basename[basename.length - 1]); results[i]["performer"] = decodeURIComponent(results[i]["performer"]).substring(11); results[i]["musicAlbum"] = decodeURIComponent(results[i]["musicAlbum"]).substring(10); results[i].label = (results[i]["performer"] ? results[i]["performer"] + " - " : "") + (results[i]["musicAlbum"] ? results[i]["musicAlbum"] + " - " : "") + (results[i]["trackNumber"] ? results[i]["trackNumber"] + " - " : "") + (results[i]["title"] ? results[i]["title"] : basename); break; case "software": var results_parts = results[i]["url"].split("/"); var app = Cinnamon.AppSystem.get_default().lookup_app(results_parts[results_parts.length - 1].split(".desktop")[0] + ".desktop"); if (!app) { continue; } results[i].label = app.get_name(); results[i].icon_app = app; break; default: results[i].label = decodeURIComponent(results[i]["url"]); break; } final_results.push(results[i]); } send_results(results); } catch(e) { global.logError(e); } } } function perform_search(pattern) { current_pattern = pattern; Util.spawn_async(['python', SearchProviderManager.extensionMeta['trackerprovider@cinnamon.org'].path + '/search_provider.py', pattern], Lang.bind(this, dbus_push_results, pattern)); } function on_result_selected(result){ Util.spawn(['xdg-open', result.url]); } Cinnamon-2.8.6/docs/search-providers-examples/trackerprovider@cinnamon.org/search_provider.py0000664000175000017500000000500412625666430031622 0ustar fabiofabio# -*- coding=utf-8 -*- import subprocess import sys import gettext import json from gi.repository import Tracker gettext.install("cinnamon", "/usr/share/locale") CONVERT_TYPES = { "http://www.tracker-project.org/temp/nmm#MusicPiece": "music", "http://www.tracker-project.org/temp/nmm#Video": "videos", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SoftwareApplication": "software", "http://www.tracker-project.org/temp/nmm#Photo": "pictures", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder": "folders", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#FileDataObject": "files" } if __name__ == "__main__": results = {} conn = Tracker.SparqlConnection.get() words = sys.argv[1].split(" "); query_params = [] for i in words: if i: if i[0] == '"' and i[-1] == '"': query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i[1:-1]) + "\"") else: query_params.append("?s fts:match \"" + Tracker.sparql_escape_string(i) + "*\"") query = """SELECT ?s nie:url(?s) nmm:musicAlbum(?s) nmm:performer(?s) nmm:trackNumber(?s) nie:title(?s) nie:mimeType(?s) rdf:type(?s) WHERE { """ + " . ".join(query_params) + """ } ORDER BY DESC (fts:rank(?s)) LIMIT 100"""; cursor = conn.query(query) while cursor.next(): defined_type = None; if not cursor.get_string(7)[0] or not cursor.get_string(1)[0]: #If we have no defined file type or no file url, skip continue result_types = cursor.get_string(7)[0].split(",") while len(result_types) > 0 and defined_type == None: t = result_types.pop() if t in CONVERT_TYPES: defined_type = CONVERT_TYPES[t] if defined_type == None: defined_type = "files" if len(results.setdefault(defined_type, [])) < 10: results.setdefault(defined_type, []).append({ "id": cursor.get_string(0)[0], "url": cursor.get_string(1)[0], "musicAlbum": cursor.get_string(2)[0], "performer": cursor.get_string(3)[0], "trackNumber": cursor.get_string(4)[0], "title": cursor.get_string(5)[0], "mimeType": cursor.get_string(6)[0], "type": defined_type }); final_results = [] for i in results: final_results += results[i] print json.dumps(final_results) Cinnamon-2.8.6/docs/reference/0000775000175000017500000000000012625666430015115 5ustar fabiofabioCinnamon-2.8.6/docs/reference/cinnamon/0000775000175000017500000000000012625666430016717 5ustar fabiofabioCinnamon-2.8.6/docs/reference/cinnamon/cinnamon-docs.sgml.in0000664000175000017500000000443312625666430022744 0ustar fabiofabio ]> Cinnamon Reference Manual This document is for Cinnamon @VERSION@. The latest version of this documentation can be found online at http://developer.linuxmint.com/reference/git/cinnamon/. Actors Application tracking Search Tray Icons Recorder Integration helpers and utilities Object Hierarchy API Index Index of deprecated API Cinnamon-2.8.6/docs/reference/cinnamon/Makefile.am0000664000175000017500000000720312625666430020755 0ustar fabiofabio## Process this file with automake to produce Makefile.in # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=cinnamon # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # Directories containing the source code # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. DOC_SOURCE_DIR=$(top_srcdir)/src # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--rebuild-types # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS=--xml-mode --output-format=xml # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS= # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/src/*.h CFILE_GLOB=$(top_srcdir)/src/*.c # Extra header to include when scanning, which are not under DOC_SOURCE_DIR # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h EXTRA_HFILES= # Header files or dirs to ignore when scanning. Use base file/dir names # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code IGNORE_HFILES= \ calendar-server \ gvc \ hotplug-sniffer \ st \ tray \ gactionmuxer.h \ gactionobservable.h \ gactionobserver.h \ cinnamon-network-agent.h \ cinnamon-recorder-src.h if !BUILD_RECORDER IGNORE_HFILES += cinnamon-recorder.h endif # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS) GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libcinnamon.la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = $(DOC_MODULES).types # Comment this out if you want 'make check' to test you doc status # and run some sanity checks if ENABLE_GTK_DOC TESTS_ENVIRONMENT = cd $(srcdir) && \ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) #TESTS = $(GTKDOC_CHECK) endif -include $(top_srcdir)/git.mk Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/0000775000175000017500000000000012625666430020743 5ustar fabiofabioCinnamon-2.8.6/docs/reference/cinnamon-tutorials/documenting-tutorial.xml0000664000175000017500000002120312625666430025640 0ustar fabiofabio Writing tutorials It is also possible to write tutorials that are not part of the code. For example, this. They can be found in docs/reference/cinnamon-tutorials/, and are included into the cinnamon documentation via the docs/reference/cinnamon-tutorials/cinnamon-tutorials-docs.sgml.in file. These are plain docbook items, and existing docbook tutorials are suitable. This is intended to be a quick guide on the DocBook syntax. Overall structure DocBook is a markup language that vaguely resembles HTML, but the tag names are more verbose. A significant difference is that DocBook is more structured, where we have a top level <book>, containing different <parts> that have <chapter>s that in turn contain <section>s. The general structure of a regular DocBook item might look like <book> <part> <chapter> <section> <section> ... </section> <section> ... </section> </section> <section> ... </section> </chapter> <chapter> ... </chapter> </part> <part> <chapter> ... </chapter> </part> </book> As shown above, sections can be nested. In the example, we have layer 1 sections and layer 2 sections. It is possible to skip section layers and, say, directly include a layer 2 section inside a chapter. This can be done via using the <sect2> instead of the <section> tag (you can specify up to the 5th layer). You can also skip parts or chapters. For example, you might have <book> <part> <sect2> ... </sect2> <chapter> ... </chapter> </part> </book> This is helpful because sections in layers behave differently. Parts are numbered with Roman numerals. For example, a part might be labeled I. Code documentation On the other hand, chapters and sections are not numbered. More importantly, using <sect2> directly will affect chunking and the table of contents. Chunking is the act of splitting different sections into different pages when presented to the user. Different parts and chapters are always chunked, and so are layer 1 sections. A section appears on the table of contents if and only if it is a part, a chapter, or a layer 1 section inside a chapter (but a layer 1 section directly inside a part is not displayed). So if you wish to use chapters but do not want the layer 1 sections to be chunked, you will want to directly place a layer 2 section inside the chapter. Similarly, if you don't want your subsections to appear in the table of contents, use a layer 2 section without a layer 1 section. Escaping characters Similar to HTML, certain characters have to be escaped. For example, the > symbol is typed as &gt; and < is &lt;. Similarly, the ampersand & has to be escaped as &amp;. <code>xi:include</code> It is possible to include content from other xml files via xi:include. The precise syntax is <xi:include href="building.xml"/> The path is relative to the top-level document (ie. $(DOC_MODULE)-docs.sgml). To use this feature, you need to declare the xi: namespace. This is done via using the following doctype declaration: <!DOCTYPE book PUBLIC '-//OASIS//DTD DocBook XML V4.3//EN' 'http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd' [ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> ]> Tags and attributes The "title" tag It is (and recommended) to give each part/chapter/section a title. This can be done via the <title> tag. For example, a section might look like <section> <title>The title</title> ... </section> You should give all part/chapter/sections a title. If you cannot think of any sensible title, you probably don't want a separate section. The "para" tag This is the tag you will use the most often. It denotes a paragraph, and is equivalent to the HTML <p> tag. For example, <section> <title>The title</title> <para>Lorem ipsum dolor sit amet</para> </section> The "code" tag To display inline code, you will need the <code> tag. This is functionally the same as the HTML <code> tag. For example, <para>Lorem <code>ipsum</code> dolor sit amet.</para> will be displayed as Lorem ipsum dolor sit amet. The "programlisting" tag This is used to display multiple lines of code, such as the examples shown above. For example, the example above is <programlisting> &lt;section&gt; &lt;title&gt;The title&lt;/title&gt; &lt;para&gt;Lorem &lt;code&gt;ipsum&lt;/code&gt; dolor sit amet.&lt;/para&gt; &lt;/section&gt;</programlisting> There are a few things to take note of. Firstly, the < and > items are escaped. Secondly, the contents of the <programlisting> is flushed to the left, regardless of the current indentation of the xml. This is since all whitepsace is rendered verbatim. Thirdly, the end </programlisting tag> is put on the same row as the last line, or else an extra row will appear in the output. If you are intending to show code, you will want to do syntax highlighting. Doing so via the regular DocBook way is useless. Instead, you need some magic, namely enclosing your <programlisting> inside an <informalexample> tag, eg. <informalexample> <programlisting> function foo () { return bar } </programlisting> <informalexample> If you do so, then you are allowed to have a line break before the closing <programlisting> tag as well as indent all your code by a common indent to follow the indent of your xml code. gtk-doc will automatically fix all those problems for you (assuming you are using gtk-doc 1.22 or above). Note that the language of syntax highlighting is specified on a per-project basis inside the Makefile (for gtk-doc 1.24 or above; it is hardcoded to be always C for gtk-doc 1.23 or below). The syntax highlight for this tutorial is for Javascript, so if you are writing about something that doesn't resemble Javascript even vaguely (eg. xml), don't use syntax highlighting. The "itemizedlist" tag This generates an itemized list, similar to the HTML <ul> tag. Individual items are put inside <listitem> tags, similar to the HTML <li>. An example usage is as follows: <itemizedlist> <listitem> A “<code>metadata.json</code>” file which contains information about the applet, such as its name, description etc.. </listitem> <listitem> An “<code>applet.js</code>” file which contains its code. </listitem> </itemizedlist> Lists can be nested, and markup can also be used. Sometimes you might want to enclose the contents of each line with a <para> tag. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/using-documentation.xml0000664000175000017500000002721612625666430025471 0ustar fabiofabio Using documentation Overview The documentation of Cinnamon is separated into 4 different parts (5 if you count muffin). What you are currently reading is the tutorials, which includes the general top-level overviews and tutorials you will need for Cinnamon. This is named "Cinnamon Tutorials". The second part of the Javascript reference, which describes the Javascript part of Cinnamon. This is named the "Cinnamon Javascript Reference Manual". This is a technical reference for the individual functions and objects available in Cinnamon. Note that this documentation is aimed at both applet/extension developers and Cinnamon developers themselves. So depending on who you are, some of the information might be entirely irrelevant. The second part of the documentation is for the C part of Cinnamon, which is simply refered to as the "Cinnamon Reference Manual". The last part is the documentation for Shell toolkit, or St. This is the graphical toolkit used to draw widgets on the screen (similar to Gtk). The modules covered by the Javascript documentation are those imported via imports.ui.* and imports.misc.*. The global object is documented in the C part of Cinnamon, as well as things accessed through imports.gi.Cinnamon. Things accessed through imports.gi.St are, unsurprisingly, documented in the St part. imports.gi.Meta refers to Muffin, while others (eg. imports.gi.Gio) are third-party (usually GNOME) libraries that are documented elsewhere. Accessing the documentation There are two ways of accessing this documentation, one of which is what you are currently using. The first method is accessing it online, which will be availble at http://linuxmint.github.io. The second method is to access it locally. Install the program devhelp and the cinnamon-doc package (might be named differently in different distros or included in the cinnamon package itself). Then run the program devhelp to access all documentations you have installed in your system (not limited to Cinnamon). Javascript documentation The Javascript documentation is divided into sections according to the files they are from. The title of each section is how you want to import it. For example, if you want to use the IconApplet object from imports.ui.applet, then you type imports.ui.applet.IconApplet. Since that is very cumbersome to type, we usually declare it as a constant first. For example, you will often see const Applet = imports.ui.applet; This way you can access the IconApplet object via Applet.IconApplet instead. This is how the individual pages are named as well. The _init function of each object is the constructor. So if you want to know what happens when you call new Applet.Applet, you look at the _init function of Applet.Applet. There usually isn't much helpful information apart form what each argument does. Certain variables and functions are prepended with _, eg. _showPanel. These are private variables and functions which shouldn't be accessed normally, and is there purely for referencial purposes. It is generally not a good idea to use them in applets/desklets. Within Cinnamon itself, you may use them when necessary. Note that the _init is also a private function, and you also shouldn't call it directly (unless you are inheriting the object). It is implicitly called when you call the constructor (which technically can be totally unrelated to the _init function. Each object comes with an object hierarchy, documented in the Object Hierarchy section. For example, Applet.IconApplet inherits Applet.Applet. The child can, obviously, access the functions and properties of the parent. However, this information is not repeated in every child. So if you cannot find the function you want in the Applet.IconApplet page, you can look at the Applet.Applet page as well. This is also true for the C documentation. C documentation The items described here are common to all C modules imported, not just those relating to Cinnamon. Reading the documentation of C modules is tricky, since they are written for C, and we are in Javascript. In particular, C has no concept of objects, while Javascript does. Thus the naming convention of many things are different when used in Cinnamon, and the documentation is not directly applicable. The following sections will describe how to translate C into Javascript. Note that most of these apply to Python and other non-C language. Objects C has no concept of objects, but GObject allows C code to create things that look like objects. For example, St.Bin is something that looks like an object, and acts like an object in Javascript. If you want to look up the documentation of St.Bin, you search for StBin (without the ".") and the documentation will come up. The removal of the "." is since, in Javascript, we first import the whole St, and then take the "Bin" object from St. This concept is non-existent in C. In C, it is simply StBin. A notable exception is with Gio and GLib objects, in which the G prefix is used in C for both while Gio and GLib is used in Javascript. For example, the C object is GSettings while the Javascript object is Gio.Settings. Translating object names is the easiest of all. Functions If you look at the page of StBin, you will see that the functions have very long names like st_bin_get_child. Since we are just pretending to have objects via GObjects, and objects do not genuinely exist in C, the functions do not "belong" to the objects. We simply put the name of the object in front of the function name and trust people will actually use it on the proper object. In C, objects are imported as if they were objects, and functions do belong to the objects. So if actor is an StBin, we do not do st_bin_get_child(actor), but simply actor.get_child(). Note that the C documentation always tells you that the first argument to the function is the object itself. For example, st_bin_get_child takes in an StBin as the argument. We do not need to supply this. Note also that not all functions are available for use in Javascript. If some functions are considered to be too C-like to be used in other languages, it will not be availble. There is generally no simple way to figure out whether the functions are available, but usually they are. If they are not, Cinnamon will complain loudly that the function doesn't exist. So the best way to know is to try it. Again, Gio and GLib functions use the g_ prefix, eg g_settings_set_int instead of gio_settings_get_int. Properties GObjects have properties. These can generally accessed directly. For example, if actor is an StBin and you want to access the x-fill property, you can use actor.x_fill. Note that we translate - to _. Sometimes, for some absurd reason, the properties cannot be accessed directly. In this case, there is still hope. You can access the property via actor.get_property("x-fill") and actor.set_property("x-fill", false). Enums Enums are the trickiest of all. An example of an enum you might see is as follows: enum StButtonMask StButtonMask A mask representing which mouse buttons an StButton responds to. Members
ST_BUTTON_ONE button 1 (left) ST_BUTTON_TWO button 2 (middle) ST_BUTTON_THREE button 3 (right) The correct way to access these enums is St.ButtonMask.ONE. We first start with the namespace St, then the name of the enums. Note that this does not relate to the name of the members of the enum. For example, if the individual enum members are called HELLO_WORLD in C, you still access them by starting with St.ButtonMask. What to put after St.ButtonMask is trickier - it is the name of the member, but what should you take? You usually have to strip out some things from the left. In this case, you remove the ST_BUTTON and retain ONE. There is no fixed rule to what to remove. However, it is usually the name of the object (StButton in this case), or the name of the whole group of enum. You can save yourself some guessing by typing St.ButtonMask in looking glass. Double-clicking into it will show you what you can access. In this case it shows ONE, TWO, THREE. Note that both St and ButtonMask are in CamelCase, but the name of the enum member uses CAPS_WITH_UNDERSCORE. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/xlet-versioning.xml0000664000175000017500000000613412625666430024626 0ustar fabiofabio ]> Supporting multiple versions With a new version of Cinnamon coming out every 6 months, there might be some cool new feature you want to use in your applet. However, you also don't want to leave out the poor users using the older versions of Cinnamon. Alternatively, when a new version of Cinnamon comes out, your extension is no longer compatible with it. Updating it would, again, leave out the users running older Cinnamons. Here extension versioning comes to the rescue. Since Cinnamon 2.6, it is possible to ship different versions of your extension for different versions of Cinnamon. Here "extension" will refer to all extensions, applets and desklets. To enable extension versioning, extensions have to add the line "multiversion": true in metadata.json. The different versions should be put in different subdirectories. For example, the version for 2.6 should be put in extension@uuid/2.6/. The contents in 2.6/ should be exactly what you will normally put in extension@uuid/, apart from metadata.json, which should always be put in the parent directory, ie. extension@uuid/. You do not need to create a subdirectory for every single Cinnamon version out there. Cinnamon looks for the most recent subdirectory that is not newer than the running Cinnamon version. For example, if you are running 2.6 and there are 2.4, 2.8 directories, the 2.4 directory will be loaded, until the you upgrade to 2.8 in which case the 2.8 directory will be used. Minor version numbers can also be used, eg. 2.6.4, and are sorted accordingly. If no suitable directory is found, then the contents in extension@uuid/ will be loaded. Note that Cinnamon versions prior to 2.6 will not understand this directory magic, and will always try to load the contents in extension@uuid/. Hence it is suggested that extension maintainers put the version for 2.4 in extension@uuid, and create new directories if changes specific to newer cinnamon versions are made. Don't make a new directory whenever a Cinnamon version is out since it is just a waste of space (and maintenance effort). Note that this also allows you to support future versions of Cinnamon. For example, if you see a cool feature in the development branch of Cinnamon, you can modify your code and put it in the 2.6 folder (assuming the current version is 2.4). Then once the user upgrades their cinnamon version, the new code will be used instead. This also allows extensions to be smoothly upgraded between versions. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/cinnamon-tutorials-docs.sgml.in0000664000175000017500000000234512625666430027014 0ustar fabiofabio ]> Cinnamon Tutorials This document is for Cinnamon @VERSION@. The latest version of this documentation can be found online at http://developer.linuxmint.com/reference/git/cinnamon-tutorials/. Documentation Cjs Extension system Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/settings.png0000664000175000017500000004232712625666430023321 0ustar fabiofabioPNG  IHDRX/EbKGD IDATxwxUglʦ-; ("b/bÊ**6"EA^BM()3ce{W);gLp9sf8C=b=Q]`8P;jMS  kKE=lE޴',q  k}N,;Kv7ޅT&&] `ٴ;Qva?o$8X_YZA;(J%qY,־y,Xq Λ ߟ*d[`)jNڶM.MRQKF)ЛE>TOw VK,˲\jKeAAU^`oy^[H+ @هx<7"Rv@/5EQQum=fu {S$IOPM&Q#|LjxbjAөJEyU6Jn$INܹsoӉ&5%IRFVΈFN~R,V(XQþj t0 7oYZGaawWLv,TvA񔻢ܶ@o+rN6m|  ŒUonذF^o0zNt~d߱&KABZ+AݎPSSS՞Xu:0׷.q;X=j඿w<Ádk^_0uXi2`Z"j錌SkYD8NEPm,^J>G5+TjR[mFk^Դs`&}Z75KV=XQhs۲ijyn"Ng jynBs[NxCGm]zepz@D38zIg L98p58FE8e0z[] R/}AB wƤLr䦞7|Ym8}V1rnIKM4/Ă#3E9 ^Te(V8/"{`-Jל7 2|k?qe$&{V>0"HD8ǎڎ~1ۡ,3t掫⸌~$%_"kdAcyDD%oQĨnj;$ev7|P'wϘ>CAƎ]g Ǝm,QYȓ[?>.U=,vdgXyVޓ{md=S2]bPہ)SouYRi1 cZ圢Z%lDm)8WPF(ysDݻOHƌZ[Vu[Ĩ;D&m:Zv-p n?}QU_~lf C^f,']v԰ -U Hfw 4m. ̯=a;?}' %2"s!FOڟ??a-+9EDK+ "?,bC?~I7qAI'OeS&svZ[OO~{Cʴ}UVm"p{:txS^z}~Q@1*?6pJ/=ynGwoؠ~<1Y)w+Q;zۗ~{0@h=z~ c|K3YG7}M7sU0X Λ.&0h4RmcΝ;m6֭[۲e(;w--(N'ُH{n=.ˏ w.}d]_7řϪ@P`3?۾}(!((h6%.)L AUåq +֑<~;ne&ۜt<[Fem@;&p({7,bX8-KmL.5Ґ'%MNgem\ ;;'1kLp]jR>̏NRe]2J![)TY hMuIvHҥ'leU|J4orzÅy$d3NyH97'}Zv%E/ᒉ8A9qUF筚G+wy^W'x'"EQjQM+ 7.4(|H~=k۩mth:,&td.vp.P֮YDڻIf"vg^:6x"I.HG!Nb0#bO:Pꨬڜ=2W]Rg \J9THڗ$EDTҷRKQC.yvi$8kkӦh.Ѭ})\"Ж"ypk&筚G1cFˏ;VBMN|wD4yZv>,j%v`6RP}[E.yI;2BE(Ḍth(aD}߮Y 72) :M*b2fR`Uoja 3sr;/ۣ䠰RQmty{4wJ/og%gsHߟъ-zacrȶcG+"ʹ)99outaf=&r8
A\|5]6w_U}tPG+?]õ+~~~~~~]ta8pn9s&>>%M_*˲lu*#Fre-f-LoycB!Ysy Ei+ BP^c9)gf͑F>GG$X/@=С%&&b[QCBB233Ձ zgv[V/FDQy1e;ƘteVs߶NKN!wֆ Ο+8_n Kbse?H?u?xa;gƉIJTDJ{,ze8OJqwO(,< Rs%"]BxɥTrjI9I9,M.Z꽡RS+*jvu?:'*Ǔ~@ekV XjG(ԣI)D#]“WJ'NJ9#c.3jh{zn}ZP%RrߩuatQf"ّz|aSWڎ &?,8ۨbԄ";Z м5/`7u%1KMΫ;Qcjy4~.PjA FE9jAAB0FE@Ҋ$%%?`0X6m|ݺuZjְ].+00P)Pnq}KB,p~48A,x<3 4l !fx[A8@D]tY|,fEYmSIq={ڷo #"c RqܩS-ZyfEQz((zEQ6oxSN5{t ˩_u~#c&| Qrv~/86==ʏ/d(gVq!4t\CY,hFDzZNg2ijXIkQw K~[w2ovaR9n~)YSvAǎ5~poƏ?Ox$5x䣆?vV?_CdžDExr,?xRj=#LDP)m,nWǞp쥧Ȧљ:?ڿ|gW:jrAoiS1Gg~踪_ׄс>vױ}' c[?rU<1u/}PhXY>i9Q!K l{>m;kÏdx&cqE]40;zTEr! |Ϝ9cX‘ ,p:D4bĈ'x›Nڲe 4yl"sY[^cF ;T._ʸn0r⽋6ss?_7FuZ,~U7  Jݟ&r_ cb{?^F5trw8v.O:7x*4O5zuTȵ)tVoڔ-._08ueGaL? H cn[ޒ{W;gFPv.jR sss}6sss}{EQ4#MY,\NG.MmMk 6?i |t[y8+9{|X1f*:kn,˃>~xFFw>l0O7mVK$֊۝|V=7<1v_ekR/,ѧ}t5VГգMsQzwvv ڕODB٪^ٗ}5/]ڽw'{̖?eXu<1x:No'%%%I5kրh۶m!!!:N]7{lI󛼩Z??́!miAcugLmJVùU =ƈ7FߺIn;w}FkW~b/>%g)y[ /4D}K_}! /+ySTk>Po@cyyy(v̙3BAwIDHDݻwma"*)=l6&WYb&lHdX>ӽzZbENNY֙3g\!r5u}k1\cCEuሉ)(( utz_u Z7y<ѣGO0\.W^^cL&]9EPf6e/#á鐆4E}lV V;ufZeG'00\WHOO߶m$I IȦ@9|HDDDDDD"uQAT7%(AB0FE@4~`bbbEaÆK9h0X@4 A@4 A@4 A@4 A@4 A@4 A@-]Q\EQtGCHGТKVM^.S8rEd^?p@Р;j9}%''nݺMRUSEKT4!7\ѰA033sذa(vXp׵kןA@.Z5{OErs ].%O1f2$Ij@ӸĮkT>s!W4lTUrx//(%y\.S;<!sr?Ê5~% =c?wvJ3?keU^EIJJ"K{| ꫯYv1^?u{PeyyyG!;.X́pQ;{lؙDtVccc:P8駟6c?ܹs9kY`卢1ϟc96%4rk&d2\.lRCD3p8??u]{q5n IDAT,Z.(ŴCtǭ[.88Dz'NA\4&)OQppKEmq{]KOOq:. F))ѳo%̇D34ݱÛuU ?Oq_~ Ix~e:c>o#z(Rm~kݼhSfũksɒ%A>"dK_;`BB''п"'#˲"JSdY圜ZuBP#TP} B?ۊz %=M`ٖ Ӧ{ Hʗ=3W+K!Z}{8Ձͪ[8nΜ9/hT{?x⠠ ߟg̛77tgI6k=|usfkmH۵7L)ú^u[D??{g{Ӝ%v!tϖ|~oSYO>7=rޛ"wxvWc |l6[JJ)-{F*SɆȾY-ZhW|QPy]vw{-Eo|<u̕O.1FJϾYd[3>хkUE 6dSlMxmވPJg=Pܣ(ȧy1)1/.rf yeИoI7f{u^]= ez ҷoh_u?}sW9OmXgN7?xϘ/ρ_{^cE( 5萫"A|WmڴwLf>}z̙>lǎ8,^TԩSGyS&u`X;_8ЮuT(c3"wS~HJG|3O Rl\:x.ǜOs9\'W?{ޛ%O_NxoffA(|TBtrAI¦;u-8zꐐY1|ɯ'|sYR罶iG_^Q!|.ǃ>ew]Õ^ʮtJRE@y?eY#p”9Rm/:s_ x/# ?#˿Oxo]RtZ+ Ͻa;w {rr8N׉Ob}w6l?nw7ƻU1fZM&ܹs׬Y((:VXny_fܹsM&jeqX߾Ľӧ̘bKZ-Ƀ"E"1b6v񁝇hlysa 7:aSwNe8u6A~w\| ݰ.%x+?8lk`3A,!cLGp1຅/1wSWgkYЋ:`3_<ܒ+pu *ډW;"j/;Hް8VzVA`]vǗ- X]*DQ뮻EQϑU}iu(C ޽{wTi2>؉ּ!M""9\9")';7Wga6}Yg 3n9t?<08?f%DCD"<5r`1lw5}qТA}o 5R=D"s圷)15dJu>8H>%4@2avm_+?q.g]C}ս][B \IK9 >b>uGe>AcDP=j\9}tllpwioL\r%)GԐk=z>j6].RTϑ1rf>:zhƘOu_rf[C,sxlS\n}htg?m9/1}C-;W~=ͩ(SNLam'͡0ّ{{b%5EA$X_acaPX(I5~]ɧ b _p^l_*3Y8SnSB2v9%HS~aH&<{`ǝS8ʔ_|*7r \b3}~(?ۅ>#[{=G2$ZdPCtg7nU>ƍ7{lN=zѣnjN=c o,'Nt:L)𪫮*Ν;S$Irq CwXng9? ctؿxxl?}Mm Q53z֬Cb;u 1Ƙ>~YeϞ-XZd8>~3r+>sKvt~*zhu!;;;a)=L9=#hbvttdd$|J aļɐ8&i.G-+ڰ%Q'ۮyÐp[/r䄗(wCTʁ0+{cؑHκ犯/<>ԩv('K~*:1֧X %nOD1ctw>bڠ(PEQ&O?tԩ܏L &(Pk&+FȲx$Ir\n cVR1qļ<*@iQ͛Wɶ/ݤStլWJq^MM Q<^x1&˲N׻\.jZ,l6LAӉvnWM:Xw,˯wq%g6&7Fn&J<|bZ7Uވ+]x":@%Tw,|qϛJn7Za6! 8ɕ>U~Ȍ1өg-K}ƒ%K6oܫW/U&M:tYʦ&d/oe9??_~ &RLL v.lܥki`0TԆT1L͗^t3>;SkwKI(iW/]nnwͳ nu6ݤ@7gΜ.lǎ[=W\ql:xV'Nlܸ:uWbCY-dGF%4Ym:!L&ۻ0YӪJIJarW*TnAnLoo{K2d勈Ax9{饗z)"R'c/,cȑ6-11QݤC#Gt8'4^pGlXe˖DbŊ^2f[1h\r/#OKD)=zYpQ_A5ve[F+,+Fҥ:K.%ޱDt:Ǐvէ̌?^5[^Su AXtiHH^'˗?e4s#FHG4 Dx<M];g,r-Dp8O;aDQ=iҤ_uԨQniA5D?䓥.4yF.W4Jk<Ϸi1"K.R5mTEЛXNx7Q?7pxBTԔ-1Ǔۄu,7ɲl6t:i4aĴw|½+~S8?_կ~rΚڀ rȸ};XJ@'nxοøi}7s6Q4AOOxB Cgr& AmsޤS ~zrJ ҆ ͂z[K7!J ͂&JH<&""w }L\PVPؘ,I[)$Ï%o][Cذzvƪ@뛳p'ģ\Vwݍ]H췈inGA_/>mڬONt?7̛q6YoxNfo7p=Vz)trVp~&>Ff5M'}3Q 7KyÍMH4thB`2\&im|۩vw3{)wUַ̟GI/p|̝1gƕl=U@qnQq&Ǯ+"W}szk;xF,u0-'_0ecZ{[v.=Vsd2_aܥұo[;g&>+g+9c>3-;v,ɜ|^ȑ?B.w@$7Ow ~-͘;kX@RƎ5?ey>w?koVw[bn΢Z(JfUS\tӆ-ڙ}cueT(rgʯJ=p^ƥ 52:pqj <#. 2D˶EI}>ψXzLkLIwC=mBB $S_L#Ev!UZ zy>))Û#ƫO:-=!ܾL7jyS Kړv%mZk}# jڟ'WM=O }vu9rs9j[>g#WE?j85z%nno{eAq}֔q׷$"'q^'DDhaD-iI*jz,EIWy-W~?bO7|OJ1|=D9į ~!FGJLl^Oy8^n&;;;"~عXK!Kq:GWn}|KB\ywmg*£" \;C?I峦1wf-Uo+ S$j)Rh[+FEYR*#V^$, scK?] }֮V:Uxt.R5|`ѲK,Yd鲗ojo\UoKa)@ b{xk>"!${סlHJ^[V=|97_~H۷NL0x爈{p@ zϠ=)n 7 $g qW w՗;]IyG 2čb=brOy,n->]3Gs_g\E*8Zڳg_iӦ͛7_IIIu(jq[]?'Z?sy)h.߮uWT)o`٫w-dV=>#ѩ[ E w]o>wE,\nTPؓ~{w4#of{%1lPz$"M{tj[OK~0 ? 6s!~S?|dknҲ 7<=cb'}`LaY3[rS|9|o; [󟷖oIx|xeu(kiϞ=o߾rmٲ E ChpS.8cLQY=$I.pܜ0vr=x`Ν͉; ziq?[}|ܹ322rȑeWΝ6lXW T֯_rVj X,fd2 N' u>m۶6m4@tIDATAckѢ(?CkZ 8Zl[jt:7oެ*z /Ah(<855u߾}7ov8zbtܹe˖M];@&Ե.#Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q QE7DIENDB`Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/building.xml0000664000175000017500000000737112625666430023272 0ustar fabiofabio Building Cinnamon
Debian-based systems
Add APT sources repositories Open /etc/apt/sources.list. For each deb line, add the same line with deb replaced with deb-src. For instance, here's how it should look like in Linux Mint 13: deb http://packages.linuxmint.com maya main upstream import deb-src http://packages.linuxmint.com maya main upstream import deb http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse deb http://extras.ubuntu.com/ubuntu precise main deb-src http://extras.ubuntu.com/ubuntu precise main
Get build dependencies Then we install the packages needed to compile the cinnamon stack. Run the following in a terminal (as root): apt-get update apt-get install dpkg-dev sudo apt-get build-dep cinnamon cinnamon-control-center cinnamon-desktop cinnamon-menus cinnamon-screensaver cinnamon-session cinnamon-settings-daemon cinnamon-translations cjs muffin nemo Now get the latest git code for everything. Run (not as root): git clone git://github.com/linuxmint/cinnamon.git git clone git://github.com/linuxmint/cinnamon-control-center.git git clone git://github.com/linuxmint/cinnamon-desktop.git git clone git://github.com/linuxmint/cinnamon-menus.git git clone git://github.com/linuxmint/cinnamon-screensaver.git git clone git://github.com/linuxmint/cinnamon-session.git git clone git://github.com/linuxmint/cinnamon-settings-daemon.git git clone git://github.com/linuxmint/cinnamon-translations.git git clone git://github.com/linuxmint/cjs.git git clone git://github.com/linuxmint/muffin.git git clone git://github.com/linuxmint/nemo.git
Compile and install the stack Build and install in the following order. Some packages are required to be installed prior to building later packages. See below for how to build and install. cinnamon-translations cinnamon-desktop cinnamon-menus **INSTALL PRECEDING PACKAGES** cinnamon-session cinnamon-settings-daemon cinnamon-screensaver cjs **INSTALL PRECEDING PACKAGES** cinnamon-control-center muffin **INSTALL PRECEDING PACKAGES** cinnamon nemo **INSTALL PRECEDING PACKAGES** To build a package: cd *package-name* dpkg-buildpackage cd .. To install a package: sudo dpkg -i *packages produced*
Optional: Compiling from stable branch The instructions above compile the Cinnamon stack from their "master" branch, which isn't always stable. To compile from the stable branch, instead of the usual building procedure, you need to to the following (for all packages): cd *package-name* git checkout stable dpkg-buildpackage cd ..
Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/write-applet.xml0000664000175000017500000003173012625666430024106 0ustar fabiofabio ]> Writing applets Introduction In this tutorial, we will go through the process of creating a simple applet to learn about the Applet API. Here we will create a Force Quit applet. For those who are unfamiliar with “force-quitting”: When a window becomes unresponsive and doesn’t want to close, the most efficient way to force it to close is to kill its process. You could use the “ps” command to find its process ID and kill it with the “kill” command. Or alternatively you could run the “xkill” command, and simply click on the window you want to kill. And that’s exactly what our “Force Quit” applet is going to do... after you click on it, your mouse cursor will change into a window killer, which you’ll target at the window you want to get rid of :) Creating the basic structure of the applet An applet has to be given a unique ID (uuid). In this tutorial we will name it "force-quit@cinnamon.org". Of course, you should give your applets your own UUIDs, using either your name or your domain name behind the @ sign. An applet is basically a directory, whose name is the UUID, containing two files: A “metadata.json” file which contains information about the applet, such as its name, description etc.. An “applet.js” file which contains its code. Applets go in ~/.local/share/cinnamon/applets (or in /usr/share/cinnamon/applets if you want them installed system-wide). So let’s go there and let’s create the files and folders necessary to any Cinnamon applet. cd mkdir -p .local/share/cinnamon/applets/force-quit@cinnamon.org cd .local/share/cinnamon/applets/force-quit@cinnamon.org touch metadata.json touch applet.js Defining the applet metadata Let's open metadata.json and describe our applet. This file defines the UUID, name, description, and icon of your applet and is used by Cinnamon to identify it and display it to the user in Cinnamon Settings. { "uuid": "force-quit@cinnamon.org", "name": "Force Quit", "description": "Click on the applet to launch xkill and force any window to quit immediately", "icon": "force-exit" } By default, only one instance of every applet can be placed on the user's panel. But if the user has multiple panels, they cannot have one force-quit applet on each panel, which is bad. Hence we should also add a max-instance property. We can specify any number we want (eg 3), or we can make it unlimited by making it -1. In this case, our new metadata.json would be { "uuid": "force-quit@cinnamon.org", "name": "Force Quit", "description": "Click on the applet to launch xkill and force any window to quit immediately", "icon": "force-exit", "max-instances": "-1" } There are more things we can add to metadata.json, but they will not be covered here. Writing our applet Here is the code for our applet: const Applet = imports.ui.applet; const Util = imports.misc.util; function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this.set_applet_icon_name("force-exit"); this.set_applet_tooltip(_("Click here to kill a window")); }, on_applet_clicked: function() { Util.spawn('xkill'); } }; function main(metadata, orientation, panel_height, instance_id) { return new MyApplet(orientation, panel_height, instance_id); } Now we'll go through the code one by one, starting from the bottom. function main(metadata, orientation, panel_height, instance_id) { return new MyApplet(orientation, panel_height, instance_id); } The main function is the only thing Cinnamon understand. To load an applet, Cinnamon calls the main function in the applet's code, and expects to get an Applet object, which it will add to the panel. So here we instantiate an MyApplet object (whose details are defined above), and returns it. You will notice that there are a lot of parameters floating around. metadata contains, basically the information inside metadata.json plus some more. This is not very helpful in general, but can sometimes provide some useful information. orientation is whether the panel is at the top or the bottom. The applet can behave differently depending on its position, eg. to make the popup menus show up on the right side. panel_height tells you, unsurprisingly, the height of the panel. This way we can scale the icons up and down depending on how large the panel is. instance_id tells you which instance of the applet you are, since there can be multiple instances of the applet present. While this is just a number assigned to the applet and doesn't have much meaning by itself, it is required to access, say, the individual settings of an applet (which will be described later). If that sounds like a lot of things to take care of, you should be relieved that the applet API handles all that for you! All you have to do is to pass it to the applet API. Here we are passing these information to the constructor, and the constructor will later pass it to the applet API. So what exactly happens when we create the applet? We first look at the first two lines: const Applet = imports.ui.applet; const Util = imports.misc.util; Here we import certain APIs provided by Cinnamon. The most important one is, of course, the Applet API. We will also need Util in order to run an external program (xkill). When we write imports.ui.applet, we are accessing the functions defined in /usr/share/cinnamon/js/ui/applet.js. Similarly, imports.misc.util accesses /usr/share/cinnamon/js/misc/util.js. We then call these things Applet and Util respectively to avoid typing the long bunch of code. Not very interesting. Next! function MyApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } This is the standard constructor of a Javascript Object. When someone calls it, it calls the _init function of the object, and does stuff. Note that here we called our applet MyApplet. You are free to call it whatever you want (and change the main function accordingly, of course), but there really is no reason to do so!. Also note that we pass all those orientation etc. information down the chain until it ultimately reaches the applet API. What we've gone through is mostly boilerplate. Now we get to the meat of the code: MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instance_id) { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this.set_applet_icon_name("force-exit"); this.set_applet_tooltip(_("Click here to kill a window")); }, on_applet_clicked: function() { Util.spawn('xkill'); } }; Roughly speaking, the prototype is the list of all functions of the object. Here we have the _init function and the on_applet_clicked function, which is all we need. Here we see what I've refered to as the "applet API" all the time. A few barebone applet objects are defined in applet.js, and we inherit one of them. Here we choose to inherit Applet.IconApplet, which is an applet that displays an icon. Inheritance in JavaScript is slightly weird. We first copy over Applet.IconApplet's prototype using the __proto__: Applet.IconApplet.prototype, line. This copies all the functions found in Applet.IconApplet to our applet, which we are going to use. Next, in our _init function, we call the _init function of Applet.IconApplet. Here we pass on all those information about orientation etc to this _init function, and this function will help us sort out all the mess required to make the applet display properly. However, contrary to popular belief, the applet API is not psychic. It has no idea what your applet wants to do (apart from displaying an icon). You have to first tell it what icon you want, in the line this.set_applet_icon_name("force-exit"); Note that set_applet_icon_name is a function defined inside Applet.IconApplet, which makes the applet display the corresponding icon. By using the applet API, we have saved ourselves from the hassle of creating icons and putting them in the right place. (force-exit is the name of an icon from the user's icon set. The icons available for use can be found in /usr/share/icons/) The next line is this.set_applet_tooltip(_("Click here to kill a window")); which says that the applet should have a tooltip called _("Click here to kill a window"). By wrapping the string with the _( ) function, we are telling Cinnamon to translate the string to the correct language, if translations are available. That is all the code in our _init function. Now we have to wait for the user to click on the applet. The applet API automatically helps you to listen to these events, and when the user presses the applet, the API will call the on_applet_clicked of your applet. Here we have on_applet_clicked: function() { Util.spawn('xkill'); } which calls the spawn function from Util, and launches an external command, xkill. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/xlet-settings-ref.xml0000664000175000017500000002640412625666430025057 0ustar fabiofabio Applet, desklet and extension settings reference This is the reference for the settings API. Widget types and required fields checkbox type: should be checkbox default: true or false (no quotes) description: String describing the setting A simple checkbox that controls a boolean type value entry type: should be entry default: default string value description: String describing the setting A text entry field that stores a string colorchooser type: should be colorchooser default: default color string - can be "red" or "rgba(x,x,x,x)", etc... description: String describing the setting A Color button that lets you choose a RGBA color code as a string keybinding type: should be keybinding default: default keybinding string - i.e. <Control>F8 or other string parseable by gtk_accelerator_parse. description: String describing the setting An input that allows you to select a keybinding for an action. radiogroup type: should be radiogroup default: default value from the list of options, or it can be a custom value if custom is defined description: String describing the setting options: node of desc:val pair options, where desc is the displayed option name, val is the stored value A group of radio buttons whose description and values are defined by options in description:value pairs. Values may be string or number. also have a value of custom, and a text entry will be provided next to that option, to allow entering a custom value. Options might be: "options" : { "Option 1" : "this value", "Option 2" : "that value", "Option 3" : "other value" } filechooser type: should be filechooser description: String describing the setting default: Default filename to use select-dir: (optional) true or false, or leave off entirely. Forces directory selection. Opens a file picker dialog to allow you to choose a filename. If select-dir is true, it will only allow directories to be selected. Stores as a string. iconfilechooser type: should be iconfilechooser description: String describing the setting default: default icon path or icon name to use Provides a preview button and text entry field. You can open a file dialog to pick an image-type file, or enter a registered icon name in the text field. Stores as a string. combobox type: should be combobox default: default value to set description: String describing the setting options: node of desc:val pair options, where desc is the displayed option name, val is the stored value Provides a dropdown list from which you can select from description:value pairs defined by options. The values can be string, number, or boolean. spinbutton type: should be spinbutton default: default value to use - int or leading min: minimum value max: maximum value units: String describing what the number is a unit of (pixels, bytes, etc..) step: adjustment amount description: String describing the setting Provides a spin button and entry for changing setting a number value. This can be integer or floating point format. For floating point, all values must have leading 0's. scale type: should be scale default: default value to use - int or leading min: minimum value max: maximum value step: adjustment amount description: String describing the setting Provides a scale widget to allow you to pick a number value between min and max, by step amount. Integer or floating point numbers can be used. For floating point, all values must have leading 0's. generic type: should be generic default: default value A generic storage object for any type of value. This is generally intended for internal settings that won't be adjusted by the user. For example, a history, or most recent command. There is no corresponding widget for it in Cinnamon Settings. header type: should be header description: String to display as a bold header A non-setting widget, this provides a bold-faced label for assisting in organizing your settings separator type: should be separator A non-setting widget, this draws a horizontal separator for assisting in organizing your settings button type: should be button description: Label for the button callback: string of callback method name (no "this", just "myFunc") A non-setting widget, this provides a button, which, when clicked, activates the callback method in your applet. Note: the callback value should be a string of the method name only. For instance, to call this.myCallback(), you would put myCallback for the callback value. Additional Setting Options These fields can be added to any widget: indent: true: Indent the widget in the settings page to help with organizing your layout. dependency: <key>: where key is the name of a checkbox setting. If that checkbox setting is un-checked, this setting will be made insensitive (greyed out). The checkbox must occur before the setting that depends on it. tooltip: Adds a popup tooltip to the widget Signals <code>settings-changed</code> Signal when the underlying config file has changed and the in-memory values have been updated. <code>changed::<key></code> Signals when key has changed in the configuration file. Use this in conjunction with getValue if you want to handle your own updating in a more traditional way (like gsettings). The callback function will be called with three paramenters: settingProvider, oldval, newval, which are, respectively, the settings object (which you usually don't need), the original value and the updated value. Additional options in metadata.json You can add the following items to metadata.json to affect how the settings are presented to the user: hide-configuration: Hides the configure button in Cinnamon Settings. Set to true if you are using only generic-type settings that should be hidden from the user. This is not a mandatory key. Omitting it will allow the configuration button to hide or display depending on whether you are utilizing the settings API or not. external-configuration-app: Allows you to define an external settings app to use instead of the built-in settings GUI. This should be a string with the name of your executable settings app (path relative to the applet's install directory). Note, this key can be overridden by the hide-configuration key. This is not a mandatory key. Omitting it will allow the configuration button to hide or display depending on whether you are utilizing the settings API or not. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/xlet-settings.xml0000664000175000017500000002714012625666430024303 0ustar fabiofabio Applet, desklet and extension settings Introduction This document is intended to serve as a guide and reference to the new Cinnamon applet, desklet, and extension settings API, new for Cinnamon 1.8. The goal of this API was to provide for an easy to implement and use storage system for settings. We will start with a broad overview of the system, and then work our way into the details. Overview In a nutshell, your applet will provide a JSON file that defines various setting types and their details, including a key identifier, description, default value, minimum and maximums (in the case of numeric types) and other options pertinent to each type of setting. When your applet is added to the panel, you instantiate a settings provider, which looks for this file, parses it, applies default values, and saves an "instance" copy to work with. Once that setting provider is initialized, your applet can then bind properties to the various setting keys from that file, and use them to set up how your applet will look, feel, and operate. There are a few types of bindings available, depending on how you want your applet to control or be controlled by these settings. That is the "live", applet side of the picture. The other side of this, is that same JSON file you created for your applet is also used by Cinnamon Settings (or System Settings as it will be known in 1.8,) to automatically generate a configuration panel to adjust all these settings. There are a number of formatting options, as well as the capability for some settings to be "dependent" on another setting - allowing you to deactivate or "grey out" settings that cannot be used in certain situations. Changes made to this panel are saved immediately, and can be handled by the applet via callbacks that you defined in your applet. The settings-schema.json file Here’s a basic example of a settings-schema.json file, that can define settings for your applet: { "width": { "type" : "scale", "default" : 10, "min" : 2, "max" : 400, "step" : 1, "description" : "Amount of space in pixels" } } The most important item here to start is the key: "width". This is what your applet is going to specify when binding a property to the setting provider. Within the width node are the various details specific to that key, and differ depending on what type of setting you’ll be using. In this case: type: defines what type of widget you want to use to adjust this value. In general the names of these types will be similar to what GTK uses, but always lowercase, and single word. default: defines what you want your default, or starting value to be for this setting. Since scale defines a numeric type, the default will be a numeral (no quotations). You can use floating point, but you must have a leading 0 for floating numbers less than 1 (i.e. 0.1). min: the lower limit for your setting. max: the upper limit for your setting. step: the step amount for this setting - how much the value changes for each click or notch of the widget. description: The description you want by your setting in the configuration panel, to explain what the setting controls. The JSON file must be valid - it must pass any structure tests, not have extra commas, and so forth. There are a number of tools available online where you can paste the contents of your file in, and it will tell you if it it valid or not, and point out problem areas. One such site is http://jsonlint.com/. So now we’ve got a settings-schema.json file. It goes in your applet directory, alongside the metadata.json, applet.js, and any other files you have. Preparing your applet: The first thing you need to do is add Settings to your imports at the top of your applet: const Settings = imports.ui.settings; You’ll also need to ensure that you’re passing along the instanceId of the applet from the main() function: function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId); return myApplet; } and function MyApplet(metadata, orientation, panel_height, instanceId) { this._init(metadata, orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, _init: function(metadata, orientation, panel_height, instanceId) { .... .... .... Note: Desklets only need to pass metadata and instanceId through this chain, and extensions have no need of any of this. Initializing the settings provider Now, to actually initialize your settings provider, usually somewhere in the _init method of MyApplet, you’ll put something like this, which will use your applet’s own instance variables: this.settings = new Settings.AppletSettings(this, "settings-example@cinnamon.org", instanceId); If you prefer to have your preferences in a separate object, you can do like this, and the object will be populated with your settings variables: this._preferences = {}; this.settings = new Settings.AppletSettings(this._preferences, "settings-example@cinnamon.org", instanceId); In other words, the object reference you pass in can be any object. The UUID there can be just hardcoded, or can be drawn from the metadata (metadata["uuid"]), whichever you prefer. You can also leave off the instanceId if you haven’t defined your applet as multiple-instance capable. Obviously, if instead of an applet, you have a desklet or an extension, you would use new Settings.DeskletSettings and new Settings.ExtensionSettings respectively. Binding your settings Since we only have one setting in our example, this will be very simple: this.settings.bindProperty(Settings.BindingDirection.IN, "width", "width", this.on_settings_changed, null); To explain: .bindProperty: tells the settings provider you want to bind one of your settings keys to an applet property. Settings.BindingDirection.IN: This tells the setting provider that you want your applet property to be updated by changes in the configuration file. Other flags exist to provide the opposite direction, as well as bidirectional updating. More on those later. "width": This is the key value you defined in your settings-schema.json file that you want to match up with. "width": This is the name of the property you want to store the value in your applet. You’ll refer to it traditionally as this.width from here on out in the applet code. this.on_settings_changed: This is the method you want called when the setting value changes. NOTE: The property (this.width) already contains the new value when this method is called. All you have to do is act on it. null: this is optional - it can be left off entirely, or used to pass any extra object to the callback if desired. That’s basically it - your property, this.width, or this._preferences.width, is now managed by the setting provider, and will be automatically updated when the configuration changes, and your callback method automatically called so you can deal with the change. Editing your configuration: Once you’ve activated your applet, you can then go in to Cinnamon Settings, Applets, and the option to Configure will be displayed when you select it. For our sample, we’d see: This has been automatically generated by our JSON file. If we adjust the scale, the JSON config file is rewritten, the settings provider for our applet notices, and reloads the new value into the applet’s this.width property, and this.on_settings_changed() is called. Alternatively, this can be launched by running the command cinnamon-settings applets *uuid* (with obvious modifications for desklets and extensions). More information The available settings items and widgets can be found an the , alongside with some extra options. The settings objects themselves are Settings.AppletSettings, Settings.DeskletSettings and Settings.ExtensionSettings, which all inherit Settings._provider. Most useful functions will be found inside the _provider object. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/documenting-source.xml0000664000175000017500000002220312625666430025276 0ustar fabiofabio Writing documentation in source The C part of Cinnamon can be documented using standard gtk-doc format, and there should be plenty of tutorials on that. The JavaScript part of Cinnamon can also be documented using something that resembles gtk-doc format. Currently, we support documenting files (eg. main.js), objects (eg. Applet.Applet), functions (including functions of files and functions of objects), signals and enums. The documentation appears as a comment right before the thing it describes. In the case of a file, it should appear at the very beginning of the file. If a object is declared using prototype, then it can appear right before either the function declaration or the prototype declaration, ie. before line 1 or line 5 in the example below. The documentation of signal can be placed anywhere within the prototype scope since there is no proper declaration of signals in javascript. function Applet() { this._init() } Applet.prototype = { _init: function() { } } The general format of a piece of code documentation is as follows: /** * name_of_thing: * @short_description: one-line description * @prop1 (type): description * @prop2 (type): description * @prop3 (type): description * * Long description * * Second paragraph of long description */ A comment block should always start with /** Avoid starting comments with this (use /* instead) even though the parser should be smart enough to not parse them, but if they look too like a piece of documentation, the parser might get confused. The next line is the name of the thing being documented. Function, object, signal, file and enum documentations are distinguished using this line. They should look, respectively, like this: * function_name: * #ObjectName: * FILE:filename.js * ENUM:EnumName * SIGNAL:signal-name Note that we do not have to include the namespace of an object, ie. it is #ObjectName, not #FileName.ObjectName. Afterwards is a short description. This is only needed for files and objects, and will be ignored for functions. It is optional, but things look ugly without it. Note that it has to be short, hence the name. It is shown in the contents page in the form Object.Name - short description Afterwards, all the properties of the thing should be listed. A "property" is a globally accessible variable in the case of a file, a genuine property in the case of an object, a parameter for a function, an argument passed in the case of a signal, or an element of the enum for an enum. The type of the property is optional, but leaving it out makes the documentation less helpful and also more ugly (except for enums, where types should not appear). If the description is too long to fit in one line, break it into two rows using a *single* line break. Single line breaks are always ignored when parsing. For example, * @prop (type): this is a very long description. Oh my gosh I am * running out of space! After the properties, a description of the thing should be given. Use *two* line breaks to signify the end of the properties section, and write as normal. It is okay to separate two properties with two line breaks. For example, * @prop1 (type): hello * * @prop2 (type): world is fine, but two line breaks within a property description is not. eg * @prop1 (type): line 1 * * line 2 * @prop2 (type): hello world is bad (line 2 will be treated as a description of the object itself). Instead, if you want the property description to have multiple paragraphs, put a \ character in place of the empty line, ie. * @prop1 (type): line 1 * \ * line 2 * @prop2 (type): hello world In the description section, two line breaks will be translated into an actual line break, and single line breaks are ignored. At the end, in the case of a function, a return value can be specified in the form * Returns (type): description of what is returned Despite looking like a property, the description can in fact have multiple paragraphs. The following is valid * Returns (type): hey this function returns a really cool thing! * Want to know what it is? * * It is a random number! Objects should indicate what they directly inherit in the description, using the form * Inherits: Applet.Applet Note that the namespace is required. Automatic substitutions The current parser is able to perform several substitutions: The type of a property specified will be automatically converted to a link to the documentation of that type, if available. In the description of itmes, @word will be automatically converted to code format, ie. enclosed within <code> tags. In the description of items, *text more text* will be shown in italics and **text more text** will be shown in bold. Note that using _underlines to highlight_ is not supported since the parser will confuse it with private variables. Links to other objects can be done with a #Hash. If you are linking to an object within the same file, you may omit the namespace, eg. using #Applet in Applet.AppletContextMenu. However, if it is from a different file, the namespace must be included. Links to functions, properties or enums of other objects with a %percentage sign. If you wish to link to functions of the same object, the following four are acceptable: this.func this.func() func func() Of course, if it is a property, then you would never want to include the () brackets. To refer to functions of other objects, you have to type the full name of the object. The namespace can be omitted if the object is in the same file. For example, the following are acceptable (assuming you are inside popupMenu.js. PopupMenu.PopupBaseMenuItem._init PopupMenu.PopupBaseMenuItem._init() PopupBaseMenuItem._init PopupBaseMenuItem._init() If there are functions, properties and enums that take the same name, the property will be linked to, unless the name ends with (), in which case it will always be a function. If a function and an enum have the same name, the whole thing will explode into pieces and don't do that. It is a bad idea to have things with the same name anyway. Code blocks can be enclosed in descriptions as follows: ``` this.is = some + code ``` Unordered lists can be created as follows: - List item 1 - List item 2 \ Continuing item 2 with `\` No longer an item Lists cannot be nested, but code blocks can appear in lists Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/importer.xml0000664000175000017500000001512312625666430023330 0ustar fabiofabio The Importer To access code of other JavaScript files, cjs has got the imports object. In cjs in combination of Cinnamon you can use following statements to import statements: imports.* imports.gi.* imports.ui.* imports.misc.* <code>imports.*</code> This is the normal form of importing modules. You can think of this object like nested objects which properties are the JavaScript files or directories. All functions, variables (var, let, const) in a JavaScript file can be accessed like this file is an object. To clearify, an example: //Direct access to file a.js const A = imports.a; //Directories must be also typed in, in order to get file c.js in directory b const C = imports.b.c; log(A.foo); //"Property foo" log(A.bar()); //"Method bar" log(C.baz); //"Property baz" a.js let foo = "Property foo"; function bar(){ return "Method bar"; } c.js in a directory named b let baz = "Property baz"; In every case, you can include cjs core modules. Those provides you useful functions or (less often) bindings to C libraries. Examples are: const Cairo = imports.cairo; //Cairo graphics const Lang = imports.lang; //useful JavaScript functions for extensing the language const Gettext = imports.gettext; //Gettext translation const TweenEquations = imports.tweener.equations; //Tween equations for animations As you can see, it is common to assign the import to a constant in UpperCamelCase which looks like the imported module. To view the source of those cjs modules, you should visit the GitHub page. <code>imports.gi.*</code> As Cinnamon uses C libraries like Clutter, Muffin and more, there is a little problem: How can be those libraries used in cjs? For this, there is GObject Introspection. For short, it allows you to use C libraries in cjs, Python and other languages. C libraries are included like this: const St = imports.gi.St; //Shell Toolkit, the normal way to display widgets on the Cinnamon screen const Cinnamon = imports.gi.Cinnamon; //Cinnamon C libraries, e.g. AppSystem Note: Not like normal imports.*, imports.gi.* imports needs to have the first letter after gi. be in upper case. <code>imports.ui.*</code> Those imports under imports.ui.* are core Cinnamon modules. Some important modules: const PopupMenu = imports.ui.popupMenu; //High-level classes for building menus for applets or context menus const Applet = imports.ui.applet; //Base applet classes The source is in /usr/share/cinnamon/js/ui/ <code>imports.misc.*</code> Those imports under imports.misc.* are belonging to Cinnamon, but aren't tied to it that much like imports.ui.*. const Util = imports.misc.util; //useful functions const Interfaces = imports.misc.interfaces; //DBus stuff The source is in /usr/share/cinnamon/js/misc/ Importing xlet modules When you want to split a big xlet code into smaller files, you'll need to import them. A simple way is using imports.xlet, wher xlet is your xlet type (applet, desklet, extension, search_provider) imports.applet.foo // get foo.js in your applet directory <code>__init__.js</code> When writing xlets, it is common that you have some functions or constants that you need in many files. For that, there is __init__.js. It is a normal JavaScript file, but every function or variable can be accessed directly via import.*. Examples are often used functions, like a modified _() function for translating your xlet. __init__.js const Gettext = imports.gettext; const uuid = "xlet@uuid"; Gettext.bindtextdomain(uuid, GLib.get_home_dir() + "/.local/share/locale"); function _(str){ return Gettext.dgettext(uuid, str); } In your other files: const uuid = imports.xlet.uuid; const _ = imports.xlet._; Remember: replace xlet in imports.xlet to your xlet type. There is no harm renaming __init__.js to something other (like util.js) and using imports.xlet.util.*. Cinnamon-2.8.6/docs/reference/cinnamon-tutorials/Makefile.am0000664000175000017500000000774212625666430023011 0ustar fabiofabio## Process this file with automake to produce Makefile.in # we want $(DOC_MAIN_SGML_FILE) to be built before sgml-build # and sgml-build depends on $(DOC_MODULE)-sections.txt $(DOC_MODULE)-sections.txt: $(DOC_MAIN_SGML_FILE) # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=cinnamon-tutorials # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # Directories containing the source code # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. DOC_SOURCE_DIR= # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--rebuild-types # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS=--xml-mode --output-format=xml # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS=--src-lang=js # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB= CFILE_GLOB= # Extra header to include when scanning, which are not under DOC_SOURCE_DIR # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h EXTRA_HFILES= # Header files or dirs to ignore when scanning. Use base file/dir names # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code IGNORE_HFILES= # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES=settings.png # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files= \ building.xml \ documenting-source.xml \ documenting-tutorial.xml \ using-documentation.xml \ write-applet.xml \ xlet-settings.xml \ xlet-settings-ref.xml \ xlet-versioning.xml \ $(NULL) # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= \ building.xml \ documenting-source.xml \ documenting-tutorial.xml \ using-documentation.xml \ write-applet.xml \ xlet-settings.xml \ xlet-settings-ref.xml \ xlet-versioning.xml \ $(NULL) # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS= GTKDOC_LIBS= # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = $(DOC_MODULES).types # Comment this out if you want 'make check' to test you doc status # and run some sanity checks if ENABLE_GTK_DOC TESTS_ENVIRONMENT = cd $(srcdir) && \ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) #TESTS = $(GTKDOC_CHECK) endif -include $(top_srcdir)/git.mk Cinnamon-2.8.6/docs/reference/cinnamon-js/0000775000175000017500000000000012625666430017331 5ustar fabiofabioCinnamon-2.8.6/docs/reference/cinnamon-js/gen_doc.py0000775000175000017500000002571712625666430021320 0ustar fabiofabio#!/usr/bin/env python # coding: utf-8 # This parser starts by parsing all the javascript code, and representing each # file/object/function as a python class. Afterwards, each object is gone # through individually to produce the gtk-doc-friendly xml files # # The parsers uses the concept of state. At each line, the parser takes a # particular state, and behaves accordingly. Conversely, the contents of the # lines can cause the parser to change state. # In normal text, the parser is in STATE_NORMAL, in which all it does is keep # track of scopes and the number of '{' and '}' brackets. In a comment block # that is not documentation, the parser takes STATE_COMMENT and ignores # everything until we leave the comment. # # When the parser sees /**, it enters STATE_INIT, which tells us that the next # line is important. The next line is usually in the form # # * function_name: # # or # # * #ObjectName: # # At this point, we create a JSFunction or JSObject object, and enter the next # STATE_PROPERTY. Here the parser parses the properties of the form # # * @prop (type): description # # until it reaches something that is not a property (signified by two line # breaks not followed by another property). Then the parser drops to # STATE_DESCRIPTION, and reads every line and puts it into the object's # description. # # STATE_DESCRIPTION continues until the comment block ends, or if it encounters # # * Returns (type): description # # Then it morphs into STATE_RETURN, and puts the remaining lines into the # description of the return value. Alternatively, in the case of objects, # # * Inherits: Object.Class # # specifies the ancestor of an object. If the parser reads this line, it drops # to STATE_COMMENT because there shouldn't be anything interesting afterwards. import sys import os import xml.etree.ElementTree as ET import re from gen_lib import * files = [] objects = {} ROOT_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) + '/../../../' if len(sys.argv) > 1: ROOT_DIR = sys.argv[1] JS_UI_DIR = os.path.join(ROOT_DIR, 'js/ui/') JS_MISC_DIR = os.path.join(ROOT_DIR, 'js/misc/') # Allow types like "object/string" TYPE_REGEX = r'\w*\.?\w+/?\w*\.?\w*' COMMENT_REGEX = re.compile(r'/\*([^*]|(\*[^/]))*\*+/') RETURNS_REGEX = re.compile(r'^Returns\s*\(?(' + TYPE_REGEX + ')?\)?:(.*)') INHERITS_REGEX = re.compile(r'^Inherits:\s*(' + TYPE_REGEX + ')\s*$') PROPERTY_REGEX = re.compile(r'^@(\w+)\s*\(?(' + TYPE_REGEX + ')?\)?:(.*)') FILE_NAME_REGEX = re.compile(r'FILE:\s*(\w+\.js):?') SIGNAL_NAME_REGEX = re.compile(r'SIGNAL:\s*([\w-]+):?') ENUM_NAME_REGEX = re.compile(r'ENUM:\s*(\w+):?') FUNCTION_NAME_REGEX = re.compile(r'^(\w+):?\s*$') OBJECT_NAME_REGEX = re.compile(r'^#(\w+):?\s*$') FILE_REGEX = re.compile(r'\w*\.js') COMMENT_START_REGEX = re.compile(r'^\s*\* ?') BLOCK_START_REGEX = re.compile(r'^\s*/\*\*\s*$') STRING_REGEX = re.compile(r'\'[^\']*\'|"[^"]*"') STATE_NORMAL = 0 STATE_PROPERTY = 1 STATE_DESCRIPTION = 2 STATE_RETURN = 3 STATE_NAME = 4 STATE_INIT = 5 STATE_COMMENT = 6 ################################################################################ ################################################################################ ## The legendary parse function ## ################################################################################ ################################################################################ ui_files = [os.path.join(JS_UI_DIR, x) for x in os.listdir(JS_UI_DIR)] ui_files.sort() misc_files = [os.path.join(JS_MISC_DIR, x) for x in os.listdir(JS_MISC_DIR)] misc_files.sort() _files = ui_files + misc_files for _file in _files: parts = _file.split("/") if not FILE_REGEX.match(parts[-1]): continue file_obj = open(_file, 'r') curr_file = JSFile(parts[-2], parts[-1][:-3]) files.append(curr_file) bracket_count = 0 # no. of '{' - no. of '}' # The current object - it is either the top-level file or the JSObject we # are parsing. curr_obj = curr_file # The current item being processed, either the top-level file, description # of the object or a function curr_item = curr_file # The current property, if any curr_prop = None state = STATE_NORMAL scope = '' for line in file_obj: ################################################################################ # Process all unimportant comments # ################################################################################ # Strip ' * ' at the beginning of each long comment block if state == STATE_PROPERTY or \ state == STATE_DESCRIPTION or \ state == STATE_RETURN or \ state == STATE_INIT: line = COMMENT_START_REGEX.sub('', line) if len(line) > 0 and line[0] == '/': state = STATE_NORMAL curr_item = None continue # If we are in a (useless) comment, skip unless comment ends in this row elif state == STATE_COMMENT: if line.find('*/') == -1: continue else: line = line[line.find('*/') + 2:] state = STATE_NORMAL # In normal cases, strip comments if necessary, unless it is the # beginning of a doc block, in which case we set the STATE_INIT state else: if '//' in line: line = line[:line.find(r'//')] if '/*' in line: # Strip all in-line comments, eg. 'asdf /* asdf */ asdf' line = COMMENT_REGEX.sub('', line) if BLOCK_START_REGEX.match(line): state = STATE_INIT continue if '/*' in line: line = line[:line.find(r'/*')] state = STATE_COMMENT ################################################################################ # Process actual useful content # ################################################################################ if state == STATE_INIT: if FILE_NAME_REGEX.match(line) and bracket_count == 0: curr_item = curr_file curr_obj = curr_file objects[curr_file.name] = curr_file state = STATE_PROPERTY elif OBJECT_NAME_REGEX.match(line) and bracket_count == 0: curr_item = JSObject(OBJECT_NAME_REGEX.match(line).group(1)) curr_obj = curr_item objects[curr_file.name + '.' + curr_obj.name] = curr_item curr_file.add_object(curr_item) state = STATE_PROPERTY elif FUNCTION_NAME_REGEX.match(line) and \ ((bracket_count == 1 and curr_obj != curr_file) or \ (bracket_count == 0 and curr_obj == curr_file)): curr_item = JSFunction(FUNCTION_NAME_REGEX.match(line).group(1)) curr_obj.add_function(curr_item) state = STATE_PROPERTY elif SIGNAL_NAME_REGEX.match(line) and \ (bracket_count > 0 and curr_obj != curr_file): curr_item = JSSignal(SIGNAL_NAME_REGEX.match(line).group(1)) curr_obj.add_signal(curr_item) state = STATE_PROPERTY elif ENUM_NAME_REGEX.match(line) and bracket_count == 0: curr_item = JSEnum(ENUM_NAME_REGEX.match(line).group(1)) objects[curr_file.name + '.' + curr_item.name] = curr_item curr_file.add_enum(curr_item) state = STATE_PROPERTY else: state = STATE_COMMENT continue if state == STATE_PROPERTY: if len(line.strip()) == 0: if curr_prop is not None: curr_prop = None continue else: # Ignore blank lines if nothing has happened yet continue prop = PROPERTY_REGEX.match(line) if prop: curr_prop = JSProperty(*prop.groups()) curr_item.add_property(curr_prop) else: if curr_prop: curr_prop.append_description(line) else: # The next block will parse this description properly state = STATE_DESCRIPTION if state == STATE_DESCRIPTION: if RETURNS_REGEX.match(line): state = STATE_RETURN curr_prop = JSProperty('Returns', *RETURNS_REGEX.match(line).groups()) curr_item.set_return(curr_prop) elif INHERITS_REGEX.match(line): # Anything after the inherit line shouldn't be there state = STATE_COMMENT curr_item.set_inherit(INHERITS_REGEX.match(line).group(1)) else: curr_item.append_description(line) continue if state == STATE_RETURN: curr_prop.append_description(line) # Here state should be STATE_NORMAL. It might be, in fact, # STATE_COMMENT, since the line ends with /*, and the state is set to # STATE_COMMENT for preparation of the next line. However, the # remaining of the line should be treated as a normal line. If we are # genuinely inside a comment, we would have `continue`ed at the # beginning. if state == STATE_NORMAL or state == STATE_COMMENT: # the "scope" variable will be last updated before we enter the # scope, so will be the "function thing() {}" or "thing.prototype = # {" line. We use this to check if we are parsing the right thing. if bracket_count == 0: scope = line # Don't count the brackets inside strings. STRING_REGEX recognizes # ' and " but doesn't know if they are esacped. So replace away all # escaped quotes line = STRING_REGEX.sub('', line.replace("\\'", "").replace('\\"', '')) bracket_count += line.count('{') - line.count('}') if bracket_count == 0: # Cinnamon-style objects and Lang.Class objects if curr_obj.orig_name + '.prototype' in scope or \ re.match(curr_obj.orig_name + r'\s*=\s*Lang\.Class', scope): curr_obj = curr_file curr_item = curr_file ################################################################################ ################################################################################ ## Generate the XML ## ################################################################################ ################################################################################ write_sgml(files, sys.argv[2] if len(sys.argv) > 2 else "") try: os.mkdir('ui') except OSError: pass try: os.mkdir('misc') except OSError: pass for _file in files: for obj in _file.objects: create_file(obj) Cinnamon-2.8.6/docs/reference/cinnamon-js/gen_lib.py0000664000175000017500000006425612625666430021317 0ustar fabiofabio# coding: utf-8 # Dear future self, # # You're looking at this file because # the parse function finally broke. # # It's not fixable. You have to rewrite it. # Sincerely, past self # # Also, it's probably at least # 2013. Did you ever take # that trip to Iceland? import re def get_type_link(typ, file): from gen_doc import objects if typ == '': return "void" else: if typ in objects: return "cinnamon-js-" + objects[typ].prefix elif file.name + "." + typ in objects: return "cinnamon-js-" + objects[file.name + "." + typ].prefix elif typ.endswith("s") and typ[:-1] in objects: return "cinnamon-js-" + objects[typ[:-1]].prefix elif typ.endswith("s") and file.name + "." + typ[:-1] in objects: return "cinnamon-js-" + objects[file.name + "." + typ[:-1]].prefix elif typ.startswith("Gio"): return typ.replace("Gio.", "G") elif typ.startswith("GLib"): return typ.replace("GLib.", "G") else: return typ.replace('.', '') def markup(line, obj): line = re.sub('@(\w*)', '\g<1>', line) line = re.sub('`([^`]*)`', '\g<1>', line) line = re.sub('\*\*([^*]*)\*\*', '\g<1>', line) line = re.sub('\*([^*]*)\*', '\g<1>', line) def format_type_link(match): res = match.group(1) return '{name}'.format( link = get_type_link(res, obj.file), name = res) line = re.sub('#(([\w]*\.)?[\w]+)', format_type_link, line) def format_ext_link(match): if match.group(1): full = match.group(1) + match.group(3) else: full = match.group(3) if match.group(4): full += match.group(4) owner = match.group(1) if owner: owner = owner[:-1] # remove trailing . else: owner = "this" thing = match.group(3) from gen_doc import objects object = None if owner == "this": object = obj.object if owner in objects: object = objects[owner] elif obj.file.name + "." + owner in objects: object = objects[obj.file.name + "." + owner] if object is None: return '{name}'.format(name = full) func_names = [x.name for x in object.functions] enum_names = [x.name for x in object.enums] prop_names = [x.name for x in object.properties] if thing in prop_names and not full.endswith("()"): return '{full}'.format( prefix = object.prefix, thing = thing, full = full) elif thing in func_names or (thing in enum_names and not full.endswith("()")): return '{full}'.format( prefix = object.prefix, thing = thing, full = full) else: return '{name}'.format(name = full) line = re.sub('%(([\w]+\.)?[\w]+\.)?([\w]+)(\(\))?', format_ext_link, line) return line class JSThing(): def append_description(self, desc): self.description += desc.replace('<', '<').replace('>', '>') def get_xml_description(self, description = None): if description is None: description = self.description stuff = description.split('\n') joined = [''] in_code = False in_list = False for line in stuff: if line.strip() == '```': if in_code: joined[-1] += '```' joined.append('') else: if in_list: joined[-1] += '\n```' else: joined.append('```\n') in_code = not in_code continue if in_code: joined[-1] += '\n' + line continue line = line.strip() if line == '\\' and in_list: joined[-1] += '\n\n' elif len(line) == 0 or line == '\\': # New line if empty joined.append('') in_list = False else: if joined[-1] == '' and line.startswith('- '): in_list = True if line.startswith('- '): joined.append('') joined[-1] += ' ' + line description = '' in_list = False list_buffer = [] for line in joined: if line.split('\n')[0].strip() == '```': description += '{0}'\ .format(line.replace('```', '')) continue if line == '': continue line = line.strip() if line.startswith('-'): in_list = True list_buffer.append(self.get_xml_description(line[1:])) continue if in_list: description += '' + \ '\n'.join('{0}'.format(item) for item in list_buffer) + \ '' list_buffer = [] in_list = False line = markup(line, self) description += '{0}'.format(line) if in_list: description += '' + \ '\n'.join('{0}'.format(item) for item in list_buffer) + \ '' list_buffer = [] return description def add_property(self, prop): if prop.name == "short_description": self.short_description = prop else: self.properties.append(prop) prop.file = self.file prop.object = self.object class JSSignal(JSThing): def __init__ (self, name): self.name = name self.description = '' self.short_description = JSProperty(None, '', '') self.properties = [] class JSFunction(JSThing): def __init__ (self, name): self.name = name self.description = '' self.short_description = JSProperty(None, '', '') self.properties = [] self.return_value = JSProperty(None, '', '') def set_return(self, retval): self.return_value = retval retval.file = self.file retval.obj = self.object class JSProperty(JSThing): def __init__ (self, name, arg_type, desc): self.name = name self.arg_type = arg_type if arg_type else '' self.description = '' self.append_description(desc + "\n") class JSFile(JSThing): def __init__ (self, directory, name): self.directory = directory self.name = name[0].capitalize() + name[1:] self.orig_name = self.name self.imports = "imports.{0}.{1}".format(directory, name) self.prefix = directory + "-" + name self.description = '' self.short_description = JSProperty(None, '', '') self.properties = [] self.objects = [] self.signals = [] self.enums = [] self.functions = [] self.file = self self.object = self def is_interesting(self): return len(self.functions) + len(self.properties) + len(self.description) > 0 def add_function(self, func): self.functions.append(func) func.file = self func.object = self def add_object(self, obj): self.objects.append(obj) obj.parent = self obj.directory = self.directory obj.prefix = self.prefix + "-" + obj.name obj.name = self.name + "-" + obj.name obj.file = self def add_enum(self, obj): self.enums.append(obj) obj.parent = self obj.directory = self.directory obj.prefix = self.prefix + "-" + obj.name obj.file = self class JSObject(JSThing): def __init__ (self, name): self.name = name self.orig_name = name self.inherit = '' self.description = '' self.short_description = JSProperty(None, '', '') self.parent = None self.directory = None self.prefix = None self.functions = [] self.properties = [] self.signals = [] self.enums = [] self.object = self def add_function(self, func): self.functions.append(func) func.file = self.file func.object = self def add_signal(self, signal): self.signals.append(signal) signal.file = self signal.object = self def set_inherit(self, inherit): self.inherit = inherit class JSEnum(JSThing): def __init__ (self, name): self.name = name self.description = '' self.short_description = JSProperty(None, '', '') self.properties = [] self.object = self SGML_FORMAT = '''\ ]> Cinnamon Javascript Reference Manual This document is for Cinnamon {version}. The latest version of this documentation can be found online at http://developer.linuxmint.com/reference/git/cinnamon-js/. {chapters} ''' SGML_CHAPTER_FORMAT = ''' {title} {entries} ''' SGML_ENTRY_FORMAT = '' FILE_FORMAT = '''\ ]> {name} 3 {name} {name} {short_description} {func_header} {prop_header} {signal_header} {enum_header} {hierarchy} {description} {functions} {properties} {signals} {enums} ''' FUNCTION_HEADER_FORMAT = ''' Functions
{function_headers} ''' FUNCTION_HEADER_ITEM_FORMAT = ''' {return_name} {name} () ''' PROPERTY_HEADER_FORMAT = ''' Properties {property_headers} ''' SIGNAL_HEADER_FORMAT = ''' Signals {signal_headers} ''' SIGNAL_HEADER_ITEM_FORMAT = ''' {name} ''' ENUM_HEADER_FORMAT = ''' Types and Values {enum_headers} ''' ENUM_HEADER_ITEM_FORMAT = ''' enum {name} ''' PROPERTY_HEADER_ITEM_FORMAT = ''' {type_name} {name} ''' HIERARCHY_FORMAT = ''' Object Hierarchy Object {hierarchy} ''' HIERARCHY_ITEM_FORMAT = '{spacing}╰── {name}' DESCRIPTION_FORMAT = ''' Description {description} ''' FUNCTIONS_FORMAT = ''' Functions {functions} ''' FUNCTION_ITEM_FORMAT = ''' {name} () {name} {return_type} {name} ({inline_params}); {description} {params} {return_desc} ''' SIGNALS_FORMAT = ''' Signal details {signals} ''' SIGNAL_ITEM_FORMAT = ''' The <literal>“{name}”</literal> signal {prefix}::{name} user_function ({inline_params}); {description} {params} ''' FUNC_PARAMETERS_FORMAT = ''' Parameters {param_items} ''' INLINE_PARAMETER_FORMAT = '{type_name}{name}' FUNC_PARAMETERS_ITEM_FORMAT = ''' {name} {description} ''' FUNC_RETURN_FORMAT = ''' Returns {desc} ''' PROPERTIES_FORMAT = ''' Property Details {properties} ''' PROPERTIES_ITEM_FORMAT = ''' The <literal>“{name}”</literal> property cinnamon-js-{prefix}:{name} {disp_name} {type_name} {description} ''' ENUMS_FORMAT = ''' Types and Values {enums} ''' ENUMS_ITEM_FORMAT = ''' enum {name} {name} {description} Members {enum_items} ''' ENUMS_ITEM_ROW_FORMAT = ''' {name} {description} ''' def write_sgml(files, version): sgml = open('cinnamon-js-docs.sgml', 'w') chapters = [] for _file in files: if not _file.is_interesting() and len(_file.objects) == 0: continue entries = [] if _file.is_interesting(): _file.objects.insert(0, _file) entries = [SGML_ENTRY_FORMAT.format( directory = _file.directory, name = obj.name) for obj in _file.objects] chapters.append(SGML_CHAPTER_FORMAT.format( prefix = _file.prefix, title = _file.imports, entries = "\n".join(entries))) sgml.write(SGML_FORMAT.format( version = version, chapters = "\n".join(chapters))) def create_file(obj): file_obj = open('{0}/{1}.xml'.format(obj.directory, obj.name), 'w') short_description = obj.short_description.description.replace("\n", " ").strip() file_obj.write(FILE_FORMAT.format( prefix = obj.prefix, name = obj.name.replace("-", "."), short_description = markup(short_description, obj), func_header = get_function_header(obj), signal_header = get_signal_header(obj), prop_header = get_properties_header(obj), enum_header = get_enum_header(obj), hierarchy = get_hierarchy(obj), description = get_description(obj), functions = get_functions(obj), signals = get_signals(obj), properties = get_properties(obj), enums = get_enums(obj))) file_obj.close() def get_function_header(obj): if len(obj.functions) == 0: return "" functions = [FUNCTION_HEADER_ITEM_FORMAT.format( return_link = get_type_link(func.return_value.arg_type, obj.file), return_name = func.return_value.arg_type, prefix = obj.prefix, name = func.name) for func in obj.functions] return FUNCTION_HEADER_FORMAT.format( prefix = obj.prefix, function_headers = "\n".join(functions)) def get_signal_header(obj): if len(obj.signals) == 0: return "" signals = [SIGNAL_HEADER_ITEM_FORMAT.format( prefix = obj.prefix, name = sig.name) for sig in obj.signals] return SIGNAL_HEADER_FORMAT.format( prefix = obj.prefix, signal_headers = "\n".join(signals)) def get_properties_header(obj): if len(obj.properties) == 0: return "" properties = [PROPERTY_HEADER_ITEM_FORMAT.format( type_link = get_type_link(prop.arg_type, obj.file), type_name = prop.arg_type, prefix = obj.prefix, name = prop.name) for prop in obj.properties] return PROPERTY_HEADER_FORMAT.format( prefix = obj.prefix, property_headers = "\n".join(properties)) def get_enum_header(obj): if len(obj.enums) == 0: return "" enums = [ENUM_HEADER_ITEM_FORMAT.format( prefix = obj.prefix, name = enum.name) for enum in obj.enums] return ENUM_HEADER_FORMAT.format( prefix = obj.prefix, enum_headers = "\n".join(enums)) def get_hierarchy(obj): from gen_doc import objects if isinstance(obj, JSFile): return "" name = obj.name.replace('-', '.') hierarchy = [] try: while True: name = objects[name].inherit if name in hierarchy: break if name: hierarchy.insert(0, name) except KeyError: pass count = 1 hierarchy_strs = [] for item in hierarchy: try: hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format( spacing = ' ' * count * 4, prefix = objects[item].prefix, name = item)) except KeyError: hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format( spacing = ' ' * count * 4, prefix = "void", name = item)) count += 1 hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format( spacing = ' ' * count * 4, prefix = "void", name = obj.name.replace('-', '.'))) return HIERARCHY_FORMAT.format( prefix = obj.prefix, hierarchy = "\n".join(hierarchy_strs)) def get_description(obj): if len(obj.description) == 0: return "" return DESCRIPTION_FORMAT.format( prefix=obj.prefix, description = obj.get_xml_description()) def get_functions(obj): if len(obj.functions) == 0: return "" functions = [] for func in obj.functions: inline_params = "" params = "" if len(func.properties) > 0: # Calculate how long the argument types are and make the arguments # align max_length = max(len(x.arg_type) for x in func.properties) + 3 # If no parameter has argument types, don't show that silly # whitespace if max_length == 3: max_length = 0 inline_params = [INLINE_PARAMETER_FORMAT.format( type_link = get_type_link(param.arg_type, obj.file), type_name = param.arg_type, name = " " * (max_length - len(param.arg_type)) + param.name) for param in func.properties] inline_params = (',\n' + ' ' * (len(func.name) + 2)).join(inline_params) params = [FUNC_PARAMETERS_ITEM_FORMAT.format( name = param.name, description = param.get_xml_description()) for param in func.properties] params = FUNC_PARAMETERS_FORMAT.format(param_items = '\n'.join(params)) return_desc = "" if func.return_value.name is not None: return_desc = FUNC_RETURN_FORMAT.format(desc=func.return_value.get_xml_description()) functions.append(FUNCTION_ITEM_FORMAT.format( prefix = obj.prefix, name = func.name, return_link = get_type_link(func.return_value.arg_type, obj.file), return_type = func.return_value.arg_type, description = func.get_xml_description(), inline_params = inline_params, params = params, return_desc = return_desc)) return FUNCTIONS_FORMAT.format( prefix = obj.prefix, functions = "\n".join(functions)) def get_signals(obj): if len(obj.signals) == 0: return "" signals = [] for sig in obj.signals: inline_params = "" params = "" if len(sig.properties) > 0: # Calculate how long the argument types are and make the arguments # align max_length = max(len(x.arg_type) for x in sig.properties) + 3 # If no parameter has argument types, don't show that silly # whitespace if max_length == 3: max_length = 0 inline_params = [INLINE_PARAMETER_FORMAT.format( type_link = get_type_link(param.arg_type, obj.file), type_name = param.arg_type, name = " " * (max_length - len(param.arg_type)) + param.name) for param in sig.properties] inline_params = (',\n' + ' ' * (len(sig.name) + 2)).join(inline_params) params = [FUNC_PARAMETERS_ITEM_FORMAT.format( name = param.name, description = param.get_xml_description()) for param in sig.properties] params = FUNC_PARAMETERS_FORMAT.format(param_items = '\n'.join(params)) signals.append(SIGNAL_ITEM_FORMAT.format( prefix = obj.prefix, name = sig.name, description = sig.get_xml_description(), inline_params = inline_params, params = params)) return SIGNALS_FORMAT.format( prefix = obj.prefix, signals = "\n".join(signals)) def get_properties(obj): if len(obj.properties) == 0: return "" properties = [PROPERTIES_ITEM_FORMAT.format( prefix = obj.prefix, name = prop.name, disp_name = ('“' + prop.name + '”').ljust(25), type_link = get_type_link(prop.arg_type, obj.file), type_name = prop.arg_type, description = prop.get_xml_description()) for prop in obj.properties] return PROPERTIES_FORMAT.format( prefix = obj.prefix, properties = "\n".join(properties)) def get_enums(obj): if len(obj.enums) == 0: return "" enums = [] for enum in obj.enums: items = [ENUMS_ITEM_ROW_FORMAT.format( name = item.name, description = item.get_xml_description()) for item in enum.properties] enums.append(ENUMS_ITEM_FORMAT.format( prefix = enum.prefix, name = enum.name, description = enum.get_xml_description(), enum_items = "\n".join(items))) return ENUMS_FORMAT.format( prefix = obj.prefix, enums = "\n".join(enums)) Cinnamon-2.8.6/docs/reference/cinnamon-js/Makefile.am0000664000175000017500000000703012625666430021365 0ustar fabiofabio## Process this file with automake to produce Makefile.in $(DOC_MAIN_SGML_FILE): $(top_srcdir)/js/*/* gen_lib.py gen_doc.py ./gen_doc.py $(top_srcdir) $(PACKAGE_VERSION) # we want $(DOC_MAIN_SGML_FILE) to be built before sgml-build # and sgml-build depends on $(DOC_MODULE)-sections.txt $(DOC_MODULE)-sections.txt: $(DOC_MAIN_SGML_FILE) # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=cinnamon-js # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # Directories containing the source code # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. DOC_SOURCE_DIR= # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--rebuild-types # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS=--xml-mode --output-format=xml # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS=--src-lang=js # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB= CFILE_GLOB= # Extra header to include when scanning, which are not under DOC_SOURCE_DIR # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h EXTRA_HFILES= # Header files or dirs to ignore when scanning. Use base file/dir names # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code IGNORE_HFILES= # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS= GTKDOC_LIBS= # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = $(DOC_MODULES).types # Comment this out if you want 'make check' to test you doc status # and run some sanity checks if ENABLE_GTK_DOC TESTS_ENVIRONMENT = cd $(srcdir) && \ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) #TESTS = $(GTKDOC_CHECK) endif -include $(top_srcdir)/git.mk Cinnamon-2.8.6/docs/reference/st/0000775000175000017500000000000012625666430015543 5ustar fabiofabioCinnamon-2.8.6/docs/reference/st/cinnamon-st-docs.sgml.in0000664000175000017500000000450412625666430022213 0ustar fabiofabio ]> Cinnamon St Reference Manual This document is for Cinnamon @VERSION@. The latest version of this documentation can be found online at http://developer.linuxmint.com/reference/git/st/. API reference Abstract classes and Interfaces Widgets Containers Styling Object Hierarchy API Index Index of deprecated API Cinnamon-2.8.6/docs/reference/st/Makefile.am0000664000175000017500000000706712625666430017611 0ustar fabiofabio## Process this file with automake to produce Makefile.in # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=cinnamon-st # Uncomment for versioned docs and specify the version of the module, e.g. '2'. #DOC_MODULE_VERSION=2 # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml # Directories containing the source code # gtk-doc will search all .c and .h files beneath these paths # for inline comments documenting functions and macros. DOC_SOURCE_DIR=$(top_srcdir)/src/st # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS=--rebuild-types --rebuild-sections # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml MKDB_OPTIONS=--xml-mode --output-format=xml # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-mkhtml MKHTML_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS= # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/src/st/*.h CFILE_GLOB=$(top_srcdir)/src/st/*.c # Extra header to include when scanning, which are not under DOC_SOURCE_DIR # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h EXTRA_HFILES= # Header files or dirs to ignore when scanning. Use base file/dir names # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code IGNORE_HFILES=st-private.h st-theme-node-private.h # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files= # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) GTKDOC_CFLAGS= GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += # Files not to distribute # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt # Comment this out if you want 'make check' to test you doc status # and run some sanity checks if ENABLE_GTK_DOC TESTS_ENVIRONMENT = cd $(srcdir) && \ DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) #TESTS = $(GTKDOC_CHECK) endif -include $(top_srcdir)/git.mk Cinnamon-2.8.6/docs/reference/Makefile.am0000664000175000017500000000006512625666430017152 0ustar fabiofabioSUBDIRS = cinnamon cinnamon-js cinnamon-tutorials st Cinnamon-2.8.6/docs/Makefile.am0000664000175000017500000000002412625666430015207 0ustar fabiofabioSUBDIRS = reference Cinnamon-2.8.6/docs/applets-examples/0000775000175000017500000000000012625666430016443 5ustar fabiofabioCinnamon-2.8.6/docs/applets-examples/finder@cinnamon.org/0000775000175000017500000000000012625666430022323 5ustar fabiofabioCinnamon-2.8.6/docs/applets-examples/finder@cinnamon.org/settings-schema.json0000664000175000017500000000022612625666430026314 0ustar fabiofabio{ "launch_shortcut": { "type" : "keybinding", "default" : "F1", "description" : "Keyboard shortcut to show the search entry" } } Cinnamon-2.8.6/docs/applets-examples/finder@cinnamon.org/metadata.json0000775000175000017500000000023512625666430025001 0ustar fabiofabio{ "uuid": "finder@cinnamon.org", "name": "Search using search providers", "description": "Search using search providers", "icon": "edit-find-symbolic" } Cinnamon-2.8.6/docs/applets-examples/finder@cinnamon.org/applet.js0000664000175000017500000004127612625666430024160 0ustar fabiofabioconst Lang = imports.lang; const Applet = imports.ui.applet; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Gio = imports.gi.Gio; const Util = imports.misc.util; const Gettext = imports.gettext.domain('cinnamon-applets'); const PopupMenu = imports.ui.popupMenu; const St = imports.gi.St; const Mainloop = imports.mainloop; const Cinnamon = imports.gi.Cinnamon; const Main = imports.ui.main; const Settings = imports.ui.settings; const _ = Gettext.gettext; const SearchProviderManager = imports.ui.searchProviderManager; const Clutter = imports.gi.Clutter; const RESULT_TYPES_LABELS = { software: _("Software"), pictures: _("Pictures"), videos: _("Videos"), music: _("Music"), folders: _("Folders"), files: _("Other Files"), provider: _("Other Results") } function SearchProviderResultButton(applet, provider, result) { this._init(applet, provider, result); } SearchProviderResultButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(applet, provider, result) { this.provider = provider; this.result = result; this._applet = applet; PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {focusOnHover: false}); this.icon = null; if (result.icon){ this.icon = result.icon; }else if (result.icon_app){ this.icon = result.icon_app.create_icon_texture(16); }else if (result.icon_filename){ this.icon = new St.Icon({gicon: new Gio.FileIcon({file: Gio.file_new_for_path(result.icon_filename)}), icon_size: 16}); } if (this.icon){ this.addActor(this.icon); } this.label = new St.Label({ text: result.label }); this.addActor(this.label); if (this.icon) { this.icon.realize(); } this.label.realize(); this.connect('activate', Lang.bind(this, this._on_activate)); }, _onButtonReleaseEvent: function (actor, event) { if (event.get_button() == 1){ this.activate(event); } return true; }, _on_activate: function(event) { try{ this.provider.on_result_selected(this.result); this._applet._search_menu.close(); } catch(e) { global.logError(e); } } } function ApplicationResultButton(applet, app) { this._init(applet, app); } ApplicationResultButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(applet, app) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {focusOnHover: false}); this._app = app; this._applet = applet; this.icon = this._app.create_icon_texture(16); this.addActor(this.icon); this.name = this._app.get_name(); this.label = new St.Label( { text: this.name }); this.label.set_style("width: 180px;"); this.addActor(this.label); this.icon.realize(); this.label.realize(); this.connect('activate', Lang.bind(this, this._on_activate)); }, _on_activate: function() { this._applet._search_menu.close(); this._app.open_new_window(-1); } } function FileResultButton(applet, result, type) { this._init(applet, result, type); } FileResultButton.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(applet, result, type, custom_label) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {focusOnHover: false}); this._filename = result["url"]; this._applet = applet; try { let icon = Cinnamon.util_get_icon_for_uri(this._filename); if (icon) { this.icon = St.TextureCache.get_default().load_gicon(null, icon, 16); } } catch (e) { } if (!this.icon) { this.icon = new St.Icon( { icon_name: (type == "files" ? "gtk-file" : "folder"), icon_size: 16, icon_type: St.IconType.FULLCOLOR }); } this.addActor(this.icon); this.label = new St.Label( { text: (custom_label ? custom_label : decodeURIComponent(this._filename)) }); this.addActor(this.label); this.icon.realize(); this.label.realize(); this.connect('activate', Lang.bind(this, this._on_activate)); }, _on_activate: function() { this._applet._search_menu.close(); Util.trySpawn(["xdg-open", this._filename]); } } function MusicResultButton(applet, result, type) { this._init(applet, result, type); } MusicResultButton.prototype = { __proto__: FileResultButton.prototype, _init: function(applet, result, type) { var basename = result["url"].split("/"); basename = decodeURIComponent(basename[basename.length - 1]); var label = (result["performer"] ? result["performer"] + " - " : "") + (result["musicAlbum"] ? result["musicAlbum"] + " - " : "") + (result["trackNumber"] ? result["trackNumber"] + " - " : "") + (result["title"] ? result["title"] : basename); FileResultButton.prototype._init.call(this, applet, result, type, label.trim()); } } function MyApplet(orientation, panel_height, instanceId) { this._init(orientation, panel_height, instanceId); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, _init: function(orientation, panel_height, instanceId) { try { Applet.IconApplet.prototype._init.call(this, orientation, panel_height, instanceId); menuItem = new Applet.MenuItem(_("Indexing Preferences"), null, Lang.bind(this, function(actor, event) { Util.spawnCommandLine('tracker-preferences'); })); this._applet_context_menu.addMenuItem(menuItem); this.settings = new Settings.AppletSettings(this, "finder@cinnamon.org", instanceId); this.settings.bindProperty(Settings.BindingDirection.IN, "launch_shortcut", "launch_shortcut", this.on_launch_shortcut_changed, null); this.set_applet_icon_name("edit-find-symbolic"); this.set_applet_tooltip(_("Search using search providers")); let menuManager = new PopupMenu.PopupMenuManager(this); this._search_menu = new Applet.AppletPopupMenu(this, orientation); menuManager.addMenu(this._search_menu); let section = new PopupMenu.PopupMenuSection(); this._search_menu.addMenuItem(section); this.searchEntry = new St.Entry( { name: 'menu-search-entry', hint_text: _("Type to search..."), track_hover: true, can_focus: true }); section.actor.set_style("padding: 10px;"); this._searchInactiveIcon = new St.Icon( { style_class: 'menu-search-entry-icon', icon_name: 'edit-find', icon_type: St.IconType.SYMBOLIC }); this.searchEntry.set_secondary_icon(this._searchInactiveIcon); this._search_menu.actor.connect('key-release-event', Lang.bind(this, this._onKeyPress)); section.actor.add_actor(this.searchEntry); this._scrollBox = new St.ScrollView( { x_fill: true, y_fill: false, y_align: St.Align.START }); this._scrollBox.set_style("width: 500px;"); this._search_menu.addActor(this._scrollBox); this._container = new St.BoxLayout( { vertical:true }); this._scrollBox.add_actor(this._container); this._scrollBox.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); this._scrollBox.set_auto_scrolling(true); this.searchEntryText = this.searchEntry.clutter_text; this.searchEntryText.connect('text-changed', Lang.bind(this, this._onSearchTextChanged)); this._search_timer = null; this._appSys = Cinnamon.AppSystem.get_default(); this.on_launch_shortcut_changed(); } catch(e) { global.logError(e); } }, _onKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol != Clutter.KEY_Up && symbol != Clutter.KEY_Down) { global.stage.set_key_focus(this.searchEntry); } }, on_launch_shortcut_changed: function() { Main.keybindingManager.addHotKey("finder_launch", this.launch_shortcut, Lang.bind(this, this.launch)); }, _onSearchTextChanged: function(se, prop) { let searchString = this.searchEntry.get_text(); if (searchString != "") { if (this._search_timer) { Mainloop.source_remove(this._search_timer); this._search_timer = null; } this._search_timer = Mainloop.timeout_add(300, Lang.bind(this, function() { this._search_timer = null; this._process_search(searchString); })); } }, _process_search: function(searchString) { this._currentSearchString = searchString; var children = this._container.get_children(); var result_type; for (var i in children) { children[i].destroy(); } SearchProviderManager.launch_all(searchString, Lang.bind(this, function(provider, results, searchString){ query_results = {}; for (var i in results){ result_type = (results[i].type ? results[i].type : 'provider'); if (!query_results[result_type]) { query_results[result_type] = new Array(); } results[i].type = 'provider'; results[i].provider = provider; results[i].id = 'provider-' + results[i].id; results[i].id = 'provider-' + results[i].id; query_results[result_type].push(results[i]); } this._show_results(searchString, query_results); }, searchString)); }, _show_results: function(searchString, results) { if (searchString != this._currentSearchString) { return; } var results_buttons = {}; let button; let this_results; var all_result_types = new Array(); for (var result_type in results) { this_results = results[result_type]; if (this_results.length > 0) { all_result_types.push(result_type); results_buttons[result_type] = new Array(); for (var i in this_results) { button = null; switch (result_type) { case 'provider': button = new SearchProviderResultButton(this, this_results[i]['provider'], this_results[i]); break; case "software": var results_parts = this_results[i]["url"].split("/"); let app = this._appSys.lookup_app(results_parts[results_parts.length - 1].split(".desktop")[0] + ".desktop"); if (app) { let appinfo = app.get_app_info(); if (!appinfo || !appinfo.get_nodisplay()) { button = new ApplicationResultButton(this, app); } } break; case "music": button = new MusicResultButton(this, this_results[i], result_type); break; case "pictures": case "videos": case "folders": case "files": button = new FileResultButton(this, this_results[i], result_type); break; } if (button != null) { button.actor.connect("notify::hover", Lang.bind(this, this._scrollToButton)); button.actor.connect("key-focus-in", Lang.bind(this, this._scrollToButton)); if (result_type == 'provider') { if (!results_buttons['provider-' + SearchProviderManager.extensionMeta[this_results[i]['provider'].uuid].name]) { all_result_types.push('provider-' + SearchProviderManager.extensionMeta[this_results[i]['provider'].uuid].name); results_buttons['provider-' + SearchProviderManager.extensionMeta[this_results[i]['provider'].uuid].name] = new Array(); } results_buttons['provider-' + SearchProviderManager.extensionMeta[this_results[i]['provider'].uuid].name].push(button); } else { results_buttons[result_type].push(button); } } } } } all_result_types.sort(function(a, b) { var order = ["software", "music", "pictures", "videos", "folders", "files", "provider"]; return order.indexOf(a) - order.indexOf(b); }); let this_results_buttons, label; for (var i in all_result_types) { result_type = all_result_types[i]; this_results_buttons = results_buttons[result_type]; if (this_results_buttons.length > 0) { if (this._container.get_children().length > 0) { var separator = new PopupMenu.PopupSeparatorMenuItem(); this._container.add_actor(separator.actor); } if (result_type.substring(0, 9) == 'provider-') { label = result_type.substring(9); } else { label = RESULT_TYPES_LABELS[result_type]; } var result_type_label = new PopupMenu.PopupMenuItem(label, { reactive: false, hover: false, sensitive: false, focusOnHover: true }); result_type_label.actor.set_style("font-weight: bold;"); this._container.add_actor(result_type_label.actor); for (var i in this_results_buttons) { this._container.add_actor(this_results_buttons[i].actor); } } } }, on_applet_clicked: function(event) { if (event.get_button() == 1) { this.launch(); } }, launch: function() { this._search_menu.toggle(); global.stage.set_key_focus(this.searchEntry); this.searchEntryText.set_selection(0, this.searchEntry.get_text().length); }, _scrollToButton: function(button) { var current_scroll_value = this._scrollBox.get_vscroll_bar().get_adjustment().get_value(); var box_height = this._scrollBox.get_allocation_box().y2 - this._scrollBox.get_allocation_box().y1; var new_scroll_value = current_scroll_value; if (current_scroll_value > button.get_allocation_box().y1 - 10) new_scroll_value = button.get_allocation_box().y1 - 10; if (box_height + current_scroll_value < button.get_allocation_box().y2 + 10) new_scroll_value = button.get_allocation_box().y2-box_height + 10; if (new_scroll_value != current_scroll_value) this._scrollBox.get_vscroll_bar().get_adjustment().set_value(new_scroll_value); } } function main(metadata, orientation, panel_height, instanceId) { let myApplet = new MyApplet(orientation, panel_height, instanceId); return myApplet; } Cinnamon-2.8.6/js/0000775000175000017500000000000012625666430012643 5ustar fabiofabioCinnamon-2.8.6/js/misc/0000775000175000017500000000000012625666430013576 5ustar fabiofabioCinnamon-2.8.6/js/misc/format.js0000664000175000017500000000463712625666430015436 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* * This function is intended to extend the String object and provide * an String.format API for string formatting. * It has to be set up using String.prototype.format = Format.format; * Usage: * "somestring %s %d".format('hello', 5); * It supports %s, %d, %x and %f, for %f it also support precisions like * "%.2f".format(1.526). All specifiers can be prefixed with a minimum * field width, e.g. "%5s".format("foo"). Unless the width is prefixed * with '0', the formatted string will be padded with spaces. */ function format() { let str = this; let i = 0; let args = arguments; return str.replace(/%([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, widthGroup, precisionGroup, genericGroup) { if (precisionGroup != '' && genericGroup != 'f') throw new Error("Precision can only be specified for 'f'"); let fillChar = (widthGroup[0] == '0') ? '0' : ' '; let width = parseInt(widthGroup, 10) || 0; function fillWidth(s, c, w) { let fill = ''; for (let i = 0; i < w; i++) fill += c; return fill.substr(s.length) + s; } let s = ''; switch (genericGroup) { case '%': return '%'; break; case 's': s = args[i++].toString(); break; case 'd': s = parseInt(args[i++]).toString(); break; case 'x': s = parseInt(args[i++]).toString(16); break; case 'f': if (precisionGroup == '') s = parseFloat(args[i++]).toString(); else s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup)); break; default: throw new Error('Unsupported conversion character %' + genericGroup); } return fillWidth(s, fillChar, width); }); } Cinnamon-2.8.6/js/misc/config.js.in0000664000175000017500000000075412625666430016014 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* The name of this package (not localized) */ const PACKAGE_NAME = '@PACKAGE_NAME@'; /* The version of this package */ const PACKAGE_VERSION = '@PACKAGE_VERSION@'; /* The version of GJS we're linking to */ const GJS_VERSION = '@GJS_VERSION@'; /* 1 if gnome-bluetooth is available, 0 otherwise */ const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@; /* The system TLS CA list */ const CINNAMON_SYSTEM_CA_FILE = '@CINNAMON_SYSTEM_CA_FILE@'; Cinnamon-2.8.6/js/misc/interfaces.js0000664000175000017500000002417312625666430016266 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; const Gio = imports.gi.Gio; const SETTINGS_DAEMON_NAME = "org.cinnamon.SettingsDaemon"; const SETTINGS_DAEMON_POWER_PATH = "/org/cinnamon/SettingsDaemon/Power"; const DBusIface = '\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; function getDBus() { let proxy = Gio.DBusProxy.makeProxyWrapper(DBusIface); return new proxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus'); } function getDBusAsync(callback) { let proxy = Gio.DBusProxy.makeProxyWrapper(DBusIface); new proxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus', callback); } /**************************************** * Generic property monitor * ****************************************/ const DBusProperties = '\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; function getDBusProperties(name, path) { let proxy = Gio.DBusProxy.makeProxyWrapper(DBusProperties); return new proxy(Gio.DBus.session, name, path); } function getDBusPropertiesAsync(name, path, callback) { let proxy = Gio.DBusProxy.makeProxyWrapper(DBusProperties); new proxy(Gio.DBus.session, name, path, callback); } /**************************************** * org.cinnamon.SettingsDaemon services * ****************************************/ let xml = { }; xml['org.cinnamon.SettingsDaemon.Power'] = [ " \ \ \ \ \ \ \ \ \ \ \ ", SETTINGS_DAEMON_NAME, SETTINGS_DAEMON_POWER_PATH ]; xml['org.cinnamon.SettingsDaemon.Power.Screen'] = [ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ", SETTINGS_DAEMON_NAME, SETTINGS_DAEMON_POWER_PATH ]; xml['org.cinnamon.SettingsDaemon.Power.Keyboard'] = [ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ", SETTINGS_DAEMON_NAME, SETTINGS_DAEMON_POWER_PATH ]; xml['org.cinnamon.SettingsDaemon.XRANDR_2'] = [ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ", SETTINGS_DAEMON_NAME, '/org/cinnamon/SettingsDaemon/XRANDR' ]; /**************************************** * Ownable interfaces * ****************************************/ const MEDIA_PLAYER_2_PATH = "/org/mpris/MediaPlayer2"; let xml_with_owner = { }; xml_with_owner['org.mpris.MediaPlayer2'] = [ " \ \ \ \ \ \ \ \ \ \ \ \ ", MEDIA_PLAYER_2_PATH ] xml_with_owner['org.mpris.MediaPlayer2.Player'] = [ " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ", MEDIA_PLAYER_2_PATH ] /* More to come */ /*********** * Factory * ***********/ function getDBusProxy(which) { let proxy = Gio.DBusProxy.makeProxyWrapper(xml[which][0]); return new proxy(Gio.DBus.session, xml[which][1], xml[which][2]); } function getDBusProxyAsync(which, callback) { let proxy = Gio.DBusProxy.makeProxyWrapper(xml[which][0]); new proxy(Gio.DBus.session, xml[which][1], xml[which][2], callback); } function getDBusProxyWithOwner(which, owner) { let proxy = Gio.DBusProxy.makeProxyWrapper(xml_with_owner[which][0]); return new proxy(Gio.DBus.session, owner, xml_with_owner[which][1]); } function getDBusProxyWithOwnerAsync(which, owner, callback) { let proxy = Gio.DBusProxy.makeProxyWrapper(xml_with_owner[which][0]); new proxy(Gio.DBus.session, owner, xml_with_owner[which][1], callback); } Cinnamon-2.8.6/js/misc/params.js0000664000175000017500000000210412625666430015414 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // parse: // @params: caller-provided parameter object, or %null // @defaults: function-provided defaults object // @allowExtras: whether or not to allow properties not in @default // // Examines @params and fills in default values from @defaults for // any properties in @defaults that don't appear in @params. If // @allowExtras is not %true, it will throw an error if @params // contains any properties that aren't in @defaults. // // If @params is %null, this returns the values from @defaults. // // Return value: a new object, containing the merged parameters from // @params and @defaults function parse(params, defaults, allowExtras) { let ret = {}, prop; if (!params) params = {}; for (let prop in params) { if (!(prop in defaults) && !allowExtras) throw new Error('Unrecognized parameter "' + prop + '"'); ret[prop] = params[prop]; } for (let prop in defaults) { if (!(prop in params)) ret[prop] = defaults[prop]; } return ret; } Cinnamon-2.8.6/js/misc/gridNavigator.js0000664000175000017500000000463512625666430016744 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; function nextIndex(itemCount, numCols, currentIndex, symbol) { let result = -1; if (itemCount > 3 // grid navigation is not suited for a low item count && (symbol === Clutter.Down || symbol === Clutter.Up)) { let numRows = Math.ceil(itemCount/numCols); let curRow = Math.floor(currentIndex/numCols); let curCol = currentIndex % numCols; let calcNewIndex = function(rowDelta) { let newIndex = (curRow + rowDelta) * numCols + curCol; if (rowDelta >= 0) { // down return newIndex < itemCount ? newIndex : curCol < numCols - 1 ? // wrap to top row, one column to the right: curCol + 1 : // wrap to top row, left-most column: 0; } else { // up let numFullRows = Math.floor(itemCount/numCols); let numIOILR = itemCount % numCols; //num Items on Incompl. Last Row return newIndex >= 0 ? newIndex : curCol === 0 ? // Wrap to the bottom of the right-most column, may not be on last row: (numFullRows * numCols) - 1 : /* If we're on the top row but not in the first column, we want to move to the bottom of the column to the left, even though that may not be the bottom of the grid. */ numIOILR && curCol > numIOILR ? ((numFullRows - 1) * numCols) + curCol - 1: ((numRows - 1) * numCols) + curCol - 1; } }; if (symbol === Clutter.Down) { result = calcNewIndex(1); } if (symbol === Clutter.Up) { result = calcNewIndex(-1); } } else if (symbol === Clutter.Left || symbol === Clutter.Up) { result = (currentIndex < 1 ? itemCount : currentIndex) - 1; } else if (symbol === Clutter.Right || symbol === Clutter.Down) { result = (currentIndex + 1) % itemCount; } else if (symbol === Clutter.Home) { result = 0; } else if (symbol === Clutter.End) { result = itemCount - 1; } return result; } Cinnamon-2.8.6/js/misc/signalManager.js0000664000175000017500000001766612625666430016724 0ustar fabiofabioconst GObject = imports.gi.GObject; const Lang = imports.lang; /** * #SignalManager: * @short_description: A convenience object for managing signals * @_object (Object): The object owning the SignalManager. All callbacks are * binded to %_object unless otherwise specified. * @_storage (Array): An array that stores all the connected signals. Each * signal is stored as an array in the form `[signalName, object, callback, * signalId]`. * * The #SignalManager is a convenience object for managing signals. If you use * this to connect signals, you can later disconnect them by signal name or * just disconnect everything! No need to keep track of those annoying * @signalIds by yourself anymore! * * A common use case is to use the #SignalManager to connect to signals and then * use the @disconnectAllSignals function when the object is destroyed, to * avoid keeping track of all the signals manually. * * However, this is not always needed. If you are connecting to a signal of * your actor, the signals are automatically disconnected when you destroy the * actor. Using the #SignalManager to disconnect all signals is only needed when * connecting to objects that persists after the object disappears. * * Every Javascript object should have its own @SignalManager, and use it to * connect signals of all objects it takes care of. For example, the panel will * have one #SignalManger object, which manages all signals from #GSettings, * `global.screen` etc. * * An example usage is as follows: * ``` * MyApplet.prototype = { * __proto__: Applet.Applet.prototype, * * _init: function(orientation, panelHeight, instanceId) { * Applet.Applet.prototype._init.call(this, orientation, panelHeight, instanceId); * * this._signalManager = new SignalManager.SignalManager(this); * this._signalManager.connect(global.settings, "changed::foo", this._onChanged); * }, * * _onChanged: function() { * // Do something * }, * * on_applet_removed_from_panel: function() { * this._signalManager.disconnectAllSignals(); * } * } * ``` */ function SignalManager(object) { this._init(object); } SignalManager.prototype = { /** * _init: * @object (Object): the object owning the #SignalManager (usually @this) */ _init: function(object) { this._object = object; this._storage = []; }, /** * connect: * @obj (Object): the object whose signal we are listening to * @sigName (string): the name of the signal we are listening to * @callback (function): the callback function * @bind (Object): (optional) the object to bind the function to. Leave * empty for the owner of the #SignalManager (which has no side effects if * you don't need to bind at all). * @force (boolean): whether to connect again even if it is connected * * This listens to the signal @sigName from @obj and calls @callback when * the signal is emitted. @callback is automatically binded to * %this._object, unless the @bind argument is set to something else, in * which case the function will be binded to @bind. * * This checks whether the signal is already connected and will not connect * again if it is already connected. This behaviour can be overridden by * settings @force to be @true. * * For example, what you would normally write as * ``` * global.settings.connect("changed::foo", Lang.bind(this, this._bar)) * ``` * would become * ``` * this._signalManager.connect(global.settings, "changed::foo", this._bar) * ``` * * Note that in this function, the first argument is the object, while the * second is the signal name. In all other methods, you first pass the * signal name, then the object (since the object is rarely passed in other * functions). */ connect: function(obj, sigName, callback, bind, force) { if (!force && this.isConnected(sigName, obj, callback)) return let id; if (bind) id = obj.connect(sigName, Lang.bind(bind, callback)); else id = obj.connect(sigName, Lang.bind(this._object, callback)); this._storage.push([sigName, obj, callback, id]); }, _signalIsConnected: function (signal) { if (!signal[1]) return false; else if (signal[1] instanceof GObject.Object)// GObject return GObject.signal_handler_is_connected(signal[1], signal[3]); else if ('signalHandlerIsConnected' in signal[1]) // JS Object return signal[1].signalHandlerIsConnected(signal[3]); else return false; }, /** * isConnected: * @sigName (string): the signal we care about * @obj (Object): (optional) the object we care about, or leave empty if we * don't care about which object it is * @callback (function): (optional) the callback function we care about, or * leave empty if we don't care about what callback is connected * * This checks whether the signal @sigName is connected. The optional * arguments @obj and @callback can be used to specify what signals in * particular we want to know. Note that when you supply @callBack, you * usually want to supply @obj as well, since two different objects can * connect to the same signal with the same callback. * * This is functionally equivalent to (and implemented as) * ``` * this.getSignals(arguments).length > 0); * ``` * * Returns: Whether the signal is connected */ isConnected: function() { return (this.getSignals(arguments).length > 0); }, /** * getSignals: * @sigName (string): the signal we care about * @obj (Object): (optional) the object we care about, or leave empty if we * don't care about which object it is * @callback (function): (optional) the callback function we care about, or * leave empty if we don't care about what callback is connected * * This returns the list of all signals that matches the description * provided. Each signal is represented by an array in the form * `[signalName, object, callback, signalId]`. * * Returns (Array): The list of signals */ getSignals: function(sigName, obj, callback) { let results = this._storage; if (sigName) results = results.filter(x => x[0] == sigName); if (obj) results = results.filter(x => x[1] == obj); if (callback) results = results.filter(x => x[2] == callback); return results; }, /** * disconnect: * @sigName (string): the signal we care about * @obj (Object): (optional) the object we care about, or leave empty if we * don't care about which object it is * @callback (function): (optional) the callback function we care about, or * leave empty if we don't care about what callback is connected * * This disconnects all *signals* named @sigName. By default, it * disconnects the signal on all objects, but can be fine-tuned with the * optional @obj and @callback arguments. * * This function will do nothing if no such signal is connected, the object * no longer exists, or the signal is somehow already disconnected. So * checks need not be performed before calling this function. */ disconnect: function() { let results = this.getSignals(arguments); results.filter(this._signalIsConnected).forEach(x => x[1].disconnect(x[3])); this._storage = this._storage.filter(x => results.indexOf(x) != -1); }, /** * disconnectAllSignals: * * Disconnects *all signals* managed by the #SignalManager. This is useful * in the @destroy function of objects. */ disconnectAllSignals: function() { this._storage.filter(this._signalIsConnected).forEach(x => x[1].disconnect(x[3])); this._storage = []; } } Cinnamon-2.8.6/js/misc/util.js0000664000175000017500000002350512625666430015116 0ustar fabiofabio/** * FILE:util.js * @short_description: File providing certain utility functions * * This file includes certain useful utility functions such as running external * commands. It is generally a good idea to use the functions defined here * instead of tapping into GLib directly since this adds some wrappers around * the functions that make them more Cinnamon-friendly and provides helpful * error messages. */ const GLib = imports.gi.GLib; const Main = imports.ui.main; // http://daringfireball.net/2010/07/improved_regex_for_matching_urls const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)'; const _leadingJunk = '[\\s`(\\[{\'\\"<\u00AB\u201C\u2018]'; const _notTrailingJunk = '[^\\s`!()\\[\\]{};:\'\\".,<>?\u00AB\u00BB\u201C\u201D\u2018\u2019]'; const _urlRegexp = new RegExp( '(^|' + _leadingJunk + ')' + '(' + '(?:' + '[a-z][\\w-]+://' + // scheme:// '|' + 'www\\d{0,3}[.]' + // www. '|' + '[a-z0-9.\\-]+[.][a-z]{2,4}/' + // foo.xx/ ')' + '(?:' + // one or more: '[^\\s()<>]+' + // run of non-space non-() '|' + // or _balancedParens + // balanced parens ')+' + '(?:' + // end with: _balancedParens + // balanced parens '|' + // or _notTrailingJunk + // last non-junk char ')' + ')', 'gi'); /** * findUrls: * @str: string to find URLs in * * Searches @str for URLs and returns an array of objects with %url * properties showing the matched URL string, and %pos properties indicating * the position within @str where the URL was found. * * Returns: the list of match objects, as described above */ function findUrls(str) { let res = [], match; while ((match = _urlRegexp.exec(str))) res.push({ url: match[2], pos: match.index + match[1].length }); return res; } /** * spawn: * @argv: an argv array * * Runs @argv in the background, handling any errors that occur * when trying to start the program. */ function spawn(argv) { let pid; try { pid = trySpawn(argv); } catch (err) { _handleSpawnError(argv[0], err); } return pid; } let subprocess_id = 0; let subprocess_callbacks = {}; function spawn_async(args, callback) { subprocess_id++; subprocess_callbacks[subprocess_id] = callback; spawn(new Array("cinnamon-subprocess-wrapper", subprocess_id.toString()).concat(args)); } /** * spawnCommandLine: * @command_line: a command line * * Runs @command_line in the background, handling any errors that * occur when trying to parse or start the program. */ function spawnCommandLine(command_line) { let pid; try { let [success, argv] = GLib.shell_parse_argv(command_line); pid = trySpawn(argv); } catch (err) { _handleSpawnError(command_line, err); } return pid; } /** * trySpawn: * @argv: an argv array * * Runs @argv in the background. If launching @argv fails, * this will throw an error. */ function trySpawn(argv) { try { let [success, pid] = GLib.spawn_async(null, argv, null, GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.STDOUT_TO_DEV_NULL | GLib.SpawnFlags.STDERR_TO_DEV_NULL, null, null); return pid; } catch (err) { if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) { err.message = _("Command not found"); } else { // The exception from gjs contains an error string like: // Error invoking GLib.spawn_command_line_async: Failed to // execute child process "foo" (No such file or directory) // We are only interested in the part in the parentheses. (And // we can't pattern match the text, since it gets localized.) err.message = err.message.replace(/.*\((.+)\)/, '$1'); } throw err; } } /** * trySpawnCommandLine: * @command_line: a command line * * Runs @command_line in the background. If launching @command_line * fails, this will throw an error. */ function trySpawnCommandLine(command_line) { let pid; try { let [success, argv] = GLib.shell_parse_argv(command_line); pid = trySpawn(argv); } catch (err) { // Replace "Error invoking GLib.shell_parse_argv: " with // something nicer err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n"); throw err; } return pid; } function _handleSpawnError(command, err) { let title = _("Execution of '%s' failed:").format(command); Main.notifyError(title, err.message); } /** * killall: * @processName: a process name * * Kills @processName. If no process with the given name is found, * this will fail silently. */ function killall(processName) { try { // pkill is more portable than killall, but on Linux at least // it won't match if you pass more than 15 characters of the // process name... However, if you use the '-f' flag to match // the entire command line, it will work, but we have to be // careful in that case that we can match // '/usr/bin/processName' but not 'gedit processName.c' or // whatever... let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )']; GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null); // It might be useful to return success/failure, but we'd need // a wrapper around WIFEXITED and WEXITSTATUS. Since none of // the current callers care, we don't bother. } catch (e) { logError(e, 'Failed to kill ' + processName); } } // This was ported from network-manager-applet // Copyright 2007 - 2011 Red Hat, Inc. // Author: Dan Williams const _IGNORED_WORDS = [ 'Semiconductor', 'Components', 'Corporation', 'Communications', 'Company', 'Corp.', 'Corp', 'Co.', 'Inc.', 'Inc', 'Incorporated', 'Ltd.', 'Limited.', 'Intel', 'chipset', 'adapter', '[hex]', 'NDIS', 'Module' ]; const _IGNORED_PHRASES = [ 'Multiprotocol MAC/baseband processor', 'Wireless LAN Controller', 'Wireless LAN Adapter', 'Wireless Adapter', 'Network Connection', 'Wireless Cardbus Adapter', 'Wireless CardBus Adapter', '54 Mbps Wireless PC Card', 'Wireless PC Card', 'Wireless PC', 'PC Card with XJACK(r) Antenna', 'Wireless cardbus', 'Wireless LAN PC Card', 'Technology Group Ltd.', 'Communication S.p.A.', 'Business Mobile Networks BV', 'Mobile Broadband Minicard Composite Device', 'Mobile Communications AB', '(PC-Suite Mode)' ]; function fixupPCIDescription(desc) { desc = desc.replace(/[_,]/, ' '); /* Attempt to shorten ID by ignoring certain phrases */ for (let i = 0; i < _IGNORED_PHRASES.length; i++) { let item = _IGNORED_PHRASES[i]; let pos = desc.indexOf(item); if (pos != -1) { let before = desc.substring(0, pos); let after = desc.substring(pos + item.length, desc.length); desc = before + after; } } /* Attmept to shorten ID by ignoring certain individual words */ let words = desc.split(' '); let out = [ ]; for (let i = 0; i < words.length; i++) { let item = words[i]; // skip empty items (that come out from consecutive spaces) if (item.length == 0) continue; if (_IGNORED_WORDS.indexOf(item) == -1) { out.push(item); } } return out.join(' '); } // key: normal char, value: regex containing all chars with accents const _LATINISE_REGEX = { //uppercase A: /[\xC0-\xC5\u0100\u0102\u0104]/g, AE: /\xC6/g, C: /[\xC7\u0106\u0108\u010A\u010C]/g, D: /[\xD0\u010E\u0110]/g, E: /[\xC8-\xCB\u0112\u0114\u0116\u0118\u011A]/g, G: /[\u011C\u011E\u0120\u0122]/g, H: /[\u0124\u0126]/g, I: /[\xCC-\xCF\u0128\u012A\u012C\u0130]/g, IJ: /\u0132/g, J: /[\u012E\u0134]/g, K: /\u0136/g, L: /[\u0139\u013B\u013D\u0130F\u0141]/g, N: /[\xD1\u0143\u0145\u0147\u014A]/g, O: /[\xD2-\xD6\xD8\u014C\u014E\u0150]/g, OE: /\u0152/g, R: /[\u0154\u0156\u0158]/g, S: /[\u015A\u015C\u015E\u0160]/g, T: /[\u0162\u0164\u0166]/g, U: /[\xD9-\xDC\u0168\u016A\u016C\u016E\u0170\u0172]/g, W: /\u0174/g, Y: /[\xDD\u0176\u0178]/g, Z: /[\u0179\u017B\u017D]/g, //lowercase a: /[\xE0-\xE5\u0101\u0103\u0105]/g, ae: /\xE6/g, c: /[\xE7\u0107\u0109\u010B\u010D]/g, d: /[\u010F\u0111]/g, e: /[\xE8-\xEB\u0113\u0115\u0117\u0119\u011B]/g, g: /[\u011D\u011F\u0121\u0123]/g, h: /[\u0125\u0127]/g, i: /[\xEC-\xEF\u0129\u012B\u012D\u0131]/g, ij: /\u0133/g, j: /[\u012F\u0135]/g, k: /[\u0137\u0138]/g, l: /[\u013A\u013C\u013E\u0140\u0142]/g, n: /[\xF1\u0144\u0146\u0148\u0149\u014B]/g, o: /[\xF2-\xF6\xF8\u014D\u014F\u0151]/g, oe: /\u0153/g, r: /[\u0155\u0157\u0159]/g, s: /[\u015B\u015D\u015F\u0161]/g, t: /[\u0163\u0165\u0167]/g, u: /[\xF9-\xFC\u0169\u016B\u016D\u016F\u0171\u0173]/g, w: /\u0175/g, y: /[\xFD\xFF\u0177]/g, z: /[\u017A\u017C\u017E]/g }; /** * latinise: * @string (string): a string * * Returns (string): @string, replaced accented chars */ function latinise(string){ //call every regex to replace chars for(var i in _LATINISE_REGEX){ string = string.replace(_LATINISE_REGEX[i], i); } return string; } Cinnamon-2.8.6/js/misc/docInfo.js0000664000175000017500000001565312625666430015527 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const St = imports.gi.St; const Mainloop = imports.mainloop; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Signals = imports.signals; const Search = imports.ui.search; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Main = imports.ui.main; const THUMBNAIL_ICON_MARGIN = 2; const MAX_RECENT_FILES = 20; function DocInfo(recentInfo) { this._init(recentInfo); } DocInfo.prototype = { _init : function(recentInfo) { this.recentInfo = recentInfo; // We actually used get_modified() instead of get_visited() // here, as GtkRecentInfo doesn't updated get_visited() // correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094 this.timestamp = recentInfo.get_modified(); this.name = recentInfo.get_display_name(); this._lowerName = this.name.toLowerCase(); this.uri = recentInfo.get_uri(); this.mimeType = recentInfo.get_mime_type(); //this.mtime = this._fetch_mtime(); // Expensive }, // _fetch_mtime : function() { // let ret = -1; // if (GLib.str_has_prefix(this.uri, "file://")) { // let file = Gio.file_new_for_uri(this.uri); // let file_info; // try { // file_info = file.query_info(Gio.FILE_ATTRIBUTE_TIME_MODIFIED, Gio.FileQueryInfoFlags.NONE, null); // if (file_info) { // ret = file_info.get_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_MODIFIED); // } // } catch (e) {} // } // return ret; // }, createIcon : function(size) { // let existing = this.factory.lookup(this.uri, this.mtime); // EXPENSIVE // if (existing) { // let file = Gio.file_new_for_path(existing); // let thumb_uri = file.get_uri(); // return St.TextureCache.get_default().load_uri_async(thumb_uri, size, size); // } // else { let gicon = this.recentInfo.get_gicon() return St.TextureCache.get_default().load_gicon(null, gicon, size); // } }, _realLaunch : function() { Gio.app_info_launch_default_for_uri(this.uri, global.create_app_launch_context()); }, // _onMountCallback: function (file, result, data) { // try { // this._realLaunch(); // } catch (e) { // let q = GLib.quark_from_static_string("g-vfs-error-quark"); // if (e.domain == q) {/* gvfs cache invalid - not sure why... retry succeeds */ // this._realLaunch(); // } else { // Main.notify(_("Problem opening file"), // _("There was a problem opening the selected file.") + // _(" Please check to see if you have the proper permissions to access this resource,") + // _(" or try manually mounting the file's enclosing volume.\n\n") + // _("The file uri is: " + file.get_uri())); // global.logError("docInfo.js: Failed to mount: " + file.get_uri()); // global.logError("............Error domain is: " + GLib.quark_to_string(e.domain)); // global.logError("............Error code is: " + e.code); // global.logError("............Error Message is: " + e.message); // } // } // }, launch : function() { // if (this.mtime == -1) { // let file = Gio.File.new_for_uri(this.uri); // file.mount_enclosing_volume(0, null, null, Lang.bind(this, this._onMountCallback), null); // } else { this._realLaunch(); // } }, matchTerms: function(terms) { let mtype = Search.MatchType.NONE; for (let i = 0; i < terms.length; i++) { let term = terms[i]; let idx = this._lowerName.indexOf(term); if (idx == 0) { mtype = Search.MatchType.PREFIX; } else if (idx > 0) { if (mtype == Search.MatchType.NONE) mtype = Search.MatchType.SUBSTRING; } else { return Search.MatchType.NONE; } } return mtype; } }; var docManagerInstance = null; function getDocManager() { if (docManagerInstance == null) docManagerInstance = new DocManager(); return docManagerInstance; } /** * DocManager wraps the DocSystem, primarily to expose DocInfo objects. */ function DocManager() { this._init(); } DocManager.prototype = { _init: function() { this._docSystem = Cinnamon.DocSystem.get_default(); this._infosByTimestamp = []; this._infosByUri = {}; this._load(); this._docSystem.connect('changed', Lang.bind(this, this._reload)); }, _load: function() { let docs = this._docSystem.get_all(); this._infosByTimestamp = []; this._infosByUri = {}; for (let i = 0; i < docs.length && i < MAX_RECENT_FILES; i++) { let recentInfo = docs[i]; let docInfo = new DocInfo(recentInfo); this._infosByTimestamp.push(docInfo); this._infosByUri[docInfo.uri] = docInfo; } }, _reload: function() { this._load(); this.emit('changed'); }, getTimestampOrderedInfos: function() { return this._infosByTimestamp; }, getInfosByUri: function() { return this._infosByUri; }, lookupByUri: function(uri) { return this._infosByUri[uri]; }, queueExistenceCheck: function(count) { return this._docSystem.queue_existence_check(count); }, _searchDocs: function(items, terms) { let multiplePrefixMatches = []; let prefixMatches = []; let multipleSubtringMatches = []; let substringMatches = []; for (let i = 0; i < items.length; i++) { let item = items[i]; let mtype = item.matchTerms(terms); if (mtype == Search.MatchType.MULTIPLE_PREFIX) multiplePrefixMatches.push(item.uri); else if (mtype == Search.MatchType.PREFIX) prefixMatches.push(item.uri); else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING) multipleSubtringMatches.push(item.uri); else if (mtype == Search.MatchType.SUBSTRING) substringMatches.push(item.uri); } return multiplePrefixMatches.concat(prefixMatches.concat(multipleSubtringMatches.concat(substringMatches))); }, initialSearch: function(terms) { return this._searchDocs(this._infosByTimestamp, terms); }, subsearch: function(previousResults, terms) { return this._searchDocs(previousResults.map(Lang.bind(this, function(url) { return this._infosByUri[url]; })), terms); } }; Signals.addSignalMethods(DocManager.prototype); Cinnamon-2.8.6/js/misc/timers.js0000664000175000017500000000117212625666430015440 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- function DebugTimer(name){ this._init(name); } DebugTimer.prototype = { _init: function(name) { this.name = name; this.start_time = 0; }, start: function() { let date = new Date(); this.start_time = date.getTime(); log("Debug timer __" + this.name + "__ started."); }, stop: function() { let date = new Date(); let diff = date.getTime() - this.start_time; log("Debug timer __" + this.name + "__ stopped at * " + diff.toString() + "ms *") this.start_time = 0; } }; Cinnamon-2.8.6/js/misc/fileDialog.js0000664000175000017500000000241612625666430016176 0ustar fabiofabioconst Util = imports.misc.util; const GLib = imports.gi.GLib; function open(callback, params) { _launchDialog(0, callback, params); } function openFolder(callback, params) { _launchDialog(1, callback, params); } function save(callback, params) { _launchDialog(2, callback, params); } function _launchDialog(type, callback, params) { let args = ["cinnamon-file-dialog"]; if (params.selectMultiple) type += 3; //add 3 to use the select-multiple version args.push(String(type)); if (params.path) args.push("-p", params.path.replace("~", GLib.get_home_dir())); if (params.name) args.push("-n", params.name); if (params.directory) args.push("-d", params.directory.replace("~", GLib.get_home_dir())); if (params.filters) { let filterList = []; for (let i = 0; i < params.filters.length; i++) { filterList.push(params.filters[i].getString()); } args.push("-f", filterList.join(",")); } Util.spawn_async(args, callback); } function Filter(name) { this._init(name); } Filter.prototype = { _init: function(name) { this.name = name; this.rules = []; }, addMimeType: function(mime) { this.rules.push("m="+mime); }, addPattern: function(pattern) { this.rules.push("p="+pattern); }, getString: function() { return this.name + ";" + this.rules.join(":"); } }Cinnamon-2.8.6/js/misc/history.js0000664000175000017500000000663312625666430015645 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Signals = imports.signals; const Clutter = imports.gi.Clutter; const Params = imports.misc.params; const DEFAULT_LIMIT = 512; function HistoryManager(params) { this._init(params); } HistoryManager.prototype = { _init: function(params) { params = Params.parse(params, { gsettingsKey: null, limit: DEFAULT_LIMIT, entry: null, deduplicate: false }); this._key = params.gsettingsKey; this._limit = params.limit; this._historyIndex = 0; if (this._key) { this._history = global.settings.get_strv(this._key); global.settings.connect('changed::' + this._key, Lang.bind(this, this._historyChanged)); } else { this._history = []; } this._entry = params.entry; if (this._entry) { this._entry.connect('key-press-event', Lang.bind(this, this._onEntryKeyPress)); } this._deduplicate = params.deduplicate; }, _historyChanged: function() { this._history = global.settings.get_strv(this._key); this._historyIndex = this._history.length; }, prevItem: function(text) { if (this._historyIndex <= 0) return text; if (text) this._history[this._historyIndex] = text; this._historyIndex--; return this._indexChanged(); }, nextItem: function(text) { if (this._historyIndex >= this._history.length) return text; if (text) this._history[this._historyIndex] = text; this._historyIndex++; return this._indexChanged(); }, lastItem: function() { if (this._historyIndex != this._history.length) { this._historyIndex = this._history.length; this._indexChanged(); } return this._historyIndex[this._history.length]; }, addItem: function(input) { if (this._history.length == 0 || this._history[this._history.length - 1] != input) { if (this._deduplicate) { this._history = this._history.filter(function(x) { return x != input; }); } this._history.push(input); this._save(); } this._historyIndex = this._history.length; }, _onEntryKeyPress: function(entry, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_Up) { this.prevItem(entry.get_text()); return true; } else if (symbol == Clutter.KEY_Down) { this.nextItem(entry.get_text()); return true; } return false; }, _indexChanged: function() { let current = this._history[this._historyIndex] || ''; this.emit('changed', current); if (this._entry) this._entry.set_text(current); return current; }, _save: function() { if (this._history.length > this._limit) this._history.splice(0, this._history.length - this._limit); if (this._key) global.settings.set_strv(this._key, this._history); } }; Signals.addSignalMethods(HistoryManager.prototype); Cinnamon-2.8.6/js/misc/modemManager.js0000664000175000017500000002574212625666430016542 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const Lang = imports.lang; const NMGtk = imports.gi.NMGtk; const Signals = imports.signals; // _getMobileProvidersDatabase: // // Gets the database of mobile providers, with references between MCCMNC/SID and // operator name // let _mpd; function _getMobileProvidersDatabase() { if (_mpd == null) { try { _mpd = new NMGtk.MobileProvidersDatabase(); _mpd.init(null); } catch (e) { log(e.message); _mpd = null; } } return _mpd; } // _findProviderForMccMnc: // @operator_name: operator name // @operator_code: operator code // // Given an operator name string (which may not be a real operator name) and an // operator code string, tries to find a proper operator name to display. // function _findProviderForMccMnc(operator_name, operator_code) { if (operator_name) { if (operator_name.length != 0 && (operator_name.length > 6 || operator_name.length < 5)) { // this looks like a valid name, i.e. not an MCCMNC (that some // devices return when not yet connected return operator_name; } if (isNaN(parseInt(operator_name))) { // name is definitely not a MCCMNC, so it may be a name // after all; return that return operator_name; } } let needle; if ((!operator_name || operator_name.length == 0) && operator_code) needle = operator_code; else if (operator_name && (operator_name.length == 6 || operator_name.length == 5)) needle = operator_name; else // nothing to search return null; let mpd = _getMobileProvidersDatabase(); if (mpd) { let provider = mpd.lookup_3gpp_mcc_mnc(needle); if (provider) return provider.get_name(); } return null; } // _findProviderForSid: // @sid: System Identifier of the serving CDMA network // // Tries to find the operator name corresponding to the given SID // function _findProviderForSid(sid) { if (sid == 0) return null; let mpd = _getMobileProvidersDatabase(); if (mpd) { let provider = mpd.lookup_cdma_sid(sid); if (provider) return provider.get_name(); } return null; } //------------------------------------------------------------------------------ // Support for the old ModemManager interface (MM < 0.7) //------------------------------------------------------------------------------ // The following are not the complete interfaces, just the methods we need // (or may need in the future) const ModemGsmNetworkInterface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface); const ModemCdmaInterface = ' \ \ \ \ \ \ \ \ \ \ \ \ '; const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface); const ModemGsm = new Lang.Class({ Name: 'ModemGsm', _init: function(path) { this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); this.signal_quality = 0; this.operator_name = null; // Code is duplicated because the function have different signatures this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, [quality]) { this.signal_quality = quality; this.emit('notify::signal-quality'); })); this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) { this.operator_name = _findProviderForMccMnc(name, code); this.emit('notify::operator-name'); })); this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) { if (err) { log(err); return; } let [status, code, name] = result; this.operator_name = _findProviderForMccMnc(name, code); this.emit('notify::operator-name'); })); this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) { if (err) { // it will return an error if the device is not connected this.signal_quality = 0; } else { let [quality] = result; this.signal_quality = quality; } this.emit('notify::signal-quality'); })); } }); Signals.addSignalMethods(ModemGsm.prototype); const ModemCdma = new Lang.Class({ Name: 'ModemCdma', _init: function(path) { this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); this.signal_quality = 0; this.operator_name = null; this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, params) { this.signal_quality = params[0]; this.emit('notify::signal-quality'); // receiving this signal means the device got activated // and we can finally call GetServingSystem if (this.operator_name == null) this._refreshServingSystem(); })); this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) { if (err) { // it will return an error if the device is not connected this.signal_quality = 0; } else { let [quality] = result; this.signal_quality = quality; } this.emit('notify::signal-quality'); })); }, _refreshServingSystem: function() { this._proxy.GetServingSystemRemote(Lang.bind(this, function([result], err) { if (err) { // it will return an error if the device is not connected this.operator_name = null; } else { let [bandClass, band, sid] = result; this.operator_name = _findProviderForSid(sid) } this.emit('notify::operator-name'); })); } }); Signals.addSignalMethods(ModemCdma.prototype); //------------------------------------------------------------------------------ // Support for the new ModemManager1 interface (MM >= 0.7) //------------------------------------------------------------------------------ const BroadbandModemInterface = ' \ \ \ \ '; const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface); const BroadbandModem3gppInterface = ' \ \ \ \ \ '; const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface); const BroadbandModemCdmaInterface = ' \ \ \ \ '; const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface); const BroadbandModem = new Lang.Class({ Name: 'BroadbandModem', _init: function(path, capabilities) { this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); this._capabilities = capabilities; this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) { if ('SignalQuality' in properties.deep_unpack()) this._reloadSignalQuality(); })); this._reloadSignalQuality(); this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) { let unpacked = properties.deep_unpack(); if ('OperatorName' in unpacked || 'OperatorCode' in unpacked) this._reload3gppOperatorName(); })); this._reload3gppOperatorName(); this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) { let unpacked = properties.deep_unpack(); if ('Nid' in unpacked || 'Sid' in unpacked) this._reloadCdmaOperatorName(); })); this._reloadCdmaOperatorName(); }, _reloadSignalQuality: function() { let [quality, recent] = this._proxy.SignalQuality; this.signal_quality = quality; this.emit('notify::signal-quality'); }, _reloadOperatorName: function() { let new_name = ""; if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0) new_name += this.operator_name_3gpp; if (this.operator_name_cdma && this.operator_name_cdma.length > 0) { if (new_name != "") new_name += ", "; new_name += this.operator_name_cdma; } this.operator_name = new_name; this.emit('notify::operator-name'); }, _reload3gppOperatorName: function() { let name = this._proxy_3gpp.OperatorName; let code = this._proxy_3gpp.OperatorCode; this.operator_name_3gpp = _findProviderForMccMnc(name, code); this._reloadOperatorName(); }, _reloadCdmaOperatorName: function() { let sid = this._proxy_cdma.Sid; this.operator_name_cdma = _findProviderForSid(sid); this._reloadOperatorName(); } }); Signals.addSignalMethods(BroadbandModem.prototype); Cinnamon-2.8.6/js/misc/windowUtils.js0000664000175000017500000000462612625666430016474 0ustar fabiofabioconst Clutter = imports.gi.Clutter; // Creates scaled clones of metaWindow and its transients, // keeping their relative size to each other. // When scaled, all clones are made to fit in width and height // if neither width nor height is given, windows are not scaled function createWindowClone(metaWindow, width, height, withTransients, withPositions) { let clones = []; let textures = []; if (!metaWindow) { return clones; } let metaWindowActor = metaWindow.get_compositor_private(); if (!metaWindowActor) { return clones; } let texture = metaWindowActor.get_texture(); let [windowWidth, windowHeight] = metaWindowActor.get_size(); let [maxWidth, maxHeight] = [windowWidth, windowHeight]; let [x, y] = metaWindowActor.get_position(); let [minX, minY] = [x, y]; let [maxX, maxY] = [minX + windowWidth, minY + windowHeight]; textures.push({t: texture, x: x, y: y, w: windowWidth, h: windowHeight}); if (withTransients) { metaWindow.foreach_transient(function(win) { let metaWindowActor = win.get_compositor_private(); texture = metaWindowActor.get_texture(); [windowWidth, windowHeight] = metaWindowActor.get_size(); [x, y] = metaWindowActor.get_position(); maxWidth = Math.max(maxWidth, windowWidth); maxHeight = Math.max(maxHeight, windowHeight); minX = Math.min(minX, x); maxX = Math.max(maxX, x + windowWidth); minY = Math.min(minY, y); maxY = Math.max(maxY, y + windowHeight); textures.push({t: texture, x: x, y: y, w: windowWidth, h: windowHeight}); }); } let scale = 1; let scaleWidth = 1; let scaleHeight = 1; if (width) { scaleWidth = Math.min(width/(maxX - minX), 1); } if (height) { scaleHeight = Math.min(height/(maxY - minY), 1); } if (width || height) { scale = Math.min(scaleWidth, scaleHeight); } for (let i = 0; i < textures.length; i++) { let data = textures[i]; let [texture, texWidth, texHeight, x, y] = [data.t, data.w, data.h, data.x, data.y]; if (withPositions) { x -= minX; y -= minY; } let params = {}; params.source = texture; if (scale != 1) { params.width = Math.round(texWidth * scale); params.height = Math.round(texHeight * scale); x = Math.round(x * scale); y = Math.round(y * scale); } let clone = {actor: new Clutter.Clone(params), x: x, y: y}; clones.push(clone); } return clones; } Cinnamon-2.8.6/js/misc/screenSaver.js0000664000175000017500000000343412625666430016420 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Gio = imports.gi.Gio; const ScreenSaverIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ '; const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface); function ScreenSaverProxy() { var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session, g_interface_name: ScreenSaverInfo.name, g_interface_info: ScreenSaverInfo, g_name: 'org.gnome.ScreenSaver', g_object_path: '/org/gnome/ScreenSaver', g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); self.init(null); self.screenSaverActive = false; self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) { self.screenSaverActive = isActive; }); self.connect('notify::g-name-owner', function() { if (self.g_name_owner) { self.GetActiveRemote(function(result, excp) { if (result) { let [isActive] = result; self.screenSaverActive = isActive; } }); } else self.screenSaverActive = false; }); return self; }Cinnamon-2.8.6/js/misc/gnomeSession.js0000664000175000017500000000556512625666430016620 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const Lang = imports.lang; const Signals = imports.signals; const PresenceIface = '\ \ \ \ \ \ \ \ \ \ \ '; const PresenceStatus = { AVAILABLE: 0, INVISIBLE: 1, BUSY: 2, IDLE: 3 }; var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface); function Presence(initCallback, cancellable) { return new PresenceProxy(Gio.DBus.session, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', initCallback, cancellable); } const InhibitorIface = ' \ \ \ \ \ \ \ \ \ \ '; var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface); function Inhibitor(objectPath, initCallback, cancellable) { return new InhibitorProxy(Gio.DBus.session, 'org.gnome.SessionManager', objectPath, initCallback, cancellable); } const SessionManagerIface = '\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface); function SessionManager(initCallback, cancellable) { return new SessionManagerProxy(Gio.DBus.session, 'org.gnome.SessionManager', '/org/gnome/SessionManager', initCallback, cancellable); }Cinnamon-2.8.6/js/misc/fileUtils.js0000664000175000017500000000542312625666430016100 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; function listDirAsync(file, callback) { let allFiles = []; file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, function (obj, res) { let enumerator = obj.enumerate_children_finish(res); function onNextFileComplete(obj, res) { let files = obj.next_files_finish(res); if (files.length) { allFiles = allFiles.concat(files); enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete); } else { enumerator.close(null); callback(allFiles); } } enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete); }); } function deleteGFile(file) { // Work around 'delete' being a keyword in JS. return file['delete'](null); } function recursivelyDeleteDir(dir) { let children = dir.enumerate_children('standard::name,standard::type', Gio.FileQueryInfoFlags.NONE, null); let info, child; while ((info = children.next_file(null)) != null) { let type = info.get_file_type(); let child = dir.get_child(info.get_name()); if (type == Gio.FileType.REGULAR) deleteGFile(child); else if (type == Gio.FileType.DIRECTORY) recursivelyDeleteDir(child); } deleteGFile(dir); } function getUserDesktopDir() { // Didn't find a function returning the user desktop dir, so parsing the user-dirs.dirs file to get it let userdirsFile = Gio.file_new_for_path(GLib.get_home_dir()+"/.config/user-dirs.dirs"); let path; if (userdirsFile.query_exists(null)){ try{ let data = userdirsFile.load_contents(null); let dataDic = new Array(); let lines = data[1].toString().split("\n"); for (var i in lines){ if (lines[i][0]=="#") continue; let line = lines[i].split("=", 2); if (line.length==2){ dataDic[line[0]] = line[1]; } } if (dataDic["XDG_DESKTOP_DIR"]) path = dataDic["XDG_DESKTOP_DIR"].substring(1, dataDic["XDG_DESKTOP_DIR"].length-1).replace("$HOME", GLib.get_home_dir()); else path = GLib.get_home_dir() + '/Desktop'; }catch(e){ path = GLib.get_home_dir() + '/Desktop'; } }else path = GLib.get_home_dir() + '/Desktop'; let file = Gio.file_new_for_path(path); if (file.query_exists(null)) return path; else return null; } Cinnamon-2.8.6/js/misc/pointerTracker.js0000664000175000017500000000242312625666430017131 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gdk = imports.gi.Gdk; function PointerTracker(){ this._init(); } PointerTracker.prototype = { _init: function() { let display = Gdk.Display.get_default(); let deviceManager = display.get_device_manager(); let pointer = deviceManager.get_client_pointer(); let [lastScreen, lastPointerX, lastPointerY] = pointer.get_position(); this.hasMoved = function() { let [screen, pointerX, pointerY] = pointer.get_position(); try { return !(screen == lastScreen && pointerX == lastPointerX && pointerY == lastPointerY); } finally { [lastScreen, lastPointerX, lastPointerY] = [screen, pointerX, pointerY]; } }; this.getPosition = function() { [lastScreen, lastPointerX, lastPointerY] = pointer.get_position(); return [lastPointerX, lastPointerY, lastScreen]; }; this.setPosition = function(x, y, screenOpt) { let [screen, pointerX, pointerY] = pointer.get_position(); pointer.warp(screenOpt || screen, Math.round(x), Math.round(y)); [lastScreen, lastPointerX, lastPointerY] = pointer.get_position(); }; } }; Cinnamon-2.8.6/js/perf/0000775000175000017500000000000012625666430013577 5ustar fabiofabioCinnamon-2.8.6/js/perf/core.js0000664000175000017500000001766712625666430015106 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Main = imports.ui.main; const Scripting = imports.ui.scripting; // This performance script measure the most important (core) performance // metrics for Cinnamon. By looking at the output metrics of this script // someone should be able to get an idea of how well Cinnamon is performing // on a particular system. let METRICS = { overviewLatencyFirst: { description: "Time to first frame after triggering overview, first time", units: "us" }, overviewFpsFirst: { description: "Frame rate when going to the overview, first time", units: "frames / s" }, overviewLatencySubsequent: { description: "Time to first frame after triggering overview, second time", units: "us"}, overviewFpsSubsequent: { description: "Frames rate when going to the overview, second time", units: "frames / s" }, overviewFps5Windows: { description: "Frames rate when going to the overview, 5 windows open", units: "frames / s" }, overviewFps10Windows: { description: "Frames rate when going to the overview, 10 windows open", units: "frames / s" }, overviewFps5Maximized: { description: "Frames rate when going to the overview, 5 maximized windows open", units: "frames / s" }, overviewFps10Maximized: { description: "Frames rate when going to the overview, 10 maximized windows open", units: "frames / s" }, overviewFps5Alpha: { description: "Frames rate when going to the overview, 5 alpha-transparent windows open", units: "frames / s" }, overviewFps10Alpha: { description: "Frames rate when going to the overview, 10 alpha-transparent windows open", units: "frames / s" }, usedAfterOverview: { description: "Malloc'ed bytes after the overview is shown once", units: "B" }, leakedAfterOverview: { description: "Additional malloc'ed bytes the second time the overview is shown", units: "B" }, applicationsShowTimeFirst: { description: "Time to switch to applications view, first time", units: "us" }, applicationsShowTimeSubsequent: { description: "Time to switch to applications view, second time", units: "us"} }; let WINDOW_CONFIGS = [ { width: 640, height: 480, alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent' }, { width: 640, height: 480, alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows' }, { width: 640, height: 480, alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows' }, { width: 640, height: 480, alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized' }, { width: 640, height: 480, alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized' }, { width: 640, height: 480, alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha' }, { width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' } ]; function run() { Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview"); Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing"); Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview"); Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view"); Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view"); Main.overview.connect('shown', function() { Scripting.scriptEvent('overviewShowDone'); }); yield Scripting.sleep(1000); for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) { // We go to the overview twice for each configuration; the first time // to calculate the mipmaps for the windows, the second time to get // a clean set of numbers. if ((i % 2) == 0) { let config = WINDOW_CONFIGS[i / 2]; yield Scripting.destroyTestWindows(); for (let k = 0; k < config.count; k++) yield Scripting.createTestWindow(config.width, config.height, config.alpha, config.maximized); yield Scripting.waitTestWindows(); yield Scripting.sleep(1000); yield Scripting.waitLeisure(); } Scripting.scriptEvent('overviewShowStart'); Main.overview.show(); yield Scripting.waitLeisure(); Main.overview.hide(); yield Scripting.waitLeisure(); global.gc(); yield Scripting.sleep(1000); Scripting.collectStatistics(); Scripting.scriptEvent('afterShowHide'); } yield Scripting.destroyTestWindows(); yield Scripting.sleep(1000); Main.overview.show(); yield Scripting.waitLeisure(); } let showingOverview = false; let finishedShowingOverview = false; let overviewShowStart; let overviewFrames; let overviewLatency; let mallocUsedSize = 0; let overviewShowCount = 0; let firstOverviewUsedSize; let haveSwapComplete = false; let applicationsShowStart; let applicationsShowCount = 0; function script_overviewShowStart(time) { showingOverview = true; finishedShowingOverview = false; overviewShowStart = time; overviewFrames = 0; } function script_overviewShowDone(time) { // We've set up the state at the end of the zoom out, but we // need to wait for one more frame to paint before we count // ourselves as done. finishedShowingOverview = true; } function script_applicationsShowStart(time) { applicationsShowStart = time; } function script_applicationsShowDone(time) { applicationsShowCount++; if (applicationsShowCount == 1) METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart; else METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart; } function script_afterShowHide(time) { if (overviewShowCount == 1) { METRICS.usedAfterOverview.value = mallocUsedSize; } else { METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value; } } function malloc_usedSize(time, bytes) { mallocUsedSize = bytes; } function _frameDone(time) { if (showingOverview) { if (overviewFrames == 0) overviewLatency = time - overviewShowStart; overviewFrames++; } if (finishedShowingOverview) { showingOverview = false; finishedShowingOverview = false; overviewShowCount++; let dt = (time - (overviewShowStart + overviewLatency)) / 1000000; // If we see a start frame and an end frame, that would // be 1 frame for a FPS computation, hence the '- 1' let fps = (overviewFrames - 1) / dt; if (overviewShowCount == 1) { METRICS.overviewLatencyFirst.value = overviewLatency; METRICS.overviewFpsFirst.value = fps; } else if (overviewShowCount == 2) { METRICS.overviewLatencySubsequent.value = overviewLatency; } // Other than overviewFpsFirst, we collect FPS metrics the second // we show each window configuration. overviewShowCount is 1,2,3... if (overviewShowCount % 2 == 0) { let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1]; METRICS[config.metric].value = fps; } } } function glx_swapComplete(time, swapTime) { haveSwapComplete = true; _frameDone(swapTime); } function clutter_stagePaintDone(time) { // If we aren't receiving GLXBufferSwapComplete events, then we approximate // the time the user sees a frame with the time we finished doing drawing // commands for the frame. This doesn't take into account the time for // the GPU to finish painting, and the time for waiting for the buffer // swap, but if this are uniform - every frame takes the same time to draw - // then it won't upset our FPS calculation, though the latency value // will be slightly too low. if (!haveSwapComplete) _frameDone(time); } Cinnamon-2.8.6/js/ui/0000775000175000017500000000000012625666430013260 5ustar fabiofabioCinnamon-2.8.6/js/ui/windowEffects.js0000664000175000017500000003046412625666430016434 0ustar fabiofabioconst Clutter = imports.gi.Clutter; const Meta = imports.gi.Meta; const AppletManager = imports.ui.appletManager; const Main = imports.ui.main; const Tweener = imports.ui.tweener; function Effect(){ throw new TypeError("Trying to instantiate abstract class WindowEffects.Effect"); } Effect.prototype = { _init: function(wm){ //wm is the instance of windowManger.js this.wm = wm; }, _end: function(actor){ actor.set_scale(1, 1); actor.opacity = actor.orig_opacity || 255; actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST); }, _fadeWindow: function(cinnamonwm, actor, opacity, time, transition){ Tweener.addTween(actor, { opacity: opacity, time: time, min: 0, max: 255, transition: transition, onComplete: this.wm._endWindowEffect, onCompleteScope: this.wm, onCompleteParams: [cinnamonwm, this.name, actor] }); }, _scaleWindow: function(cinnamonwm, actor, scale_x, scale_y, time, transition, keepAnchorPoint){ if (!keepAnchorPoint) actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER); Tweener.addTween(actor, { scale_x: scale_x, scale_y: scale_y, time: time, min: 0, transition: transition, onComplete: this.wm._endWindowEffect, onCompleteScope: this.wm, onCompleteParams: [cinnamonwm, this.name, actor] }); }, _moveWindow: function(cinnamonwm, actor, x, y, time, transition){ Tweener.addTween(actor, { x: x, y: y, time: time, transition: transition, onComplete: this.wm._endWindowEffect, onCompleteScope: this.wm, onCompleteParams: [cinnamonwm, this.name, actor] }); } }; function Map(){ this._init.apply(this, arguments); } Map.prototype = { __proto__: Effect.prototype, name: "map", arrayName: "_mapping", wmCompleteName: "completed_map", scale: function(cinnamonwm, actor, time, transition){ actor.set_scale(0, 0); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition); }, fade: function(cinnamonwm, actor, time, transition){ actor.opacity = 0; this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); }, blend: function(cinnamonwm, actor, time, transition){ actor.opacity = 0; actor.set_scale(1.5, 1.5); this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition); }, move: function(cinnamonwm, actor, time, transition){ let [width, height] = actor.get_allocation_box().get_size(); let [xDest, yDest] = actor.get_transformed_position(); xDest += width /= 2; yDest += height /= 2; let [xSrc, ySrc] = global.get_pointer(); xSrc -= width; ySrc -= height; actor.set_position(xSrc, ySrc); actor.set_scale(0, 0); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition); this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, flyUp: function(cinnamonwm, actor, time, transition){ //FIXME: somehow we need this line to get the correct position, without it will return [0, 0] actor.get_allocation_box().get_size(); let [xDest, yDest] = actor.get_transformed_position(); let ySrc = global.stage.get_height(); actor.set_position(xDest, ySrc); let dist = Math.abs(ySrc - yDest); time *= dist / Main.layoutManager.primaryMonitor.height * 2; // The transition time set is the time if the animation starts/ends at the middle of the screen. Scale it proportional to the actual distance so that the speed of all animations will be constant. this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, flyDown: function(cinnamonwm, actor, time, transition){ //FIXME - see also flyUp actor.get_allocation_box().get_size(); let [xDest, yDest] = actor.get_transformed_position(); let ySrc = -actor.get_allocation_box().get_height(); actor.set_position(xDest, ySrc); let dist = Math.abs(ySrc - yDest); time *= dist / Main.layoutManager.primaryMonitor.height * 2; // The time time set is the time if the animation starts/ends at the middle of the screen. Scale it proportional to the actual distance so that the speed of all animations will be constant. this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, traditional: function(cinnamonwm, actor, time, transition) { switch (actor._windowType) { case Meta.WindowType.NORMAL: actor.set_pivot_point(0, 0); actor.scale_x = 0.01; actor.scale_y = 0.05; actor.opacity = 0; this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition); break; case Meta.WindowType.MODAL_DIALOG: case Meta.WindowType.DIALOG: actor.set_pivot_point(0, 0); actor.scale_x = 1; actor.scale_y = 0; actor.opacity = 0; this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition); break; default: this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); } } } function Close(){ this._init.apply(this, arguments); } Close.prototype = { __proto__: Effect.prototype, name: "close", arrayName: "_destroying", wmCompleteName: "completed_destroy", _end: function(actor){ let parent = actor.get_meta_window().get_transient_for(); if(parent && actor._parentDestroyId){ parent.disconnect(actor._parentDestroyId); actor._parentDestroyId = 0; } }, scale: function(cinnamonwm, actor, time, transition){ this._scaleWindow(cinnamonwm, actor, 0, 0, time, transition); }, fade: function(cinnamonwm, actor, time, transition){ Tweener.removeTweens(actor); this._fadeWindow(cinnamonwm, actor, 0, time, transition); }, blend: function(cinnamonwm, actor, time, transition){ this._fadeWindow(cinnamonwm, actor, 0, time, transition); this._scaleWindow(cinnamonwm, actor, 1.5, 1.5, time, transition); }, move: function(cinnamonwm, actor, time, transition){ let [xDest, yDest] = global.get_pointer(); this._scaleWindow(cinnamonwm, actor, 0, 0, time, transition); this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, flyUp: function(cinnamonwm, actor, time, transition){ let xDest = actor.get_transformed_position()[0]; let yDest = -actor.get_allocation_box().get_height(); let dist = Math.abs(actor.get_transformed_position()[1] - yDest); time *= dist / Main.layoutManager.primaryMonitor.height * 2; // The time time set is the time if the animation starts/ends at the middle of the screen. Scale it proportional to the actual distance so that the speed of all animations will be constant. this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, flyDown: function(cinnamonwm, actor, time, transition){ let xDest = actor.get_transformed_position()[0]; let yDest = global.stage.get_height(); let dist = Math.abs(actor.get_transformed_position()[1] - yDest); time *= dist / Main.layoutManager.primaryMonitor.height * 2; // The transition time set is the time if the animation starts/ends at the middle of the screen. Scale it proportional to the actual distance so that the speed of all animations will be constant. this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); }, traditional: function(cinnamonwm, actor, time, transition) { switch (actor._windowType) { case Meta.WindowType.NORMAL: actor.set_pivot_point(0, 0); this._scaleWindow(cinnamonwm, actor, 0.8, 0.8, time, transition); this._fadeWindow(cinnamonwm, actor, 0, time, transition); break; case Meta.WindowType.MODAL_DIALOG: case Meta.WindowType.DIALOG: actor.set_pivot_point(0, 0); this._fadeWindow(cinnamonwm, actor, 0.5, time, transition); this._scaleWindow(cinnamonwm, actor, 1.0, 0, time, transition); break; default: this.scale(cinnamonwm, actor, time, transition); } } } function Minimize(){ this._init.apply(this, arguments); } Minimize.prototype = { __proto__: Close.prototype, name: "minimize", arrayName: "_minimizing", wmCompleteName: "completed_minimize", //use default _end method _end: Effect.prototype._end, traditional: function(cinnamonwm, actor, time, transition) { let success; let geom = new Meta.Rectangle(); success = actor.meta_window.get_icon_geometry(geom); if (success) { actor.set_scale(1, 1); let xDest, yDest, xScale, yScale; xDest = geom.x; yDest = geom.y; xScale = geom.width / actor.width; yScale = geom.height / actor.height; actor.get_meta_window()._cinnamonwm_has_origin = true; this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); this._scaleWindow(cinnamonwm, actor, xScale, yScale, time, transition, true); this._fadeWindow(cinnamonwm, actor, 0, time, transition); } else { this.scale(cinnamonwm, actor, time, transition); // fall-back effect } } } function Unminimize(){ this._init.apply(this, arguments); } Unminimize.prototype = { //unminimizing is a "map" effect but should use "minimize" setting values __proto__: Effect.prototype, name: "unminimize", arrayName: "_mapping", wmCompleteName: "completed_map", _end: Map.prototype._end, traditional: function(cinnamonwm, actor, time, transition) { let success; let geom = new Meta.Rectangle(); success = actor.meta_window.get_icon_geometry(geom); if (success) { actor.set_scale(0.1, 0.1); actor.opacity = 0; let xSrc = geom.x; let ySrc = geom.y; let [xDest, yDest] = actor.get_transformed_position(); actor.set_position(xSrc, ySrc); this._moveWindow(cinnamonwm, actor, xDest, yDest, time, transition); this._scaleWindow(cinnamonwm, actor, 1, 1, time, transition, true); this._fadeWindow(cinnamonwm, actor, actor.orig_opacity, time, transition); } else { throw "No origin found"; } } } function Tile(){ this._init.apply(this, arguments); } Tile.prototype = { __proto__: Effect.prototype, name: "tile", arrayName: "_tiling", wmCompleteName: "completed_tile", scale: function(cinnamonwm, actor, time, transition, args){ let [targetX, targetY, targetWidth, targetHeight] = args; if(targetWidth == actor.width) targetWidth -= 1; if(targetHeight == actor.height) targetHeight -= 1; let scale_x = targetWidth / actor.width; let scale_y = targetHeight / actor.height; let anchor_x = (actor.x - targetX) * actor.width / (targetWidth - actor.width); let anchor_y = (actor.y - targetY) * actor.height / (targetHeight - actor.height); actor.move_anchor_point(anchor_x, anchor_y); this._scaleWindow(cinnamonwm, actor, scale_x, scale_y, time, transition, true); } } function Maximize(){ this._init.apply(this, arguments); } Maximize.prototype = { __proto__: Tile.prototype, name: "maximize", arrayName: "_maximizing", wmCompleteName: "completed_maximize", } function Unmaximize(){ this._init.apply(this, arguments); } Unmaximize.prototype = { __proto__: Tile.prototype, name: "unmaximize", arrayName: "_unmaximizing", wmCompleteName: "completed_unmaximize" } Cinnamon-2.8.6/js/ui/dnd.js0000664000175000017500000007257312625666430014401 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const St = imports.gi.St; const Lang = imports.lang; const Meta = imports.gi.Meta; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const Tweener = imports.ui.tweener; const Main = imports.ui.main; const Params = imports.misc.params; const DND_ANIMATION_TIME = 0.2; // Time to scale down to maxDragActorSize const SCALE_ANIMATION_TIME = 0.25; // Time to animate to original position on cancel const SNAP_BACK_ANIMATION_TIME = 0.25; // Time to animate to original position on success const REVERT_ANIMATION_TIME = 0.75; const DragMotionResult = { NO_DROP: 0, COPY_DROP: 1, MOVE_DROP: 2, CONTINUE: 3 }; const DRAG_CURSOR_MAP = { 0: Cinnamon.Cursor.DND_UNSUPPORTED_TARGET, 1: Cinnamon.Cursor.DND_COPY, 2: Cinnamon.Cursor.DND_MOVE }; const DragDropResult = { FAILURE: 0, SUCCESS: 1, CONTINUE: 2 }; let eventHandlerActor = null; let currentDraggable = null; let dragMonitors = []; let targetMonitors = []; function _getEventHandlerActor() { if (!eventHandlerActor) { eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 }); Main.uiGroup.add_actor(eventHandlerActor); // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen // when you've grabbed the pointer. eventHandlerActor.connect('event', function(actor, event) { return currentDraggable._onEvent(actor, event); }); } return eventHandlerActor; } function addDragMonitor(monitor) { dragMonitors.push(monitor); } function removeDragMonitor(monitor) { for (let i = 0; i < dragMonitors.length; i++) if (dragMonitors[i] == monitor) { dragMonitors.splice(i, 1); return; } } function isDragging() { return currentDraggable != null; } const _Draggable = new Lang.Class({ Name: 'Draggable', _init : function(actor, params, target) { params = Params.parse(params, { manualMode: false, restoreOnSuccess: false, overrideX: undefined, overrideY: undefined, dragActorMaxSize: undefined, dragActorOpacity: undefined }); this.inhibit = false; // Use the inhibit flag to temporarily disable an object from being draggable this.actor = actor; this.target = null; if (target) { this.target = target; } this.buttonPressEventId = 0; this.destroyEventId = 0; if (!params.manualMode) this.buttonPressEventId = this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.destroyEventId = this.actor.connect('destroy', Lang.bind(this, function() { this._actorDestroyed = true; if (this._dragInProgress && this._dragCancellable) this._cancelDrag(global.get_current_time()); this.disconnectAll(); })); this._onEventId = null; this._restoreOnSuccess = params.restoreOnSuccess; this._dragActorMaxSize = params.dragActorMaxSize; this._dragActorOpacity = params.dragActorOpacity; this._overrideX = params.overrideX; this._overrideY = params.overrideY; this._buttonDown = false; // The mouse button has been pressed and has not yet been released. this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet. this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting). this._dragCancellable = true; this._eventsGrabbed = false; }, _onButtonPress : function (actor, event) { if (this.inhibit) return false; if (event.get_button() != 1) return false; if (Tweener.getTweenCount(actor)) return false; this._buttonDown = true; this._grabActor(); let [stageX, stageY] = event.get_coords(); this._dragStartX = stageX; this._dragStartY = stageY; return false; }, _grabActor: function() { Clutter.grab_pointer(this.actor); this._onEventId = this.actor.connect('event', Lang.bind(this, this._onEvent)); }, _ungrabActor: function() { if (!this._onEventId) return; Clutter.ungrab_pointer(); this.actor.disconnect(this._onEventId); this._onEventId = null; }, _grabEvents: function() { if (!this._eventsGrabbed) { this._eventsGrabbed = Main.pushModal(_getEventHandlerActor()); if (this._eventsGrabbed) Clutter.grab_pointer(_getEventHandlerActor()); } }, _ungrabEvents: function() { if (this._eventsGrabbed) { Clutter.ungrab_pointer(); Main.popModal(_getEventHandlerActor()); this._eventsGrabbed = false; } }, _onEvent: function(actor, event) { // We intercept BUTTON_RELEASE event to know that the button was released in case we // didn't start the drag, to drop the draggable in case the drag was in progress, and // to complete the drag and ensure that whatever happens to be under the pointer does // not get triggered if the drag was cancelled with Esc. if (event.type() == Clutter.EventType.BUTTON_RELEASE) { this._buttonDown = false; if (this._dragInProgress) { return this._dragActorDropped(event); } else if (this._dragActor != null && !this._animationInProgress) { // Drag must have been cancelled with Esc. this._dragComplete(); return true; } else { // Drag has never started. this._ungrabActor(); return false; } // We intercept MOTION event to figure out if the drag has started and to draw // this._dragActor under the pointer when dragging is in progress } else if (event.type() == Clutter.EventType.MOTION) { if (this._dragInProgress) { return this._updateDragPosition(event); } else if (this._dragActor == null) { return this._maybeStartDrag(event); } // We intercept KEY_PRESS event so that we can process Esc key press to cancel // dragging and ignore all other key presses. } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragInProgress) { let symbol = event.get_key_symbol(); if (symbol == Clutter.Escape) { this._cancelDrag(event.get_time()); return true; } } return false; }, /** * fakeRelease: * * Fake a release event. * Must be called if you want to intercept release events on draggable * actors for other purposes (for example if you're using * PopupMenu.ignoreRelease()) */ fakeRelease: function() { this._buttonDown = false; this._ungrabActor(); }, /** * startDrag: * @stageX: X coordinate of event * @stageY: Y coordinate of event * @time: Event timestamp * * Directly initiate a drag and drop operation from the given actor. * This function is useful to call if you've specified manualMode * for the draggable. */ startDrag: function (stageX, stageY, time) { currentDraggable = this; this._dragInProgress = true; // Special-case St.Button: the pointer grab messes with the internal // state, so force a reset to a reasonable state here if (this.actor instanceof St.Button) { this.actor.fake_release(); this.actor.hover = false; } this.emit('drag-begin', time); if (this._onEventId) this._ungrabActor(); this._grabEvents(); global.set_cursor(Cinnamon.Cursor.DND_IN_DRAG); this._dragX = this._dragStartX = stageX; this._dragY = this._dragStartY = stageY; if (this.actor._delegate && this.actor._delegate.getDragActor) { this._dragActor = this.actor._delegate.getDragActor(); this._dragActor.reparent(Main.uiGroup); this._dragActor.raise_top(); Cinnamon.util_set_hidden_from_pick(this._dragActor, true); // Drag actor does not always have to be the same as actor. For example drag actor // can be an image that's part of the actor. So to perform "snap back" correctly we need // to know what was the drag actor source. if (this.actor._delegate.getDragActorSource) { this._dragActorSource = this.actor._delegate.getDragActorSource(); // If the user dragged from the source, then position // the dragActor over it. Otherwise, center it // around the pointer let [sourceX, sourceY] = this._dragActorSource.get_transformed_position(); let x, y; if (stageX > sourceX && stageX <= sourceX + this._dragActor.width && stageY > sourceY && stageY <= sourceY + this._dragActor.height) { x = sourceX; y = sourceY; } else { x = stageX - this._dragActor.width / 2; y = stageY - this._dragActor.height / 2; } this._dragActor.set_position(x, y); } else { this._dragActorSource = this.actor; } this._dragOrigParent = undefined; this._dragOffsetX = this._dragActor.x - this._dragStartX; this._dragOffsetY = this._dragActor.y - this._dragStartY; } else { this._dragActor = this.actor; this._dragActorSource = undefined; this._dragOrigParent = this.actor.get_parent(); this._dragOrigX = this._dragActor.x; this._dragOrigY = this._dragActor.y; this._dragOrigScale = this._dragActor.scale_x; // Set the actor's scale such that it will keep the same // transformed size when it's reparented to the uiGroup let [scaledWidth, scaledHeight] = this.actor.get_transformed_size(); this._dragActor.set_scale(scaledWidth / this.actor.width, scaledHeight / this.actor.height); let [actorStageX, actorStageY] = this.actor.get_transformed_position(); this._dragOffsetX = actorStageX - this._dragStartX; this._dragOffsetY = actorStageY - this._dragStartY; this._dragActor.reparent(Main.uiGroup); this._dragActor.raise_top(); Cinnamon.util_set_hidden_from_pick(this._dragActor, true); } this._dragOrigOpacity = this._dragActor.opacity; if (this._dragActorOpacity != undefined) this._dragActor.opacity = this._dragActorOpacity; this._snapBackX = this._dragStartX + this._dragOffsetX; this._snapBackY = this._dragStartY + this._dragOffsetY; this._snapBackScale = this._dragActor.scale_x; if (this._dragActorMaxSize != undefined) { let [scaledWidth, scaledHeight] = this._dragActor.get_transformed_size(); let currentSize = Math.max(scaledWidth, scaledHeight); if (currentSize > this._dragActorMaxSize) { let scale = this._dragActorMaxSize / currentSize; let origScale = this._dragActor.scale_x; let origDragOffsetX = this._dragOffsetX; let origDragOffsetY = this._dragOffsetY; // The position of the actor changes as we scale // around the drag position, but we can't just tween // to the final position because that tween would // fight with updates as the user continues dragging // the mouse; instead we do the position computations in // an onUpdate() function. Tweener.addTween(this._dragActor, { scale_x: scale * origScale, scale_y: scale * origScale, time: SCALE_ANIMATION_TIME, transition: 'easeOutQuad', onUpdate: function() { let currentScale = this._dragActor.scale_x / origScale; this._dragOffsetX = currentScale * origDragOffsetX; this._dragOffsetY = currentScale * origDragOffsetY; this._setDragActorPosition(); }, onUpdateScope: this }); } } }, _maybeStartDrag: function(event) { let [stageX, stageY] = event.get_coords(); // See if the user has moved the mouse enough to trigger a drag let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold; if ((Math.abs(stageX - this._dragStartX) > threshold || Math.abs(stageY - this._dragStartY) > threshold)) { this.startDrag(stageX, stageY, event.get_time()); this._updateDragPosition(event); } return true; }, _updateDragHover : function () { this._updateHoverId = 0; let target = null; let x = this._overrideX == undefined ? this._dragX : this._overrideX; let y = this._overrideY == undefined ? this._dragY : this._overrideY; if (this.target) target = this.target; else target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, x, y); let dragEvent = { x: this._dragX, y: this._dragY, dragActor: this._dragActor, source: this.actor._delegate, targetActor: target }; for (let i = 0; i < dragMonitors.length; i++) { let motionFunc = dragMonitors[i].dragMotion; if (motionFunc) { let result = motionFunc(dragEvent); if (result != DragMotionResult.CONTINUE) { global.set_cursor(DRAG_CURSOR_MAP[result]); return false; } } } while (target) { if (target._delegate && target._delegate.handleDragOver) { let [r, targX, targY] = target.transform_stage_point(x, y); // We currently loop through all parents on drag-over even if one of the children has handled it. // We can check the return value of the function and break the loop if it's true if we don't want // to continue checking the parents. let result = target._delegate.handleDragOver(this.actor._delegate, this._dragActor, targX, targY, 0); if (result != DragMotionResult.CONTINUE) { global.set_cursor(DRAG_CURSOR_MAP[result]); return false; } } target = target.get_parent(); } global.set_cursor(Cinnamon.Cursor.DND_IN_DRAG); return false; }, _queueUpdateDragHover: function() { if (this._updateHoverId) { GLib.source_remove(this._updateHoverId); this._updateHoverId = 0; } this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._updateDragHover)); }, _updateDragPosition : function (event) { let [stageX, stageY] = event.get_coords(); this._dragX = stageX; this._dragY = stageY; this._setDragActorPosition(); this._queueUpdateDragHover(); return true; }, _setDragActorPosition: function() { this._dragActor.x = this._overrideX == undefined ? this._dragX + this._dragOffsetX : this._overrideX; this._dragActor.y = this._overrideY == undefined ? this._dragY + this._dragOffsetY : this._overrideY; }, _dragActorDropped: function(event) { let [dropX, dropY] = event.get_coords(); let target = null; if (this._overrideX != undefined) dropX = this._overrideX; if (this._overrideY != undefined) dropY = this._overrideY; if (this.target) target = this.target; else target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, dropX, dropY); // We call observers only once per motion with the innermost // target actor. If necessary, the observer can walk the // parent itself. let dropEvent = { dropActor: this._dragActor, targetActor: target, clutterEvent: event }; for (let i = 0; i < dragMonitors.length; i++) { let dropFunc = dragMonitors[i].dragDrop; if (dropFunc) switch (dropFunc(dropEvent)) { case DragDropResult.FAILURE: case DragDropResult.SUCCESS: return true; case DragDropResult.CONTINUE: continue; } } // At this point it is too late to cancel a drag by destroying // the actor, the fate of which is decided by acceptDrop and its // side-effects this._dragCancellable = false; while (target) { if (target._delegate && target._delegate.acceptDrop) { let [r, targX, targY] = target.transform_stage_point(dropX, dropY); if (target._delegate.acceptDrop(this.actor._delegate, this._dragActor, targX, targY, event.get_time())) { // If it accepted the drop without taking the actor, // handle it ourselves. if (this._dragActor.get_parent() == Main.uiGroup) { if (this._restoreOnSuccess) { this._restoreDragActor(event.get_time()); return true; } else this._dragActor.destroy(); } this._dragInProgress = false; global.unset_cursor(); this.emit('drag-end', event.get_time(), true); this._dragComplete(); return true; } } target = target.get_parent(); } this._cancelDrag(event.get_time()); return true; }, _getRestoreLocation: function() { let x, y, scale; if (this._dragActorSource && this._dragActorSource.visible) { // Snap the clone back to its source [x, y] = this._dragActorSource.get_transformed_position(); let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size(); scale = this._dragActor.width / sourceScaledWidth; } else if (this._dragOrigParent) { // Snap the actor back to its original position within // its parent, adjusting for the fact that the parent // may have been moved or scaled let [parentX, parentY] = this._dragOrigParent.get_transformed_position(); let [parentWidth, parentHeight] = this._dragOrigParent.get_size(); let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size(); let parentScale = 1.0; if (parentWidth != 0) parentScale = parentScaledWidth / parentWidth; x = parentX + parentScale * this._dragOrigX; y = parentY + parentScale * this._dragOrigY; scale = this._dragOrigScale * parentScale; } else { // Snap back actor to its original stage position x = this._snapBackX; y = this._snapBackY; scale = this._snapBackScale; } return [x, y, scale]; }, _cancelDrag: function(eventTime) { this.emit('drag-cancelled', eventTime); this._dragInProgress = false; let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation(); if (this._actorDestroyed) { global.unset_cursor(); if (!this._buttonDown) this._dragComplete(); this.emit('drag-end', eventTime, false); if (!this._dragOrigParent) this._dragActor.destroy(); return; } this._animationInProgress = true; // No target, so snap back Tweener.addTween(this._dragActor, { x: snapBackX, y: snapBackY, scale_x: snapBackScale, scale_y: snapBackScale, opacity: this._dragOrigOpacity, time: SNAP_BACK_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._onAnimationComplete, onCompleteScope: this, onCompleteParams: [this._dragActor, eventTime] }); }, _restoreDragActor: function(eventTime) { this._dragInProgress = false; [restoreX, restoreY, restoreScale] = this._getRestoreLocation(); // fade the actor back in at its original location this._dragActor.set_position(restoreX, restoreY); this._dragActor.set_scale(restoreScale, restoreScale); this._dragActor.opacity = 0; this._animationInProgress = true; Tweener.addTween(this._dragActor, { opacity: this._dragOrigOpacity, time: REVERT_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._onAnimationComplete, onCompleteScope: this, onCompleteParams: [this._dragActor, eventTime] }); }, _onAnimationComplete : function (dragActor, eventTime) { if (this._dragOrigParent) { dragActor.reparent(this._dragOrigParent); dragActor.set_scale(this._dragOrigScale, this._dragOrigScale); dragActor.set_position(this._dragOrigX, this._dragOrigY); } else { dragActor.destroy(); } global.unset_cursor(); this.emit('drag-end', eventTime, false); this._animationInProgress = false; if (!this._buttonDown) this._dragComplete(); }, _dragComplete: function() { if (!this._actorDestroyed) Cinnamon.util_set_hidden_from_pick(this._dragActor, false); this._ungrabEvents(); global.sync_pointer(); if (this._updateHoverId) { GLib.source_remove(this._updateHoverId); this._updateHoverId = 0; } this._dragActor = undefined; currentDraggable = null; } }); Signals.addSignalMethods(_Draggable.prototype); /** * makeDraggable: * @actor: Source actor * @params: (optional) Additional parameters * * Create an object which controls drag and drop for the given actor. * * If %manualMode is %true in @params, do not automatically start * drag and drop on click * * If %dragActorMaxSize is present in @params, the drag actor will * be scaled down to be no larger than that size in pixels. * * If %dragActorOpacity is present in @params, the drag actor will * will be set to have that opacity during the drag. * * Note that when the drag actor is the source actor and the drop * succeeds, the actor scale and opacity aren't reset; if the drop * target wants to reuse the actor, it's up to the drop target to * reset these values. */ function makeDraggable(actor, params, target) { return new _Draggable(actor, params, target); } function GenericDragItemContainer() { this._init(); } GenericDragItemContainer.prototype = { _init: function() { this.actor = new Cinnamon.GenericContainer({ style_class: 'drag-item-container' }); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor._delegate = this; this.child = null; this._childScale = 1; this._childOpacity = 255; this.animatingOut = false; }, _allocate: function(actor, box, flags) { if (this.child == null) return; let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = this.child.get_preferred_size(); let [childScaleX, childScaleY] = this.child.get_scale(); let childWidth = Math.min(natChildWidth * childScaleX, availWidth); let childHeight = Math.min(natChildHeight * childScaleY, availHeight); let childBox = new Clutter.ActorBox(); childBox.x1 = (availWidth - childWidth) / 2; childBox.y1 = (availHeight - childHeight) / 2; childBox.x2 = childBox.x1 + childWidth; childBox.y2 = childBox.y1 + childHeight; this.child.allocate(childBox, flags); }, _getPreferredHeight: function(actor, forWidth, alloc) { alloc.min_size = 0; alloc.natural_size = 0; if (this.child == null) return; let [minHeight, natHeight] = this.child.get_preferred_height(forWidth); alloc.min_size += minHeight * this.child.scale_y; alloc.natural_size += natHeight * this.child.scale_y; }, _getPreferredWidth: function(actor, forHeight, alloc) { alloc.min_size = 0; alloc.natural_size = 0; if (this.child == null) return; let [minWidth, natWidth] = this.child.get_preferred_width(forHeight); alloc.min_size = minWidth * this.child.scale_y; alloc.natural_size = natWidth * this.child.scale_y; }, setChild: function(actor) { if (this.child == actor) return; this.actor.destroy_children(); this.child = actor; this.actor.add_actor(this.child); }, animateIn: function() { if (this.child == null) return; this.childScale = 0; this.childOpacity = 0; Tweener.addTween(this, { childScale: 1.0, childOpacity: 255, time: DND_ANIMATION_TIME, transition: 'easeOutQuad' }); }, animateOutAndDestroy: function() { if (this.child == null) { this.actor.destroy(); return; } this.animatingOut = true; this.childScale = 1.0; Tweener.addTween(this, { childScale: 0.0, childOpacity: 0, time: DND_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.actor.destroy(); }) }); }, set childScale(scale) { this._childScale = scale; if (this.child == null) return; this.child.set_scale_with_gravity(scale, scale, Clutter.Gravity.CENTER); this.actor.queue_relayout(); }, get childScale() { return this._childScale; }, set childOpacity(opacity) { this._childOpacity = opacity; if (this.child == null) return; this.child.set_opacity(opacity); this.actor.queue_redraw(); }, get childOpacity() { return this._childOpacity; } }; function GenericDragPlaceholderItem() { this._init(); } GenericDragPlaceholderItem.prototype = { __proto__: GenericDragItemContainer.prototype, _init: function() { GenericDragItemContainer.prototype._init.call(this); this.setChild(new St.Bin({ style_class: 'drag-placeholder' })); } }; function LauncherDraggable() { this._init(); } LauncherDraggable.prototype = { _init: function() { this.launchersBox = null; }, getId: function() { /* Implemented by draggable launchers */ global.logError("Could not complete drag-and-drop. Launcher does not implement LauncherDraggable"); } }; Cinnamon-2.8.6/js/ui/radioButton.js0000664000175000017500000001417412625666430016117 0ustar fabiofabioconst Clutter = imports.gi.Clutter; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Signals = imports.signals; const Lang = imports.lang; function RadioButtonContainer() { this._init(); } RadioButtonContainer.prototype = { _init: function() { this.actor = new Cinnamon.GenericContainer({ y_align: St.Align.MIDDLE }); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('style-changed', Lang.bind(this, function() { let node = this.actor.get_theme_node(); this._spacing = node.get_length('spacing'); })); this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH; this._box = new St.Bin(); this.actor.add_actor(this._box); this.label = new St.Label(); this.label.clutter_text.set_line_wrap(false); this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE); this.actor.add_actor(this.label, { y_fill: true, y_align: St.Align.END }); this._spacing = 0; }, _getPreferredWidth: function(actor, forHeight, alloc) { let [minWidth, natWidth] = this._box.get_preferred_width(forHeight); alloc.min_size = minWidth + this._spacing; alloc.natural_size = natWidth + this._spacing; }, _getPreferredHeight: function(actor, forWidth, alloc) { let [minBoxHeight, natBoxHeight] = this._box.get_preferred_height(-1); let [minLabelHeight, natLabelHeight] = this.label.get_preferred_height(-1); alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight); alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight); }, _allocate: function(actor, box, flags) { let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let childBox = new Clutter.ActorBox(); let [minBoxWidth, natBoxWidth] = this._box.get_preferred_width(-1); let [minBoxHeight, natBoxHeight] = this._box.get_preferred_height(-1); childBox.x1 = box.x1; childBox.x2 = box.x1 + natBoxWidth; childBox.y1 = box.y1; childBox.y2 = box.y1 + natBoxHeight; this._box.allocate(childBox, flags); childBox.x1 = box.x1 + natBoxWidth + this._spacing; childBox.x2 = availWidth - childBox.x1; childBox.y1 = box.y1; childBox.y2 = box.y2; this.label.allocate(childBox, flags); } }; function RadioBox(state) { this._init(state); } RadioBox.prototype = { _init: function(state) { this.actor = new St.Button({ style_class: 'radiobutton', button_mask: St.ButtonMask.ONE, toggle_mode: true, can_focus: true, x_fill: true, y_fill: true, y_align: St.Align.MIDDLE }); this.actor._delegate = this; this.actor.checked = state; // FIXME: The current size is big and the container only is useful, // because the current theme. Can be fixed the theme also? this.actor.style = 'width: 12px;'; this._container = new St.Bin(); this.actor.set_child(this._container); }, setToggleState: function(state) { this.actor.checked = state; }, toggle: function() { this.setToggleState(!this.actor.checked); }, destroy: function() { this.actor.destroy(); } }; function RadioButton(label) { this._init(label); } RadioButton.prototype = { __proto__: RadioBox.prototype, _init: function(label) { RadioBox.prototype._init.call(this, false); this._container.destroy(); this._container = new RadioButtonContainer(); this.actor.set_child(this._container.actor); if (label) this.setLabel(label); }, setLabel: function(label) { this._container.label.set_text(label); }, getLabelActor: function() { return this._container.label; } }; function RadioButtonGroup() { this._init(); } RadioButtonGroup.prototype = { _init: function() { this.actor = new St.BoxLayout({ vertical: true, width: 250 }); this._buttons = []; this._activeId = null; }, addButton: function(buttonId, label) { this.radioButton = new RadioButton(label); this.radioButton.actor.connect("clicked", Lang.bind(this, function(actor) { this.buttonClicked(actor, buttonId); })); this._buttons.push({ id: buttonId, button: this.radioButton }); this.actor.add(this.radioButton.actor, { x_fill: true, y_fill: false, y_align: St.Align.MIDDLE }); }, radioChanged: function(actor) { }, buttonClicked: function(actor, buttonId) { for (var i in this._buttons) { if (buttonId != this._buttons[i]['id'] && this._buttons[i]['button'].actor.checked) { this._buttons[i]['button'].actor.checked = false; } else if (buttonId == this._buttons[i]['id'] && !this._buttons[i]['button'].actor.checked) { this._buttons[i]['button'].actor.checked = true; } } // Only trigger real changes to radio selection. if (buttonId != this._activeId) { this._activeId = buttonId; this.emit('radio-changed', this._activeId); } }, setActive: function(buttonId) { for (var i in this._buttons) { this._buttons[i]['button'].actor.checked = (buttonId == this._buttons[i]['id']); } if (this._activeId != buttonId) { this._activeId = buttonId; this.emit('radio-changed', this._activeId); } }, getActive: function() { return this._activeId; } } Signals.addSignalMethods(RadioButtonGroup.prototype); Cinnamon-2.8.6/js/ui/dbusMenu.js0000664000175000017500000005556212625666430015415 0ustar fabiofabio// Copyright (C) 2011 Giovanni Campagna // Copyright (C) 2013-2014 Jonas Kümmerlin // // 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 2 // 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, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const Lang = imports.lang; const Signals = imports.signals; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const GdkPixbuf = imports.gi.GdkPixbuf; const PopupMenu = imports.ui.popupMenu; // We list all the properties we know and use here, so we won' have to deal with unexpected type mismatches const MandatedTypes = { 'visible' : GLib.VariantType.new("b"), 'enabled' : GLib.VariantType.new("b"), 'label' : GLib.VariantType.new("s"), 'type' : GLib.VariantType.new("s"), 'children-display' : GLib.VariantType.new("s"), 'icon-name' : GLib.VariantType.new("s"), 'icon-data' : GLib.VariantType.new("ay"), 'toggle-type' : GLib.VariantType.new("s"), 'toggle-state' : GLib.VariantType.new("i"), 'action' : GLib.VariantType.new("s"), 'accel' : GLib.VariantType.new("s"), }; const DefaultValues = { 'visible' : GLib.Variant.new_boolean(true), 'enabled' : GLib.Variant.new_boolean(true), 'label' : GLib.Variant.new_string(""), 'type' : GLib.Variant.new_string(""), 'action' : GLib.Variant.new_string(""), 'accel' : GLib.Variant.new_string("") // Elements not in here must return null }; const DBusMenuIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const BusClientProxy = Gio.DBusProxy.makeProxyWrapper(DBusMenuIface); /** * #PropertyStore: * @short_description: A Class to saves menu property values and handles type checking and defaults. */ function PropertyStore() { this._init.apply(this, arguments); } PropertyStore.prototype = { _init: function(initProperties) { this._props = {}; if (initProperties) { for (let i in initProperties) { this.set(i, initProperties[i]); } } }, set: function(name, value) { if (name in MandatedTypes && value && value.is_of_type && !value.is_of_type(MandatedTypes[name])) global.logWarning("Cannot set property "+name+": type mismatch!"); else if (value) this._props[name] = value; else delete this._props[name]; }, get: function(name) { if (name in this._props) return this._props[name]; else if (name in DefaultValues) return DefaultValues[name]; else return null; }, // FIXME: This apparently be complex, and the same are handled better // in the original extension. The problem is the icons, the we can not // compare as a simple variant because is not. There are another way? compareNew: function(name, newValue) { if (!(name in MandatedTypes)) return true; if (name in MandatedTypes && newValue && newValue.is_of_type && !newValue.is_of_type(MandatedTypes[name])) return false; let oldValue = this.get(name); if (oldValue == newValue) return false; if (newValue && !oldValue || oldValue && !newValue) return true; let isOldContainer = oldValue.is_container(); let isNewContainer = newValue.is_container(); if ((!isOldContainer) && (!isNewContainer)) { return (oldValue.compare(newValue) != 0); } else if (isOldContainer != isNewContainer) return true; let oldArray = oldValue.deep_unpack(); let newArray = newValue.deep_unpack(); if (oldArray.length != newArray.length) return true; for (let child in oldArray) { if (!(child in newArray) || (oldArray[child] != newArray[child])) return true; } return false; }, getString: function(propName) { let prop = this.getVariant(propName); return prop ? prop.get_string()[0] : null; }, getVariant: function(propName) { return this.get(propName); }, getBool: function(propName) { let prop = this.getVariant(propName); return prop ? prop.get_boolean() : false; }, getInt: function(propName) { let prop = this.getVariant(propName); return prop ? prop.get_int32() : 0; }, setVariant: function(prop, value) { if (this.compareNew(prop, value)) { this.set(prop, value); return true; } return false; } }; /** * #DbusMenuItem: * @short_description: Represents a single Dbus menu item. */ function DbusMenuItem() { this._init.apply(this, arguments); } DbusMenuItem.prototype = { __proto__: PopupMenu.PopupMenuAbstractItem.prototype, // Will steal the properties object _init: function(id, childrenIds, properties, client) { PopupMenu.PopupMenuAbstractItem.prototype._init.call(this, id, childrenIds, this._createParameters(properties, client)); }, updatePropertiesAsVariant: function(properties) { let propStore = new PropertyStore(properties); if ("label" in properties) this.setLabel(propStore.getString("label").replace(/_([^_])/, "$1")); if ("accel" in properties) this.setAccel(this._getAccel(propStore.getString("accel"))); if ("enabled" in properties) this.setSensitive(propStore.getBool("enabled")); if ("visible" in properties) this.setVisible(propStore.getBool("visible")); if ("toggle-type" in properties) this.setToggleType(propStore.getString("toggle-type")); if ("toggle-state" in properties) this.setToggleState(propStore.getInt("toggle-state")); if ("icon-name" in properties) this.setIconName(propStore.getString("icon-name")); if ("icon-data" in properties) this.setGdkIcon(this._getGdkIcon(propStore.getVariant("icon-data"))); if (("children-display" in properties)||("type" in properties)) this.setFactoryType(this._getFactoryType(propStore.getString('children-display'), propStore.getString('type'))); if ("action" in properties) this.setAction(propStore.getString("action")); if ("param-type" in properties) this.setParamType(propStore.getVariant("param-type")); }, getItemById: function(id) { return this._client.getItem(id); }, handleEvent: function(event, params) { if (event in PopupMenu.FactoryEventTypes) { this._client.sendEvent(this._id, event, params, 0); } }, // FIXME We really don't need the PropertyStore object, and some private function // could make a clean on our "unsave" variants. _createParameters: function(properties, client) { this._client = client; let propStore = new PropertyStore(properties); let params = {}; if ("label" in properties) params.label = propStore.getString("label").replace(/_([^_])/, "$1"); if ("accel" in properties) params.accel = this._getAccel(propStore.getString("accel")); if ("enabled" in properties) params.sensitive = propStore.getBool("enabled"); if ("visible" in properties) params.visible = propStore.getBool("visible"); if ("toggle-type" in properties) params.toggleType = propStore.getString("toggle-type"); if ("toggle-state" in properties) params.toggleState = propStore.getInt("toggle-state"); if ("icon-name" in properties) params.iconName = propStore.getString("icon-name"); if ("icon-data" in properties) params.iconData = this._getGdkIcon(propStore.getVariant("icon-data")); if (("children-display" in properties)||("type" in properties)) params.type = this._getFactoryType(propStore.getString('children-display'), propStore.getString('type')) if ("action" in properties) params.action = propStore.getString("action"); if ("param-type" in properties) params.paramType = propStore.getVariant("param-type"); return params; }, _getAccel: function(accel_name) { if (accel_name) { [key, mods] = Gtk.accelerator_parse(accel_name); return Gtk.accelerator_get_label(key, mods); } return null; }, _getFactoryType: function(child_display, child_type) { if ((child_display) || (child_type)) { if ((child_display == "rootmenu")||(this._id == this._client.getRootId())) return PopupMenu.FactoryClassTypes.RootMenuClass; if (child_display == "submenu") return PopupMenu.FactoryClassTypes.SubMenuMenuItemClass; else if (child_display == "section") return PopupMenu.FactoryClassTypes.MenuSectionMenuItemClass; else if (child_type == "separator") return PopupMenu.FactoryClassTypes.SeparatorMenuItemClass; return PopupMenu.FactoryClassTypes.MenuItemClass; } return null; }, _getGdkIcon: function(value) { try { if (value) { let data = value.get_data_as_bytes() let stream = Gio.MemoryInputStream.new_from_bytes(data); return GdkPixbuf.Pixbuf.new_from_stream(stream, null); } } catch(e) { global.log("Error loading icon: " + e.message); } return null; }, destroy: function() { if (this._client) { PopupMenu.PopupMenuAbstractItem.prototype.destroy.call(this); this._client = null; } } }; /** * #DBusClient: * @short_description: The client does the heavy lifting of actually reading layouts and distributing events. */ function DBusClient() { this._init.apply(this, arguments); } DBusClient.prototype = { _init: function(busName, busPath) { this._busName = busName; this._busPath = busPath; this._idLayoutUpdate = 0; this._shellMenu = null; // Will be set to true if a layout update is requested while one is already in progress // then the handler that completes the layout update will request another update this._flagLayoutUpdateRequired = false; this._flagLayoutUpdateInProgress = false; // Property requests are queued this._propertiesRequestedFor = []; // ids let initId = this.getRootId(); this._items = {}; this._items[initId] = new DbusMenuItem(initId, [], { 'children-display': GLib.Variant.new_string('rootmenu'), 'visible': GLib.Variant.new_boolean(false) }, this); this._startMainProxy(); }, getShellMenu: function() { return this._shellMenu; }, setShellMenu: function(shellMenu) { this._shellMenu = shellMenu; }, getRoot: function() { if (this._items) return this._items[this.getRootId()]; return null; }, getRootId: function() { return 0; }, getItem: function(id) { if ((this._items)&&(id in this._items)) return this._items[id]; global.logWarning("trying to retrieve item for non-existing id "+id+" !?"); return null; }, // We don't need to cache and burst-send that since it will not happen that frequently sendAboutToShow: function(id) { if (this._proxyMenu) { this._proxyMenu.AboutToShowRemote(id, Lang.bind(this, function(result, error) { if (error) global.logWarning("while calling AboutToShow: " + error); else if (result && result[0]) this._requestLayoutUpdate(); })); } }, sendEvent: function(id, event, params, timestamp) { if (this._proxyMenu) { if (!params) params = GLib.Variant.new_int32(0); this._proxyMenu.EventRemote(id, event, params, timestamp, function(result, error) {}); // We don't care if (event == PopupMenu.FactoryEventTypes.opened) this.sendAboutToShow(id); } }, _startMainProxy: function() { this._proxyMenu = new BusClientProxy(Gio.DBus.session, this._busName, this._busPath, Lang.bind(this, this._clientReady)); }, _requestLayoutUpdate: function() { if (this._idLayoutUpdate != 0) this._idLayoutUpdate = 0; if (this._flagLayoutUpdateInProgress) this._flagLayoutUpdateRequired = true; else this._beginLayoutUpdate(); }, _requestProperties: function(id) { // If we don't have any requests queued, we'll need to add one if (this._propertiesRequestedFor.length < 1) GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, Lang.bind(this, this._beginRequestProperties)); if (this._propertiesRequestedFor.filter(function(e) { return e === id; }).length == 0) this._propertiesRequestedFor.push(id); }, _beginRequestProperties: function() { if (this._proxyMenu) { this._proxyMenu.GetGroupPropertiesRemote(this._propertiesRequestedFor, [], Lang.bind(this, this._endRequestProperties)); this._propertiesRequestedFor = []; } return false; }, _endRequestProperties: function(result, error) { if (error) { global.logWarning("Could not retrieve properties: " + error); } else if (this._items) { // For some funny reason, the result array is hidden in an array result[0].forEach(function([id, properties]) { if (!(id in this._items)) return; this._items[id].updatePropertiesAsVariant(properties); }, this); } }, // Traverses the list of cached menu items and removes everyone that is not in the list // so we don't keep alive unused items _gcItems: function() { if (this._items) { let tag = new Date().getTime(); let toTraverse = [ this.getRootId() ]; while (toTraverse.length > 0) { let item = this.getItem(toTraverse.shift()); item._dbusClientGcTag = tag; Array.prototype.push.apply(toTraverse, item.getChildrenIds()); } for (let id in this._items) if (this._items[id]._dbusClientGcTag != tag) delete this._items[id]; } }, // The original implementation will only request partial layouts if somehow possible // we try to save us from multiple kinds of race conditions by always requesting a full layout _beginLayoutUpdate: function() { // We only read the type property, because if the type changes after reading all properties, // the view would have to replace the item completely which we try to avoid if (this._proxyMenu) { this._proxyMenu.GetLayoutRemote(0, -1, [ 'type', 'children-display' ], Lang.bind(this, this._endLayoutUpdate)); this._flagLayoutUpdateInProgress = true; } this._flagLayoutUpdateRequired = false; }, _endLayoutUpdate: function(result, error) { if (error) { global.logWarning("While reading menu layout: " + error); return; } let [ revision, root ] = result; this._doLayoutUpdate(root); this._gcItems(); if (this._flagLayoutUpdateRequired) this._beginLayoutUpdate(); else this._flagLayoutUpdateInProgress = false; }, _doLayoutUpdate: function(item) { let [ id, properties, children ] = item; if (this._items) { let childrenUnpacked = children.map(function(child) { return child.deep_unpack(); }); let childrenIds = childrenUnpacked.map(function(child) { return child[0]; }); // Make sure all our children exist childrenUnpacked.forEach(this._doLayoutUpdate, this); // Make sure we exist if (id in this._items) { // We do, update our properties if necessary this._items[id].updatePropertiesAsVariant(properties); // Make sure our children are all at the right place, and exist let oldChildrenIds = this._items[id].getChildrenIds(); for (let i = 0; i < childrenIds.length; ++i) { // Try to recycle an old child let oldChild = -1; for (let j = 0; j < oldChildrenIds.length; ++j) { if (oldChildrenIds[j] == childrenIds[i]) { oldChild = oldChildrenIds.splice(j, 1)[0]; break; } } if (oldChild < 0) { // No old child found, so create a new one! this._items[id].addChild(i, childrenIds[i]); } else { // Old child found, reuse it! this._items[id].moveChild(childrenIds[i], i); } } // Remove any old children that weren't reused oldChildrenIds.forEach(function(child_id) { this._items[id].removeChild(child_id); }, this); } else { // We don't, so let's create us this._items[id] = new DbusMenuItem(id, childrenIds, properties, this); this._requestProperties(id); } } return id; }, _clientReady: function(result, error) { if (error) { //FIXME: show message to the user? global.logWarning("Could not initialize menu proxy: " + error); return; } this._requestLayoutUpdate(); // Listen for updated layouts and properties if (this._proxyMenu) { this._proxyMenu.connectSignal("LayoutUpdated", Lang.bind(this, this._onLayoutUpdated)); this._proxyMenu.connectSignal("ItemsPropertiesUpdated", Lang.bind(this, this._onPropertiesUpdated)); } }, _onLayoutUpdated: function(proxy, sender, items) { if (this._idLayoutUpdate == 0) { this._idLayoutUpdate = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, Lang.bind(this, this._requestLayoutUpdate)); } }, _onPropertiesUpdated: function(proxy, name, [changed, removed]) { if (this._items) { changed.forEach(function([id, properties]) { if (!(id in this._items)) return; this._items[id].updatePropertiesAsVariant(properties); }, this); removed.forEach(function([id, propNames]) { if (!(id in this._items)) return; let properties = {}; propNames.forEach(function(propName) { properties[propName] = null; }, this); this._items[id].updatePropertiesAsVariant(properties); }, this); } }, destroy: function() { if (this._proxyMenu) { Signals._disconnectAll.apply(this._proxyMenu); this._proxyMenu = null; let root = this.getRoot(); root.destroy(); this._items = null; } } }; Cinnamon-2.8.6/js/ui/appletManager.js0000664000175000017500000004702612625666430016407 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Main = imports.ui.main; const Applet = imports.ui.applet; const Extension = imports.ui.extension; const ModalDialog = imports.ui.modalDialog; // Maps uuid -> metadata object var appletMeta; // Maps uuid -> importer object (applet directory tree) var applets; // Maps applet_id -> applet objects const appletObj = []; var appletsLoaded = false; // An applet can assume a role // Instead of hardcoding looking for a particular applet, // We let applets announce that they can fill a particular // role, using the 'role' metadata entry. // For now, just notifications, but could be expanded. // question - should multiple applets be able to fill // the same role? const Roles = { NOTIFICATIONS: 'notifications', PANEL_LAUNCHER: 'panellauncher' } let enabledAppletDefinitions; let clipboard = []; function init() { applets = Extension.Type.APPLET.maps.importObjects; appletMeta = Extension.Type.APPLET.maps.meta; appletsLoaded = false; // Load all applet extensions, the applets themselves will be added in finishExtensionLoad enabledAppletDefinitions = getEnabledAppletDefinitions(); for (let uuid in enabledAppletDefinitions.uuidMap) { Extension.loadExtension(uuid, Extension.Type.APPLET); } appletsLoaded = true; global.settings.connect('changed::enabled-applets', onEnabledAppletsChanged); } // Callback for extension.js function finishExtensionLoad(extension) { // Add all applet instances for this extension let definitions = enabledAppletDefinitions.uuidMap[extension.uuid]; if (definitions) { for(let i=0; i list of applet definitions uuidMap: {}, // maps applet_id -> single applet definition idMap: [] }; // Upgrade settings if required checkForUpgrade(result.raw); // Parse all definitions for (let i=0; i 4) { let panelId = parseInt(elements[0].slice(5)); let panel = Main.panelManager.panels[panelId]; let orientation; let order; try { order = parseInt(elements[2]); } catch(e) { order = 0; } // Panel might not exist. Still keep definition for future use. let location; if (panel) { orientation = panel.bottomPosition ? St.Side.BOTTOM : St.Side.TOP; location = getLocation(panel, elements[1]); } return { panel: panel, panelId: panelId, orientation: orientation, location: location, location_label: elements[1], center: elements[1] == "center", order: order, uuid: elements[3], applet_id: elements[4] }; } global.logError("Bad applet definition: " + definition); return null; } function checkForUpgrade(newEnabledApplets) { // upgrade if old version let nextAppletId = global.settings.get_int("next-applet-id"); for (let i=0; i (x._applet instanceof Applet.Applet) && (appletDefinition.order < x._applet._order)); if (before) location.insert_before(applet.actor, before); else location.add_actor(applet.actor); applet._panelLocation = location; if(!extension._loadedDefinitions) { extension._loadedDefinitions = {}; } extension._loadedDefinitions[appletDefinition.applet_id] = appletDefinition; applet.on_applet_added_to_panel_internal(appletsLoaded); return true; } catch(e) { extension.unlockRole(); extension.logError('Failed to load applet: ' + appletDefinition.uuid + "/" + appletDefinition.applet_id, e); return false; } } function get_role_provider(role) { if (Extension.Type.APPLET.roles[role]) { return Extension.Type.APPLET.roles[role].roleProvider; } return null; } function get_role_provider_exists(role) { return get_role_provider(role) != null; } function createApplet(extension, appletDefinition) { if (!appletDefinition.panel) return null; let applet_id = appletDefinition.applet_id; let orientation = appletDefinition.orientation; let panel_height = appletDefinition.panel.actor.get_height(); if (appletObj[applet_id] != undefined) { global.log(applet_id + ' applet already loaded'); if (appletObj[applet_id]._panelHeight != panel_height) { appletObj[applet_id].setPanelHeight(panel_height); } appletObj[applet_id].setOrientation(orientation); return appletObj[applet_id]; } let applet; try { applet = extension.module.main(extension.meta, orientation, panel_height, applet_id); } catch (e) { extension.logError('Failed to evaluate \'main\' function on applet: ' + appletDefinition.uuid + "/" + appletDefinition.applet_id, e); return null; } appletObj[applet_id] = applet; applet._uuid = extension.uuid; applet._meta = extension.meta; applet.instance_id = applet_id; applet.panel = appletDefinition.panel; applet.finalizeContextMenu(); return(applet); } function _removeAppletFromPanel(uuid, applet_id) { let enabledApplets = enabledAppletDefinitions.raw; for (let i=0; i x && (x._uuid == uuid || x.instance_id == instanceId)); } /** * loadAppletsOnPanel: * @panel (Panel.Panel): The panel * * Loads all applets on the panel if not loaded */ function loadAppletsOnPanel(panel) { let orientation = panel.bottomPosition ? St.Side.BOTTOM : St.Side.TOP; let definition; for (let applet_id in enabledAppletDefinitions.idMap){ definition = enabledAppletDefinitions.idMap[applet_id]; if(definition.panelId == panel.panelId) { definition.panel = panel; definition.location = getLocation(panel, definition.location_label); definition.orientation = orientation; let extension = Extension.Type.APPLET.maps.objects[definition.uuid]; if(extension) { addAppletToPanels(extension, definition); } } } } /** * updateAppletsOnPanel: * @panel (Panel.Panel): The panel * * Updates the definition, orientation and height of applets on the panel */ function updateAppletsOnPanel (panel) { let height = panel.actor.get_height(); let orientation = panel.bottomPosition ? St.Side.BOTTOM : St.Side.TOP; let definition; for (let applet_id in enabledAppletDefinitions.idMap){ definition = enabledAppletDefinitions.idMap[applet_id]; if(definition.panel == panel) { definition.location = getLocation(panel, definition.location_label); definition.orientation = orientation; if (appletObj[applet_id]) { try { appletObj[applet_id].setPanelHeight(height); appletObj[applet_id].setOrientation(orientation); } catch (e) { global.logError("Error during setPanelHeight() and setOrientation() call on applet: " + definition.uuid + "/" + applet_id, e); } } } } } /** * unloadAppletsOnPanel: * @panel (Panel.Panel): The panel * * Unloads all applets on the panel */ function unloadAppletsOnPanel (panel) { enabledAppletDefinitions.idMap .filter(x => x.panel == panel) .forEach(removeAppletFromPanels); } function copyAppletConfiguration(panelId) { clipboard = enabledAppletDefinitions.idMap.filter(x => x.panelId == panelId); } function clearAppletConfiguration(panelId) { let raw = global.settings.get_strv("enabled-applets"); raw = raw.filter(x => x.split(":")[0].slice(5) != panelId); global.settings.set_strv("enabled-applets", raw); } function pasteAppletConfiguration(panelId) { clearAppletConfiguration(panelId); let raw = global.settings.get_strv("enabled-applets"); let skipped = false; let len = clipboard.length; let nextId = global.settings.get_int("next-applet-id"); for (let i = 0; i < len; i++) { let max = Extension.get_max_instances(clipboard[i].uuid, Extension.Type.APPLET); if (max == -1) { raw.push("panel" + panelId + ":" + clipboard[i].location_label + ":" + clipboard[i].order + ":" + clipboard[i].uuid + ":" + nextId); nextId ++; continue; } let curr = enabledAppletDefinitions.uuidMap[clipboard[i].uuid]; let count = curr.length; if (count >= max) { // If we have more applets that allowed, we see if we any of them are removed above let i = count; while (i--) { // Do a reverse loop because the value of count will change if (curr[i].panelId == panelId) count --; } } } clipboard.forEach(function(x) { let uuid = x.uuid let max = Extension.get_max_instances(uuid); if (max == -1 || raw.filter(a => a.split(":")[2] == uuid).length < max) { raw.push("panel" + panelId + ":" + x.location_label + ":" + x.order + ":" + uuid + ":" + nextId); nextId ++; } else { skipped = true; } }); global.settings.set_int("next-applet-id", nextId); global.settings.set_strv("enabled-applets", raw); if (skipped) { let dialog = new ModalDialog.NotifyDialog(_("Certain applets do not allow multiple instances and were not copied") + "\n\n"); dialog.open(); } } function getRunningInstancesForUuid(uuid) { return appletObj.filter(x => x && x._uuid == uuid); } function callAppletInstancesChanged(uuid) { for (let applet_id in enabledAppletDefinitions.idMap) { if (appletObj[applet_id]) { if (uuid == appletObj[applet_id]._uuid) { appletObj[applet_id].on_applet_instances_changed(); } } } } function getLocation(panel, location) { return {"center": panel._centerBox, "right" : panel._rightBox, "left" : panel._leftBox}[location]; } Cinnamon-2.8.6/js/ui/expo.js0000664000175000017500000004465612625666430014610 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Meta = imports.gi.Meta; const Signals = imports.signals; const Lang = imports.lang; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const DND = imports.ui.dnd; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const ExpoThumbnail = imports.ui.expoThumbnail; // Time for initial animation going into Overview mode const ANIMATION_TIME = 0.3; function Expo() { this._init.apply(this, arguments); } Expo.prototype = { _init : function() { Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout)); }, beforeShow: function() { // The main BackgroundActor is inside global.window_group which is // hidden when displaying the overview, so we create a new // one. Instances of this class share a single CoglTexture behind the // scenes which allows us to show the background with different // rendering options without duplicating the texture data. this._background = Meta.BackgroundActor.new_for_screen(global.screen); this._background.hide(); global.overlay_group.add_actor(this._background); this._spacing = 0; this._group = new St.Group({ name: 'expo', reactive: true }); this._group._delegate = this; this._group.connect('style-changed', Lang.bind(this, function() { let node = this._group.get_theme_node(); let spacing = node.get_length('spacing'); if (spacing != this._spacing) { this._spacing = spacing; this._relayout(); } })); this.visible = false; // animating to overview, in overview, animating out this._shown = false; // show() and not hide() this._modal = false; // have a modal grab this.animationInProgress = false; this._hideInProgress = false; // During transitions, we raise this to the top to avoid having the overview // area be reactive; it causes too many issues such as double clicks on // Dash elements, or mouseover handlers in the workspaces. this._gradient = new St.Button({reactive: false}); this._gradient.set_style_class_name("expo-background"); this._group.add_actor(this._gradient); this._coverPane = new Clutter.Rectangle({ opacity: 0, reactive: true }); this._group.add_actor(this._coverPane); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._addWorkspaceButton = new St.Button({style_class: 'workspace-add-button'}); this._group.add_actor(this._addWorkspaceButton); this._addWorkspaceButton.connect('clicked', Lang.bind(this, function () { Main._addWorkspace();})); this._addWorkspaceButton.handleDragOver = function(source, actor, x, y, time) { return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; }; this._addWorkspaceButton.acceptDrop = function(source, actor, x, y, time) { if (source.metaWindow) { let draggable = source._draggable; actor.get_parent().remove_actor(actor); draggable._dragOrigParent.add_actor(actor); actor.opacity = draggable._dragOrigOpacity; Main.moveWindowToNewWorkspace(source.metaWindow); } return true; }; this._addWorkspaceButton._delegate = this._addWorkspaceButton; this._windowCloseArea = new St.Button({style_class: 'window-close-area'}); this._windowCloseArea.handleDragOver = function(source, actor, x, y, time) { return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; }; this._windowCloseArea.acceptDrop = function(source, actor, x, y, time) { if (source.metaWindow) { let draggable = source._draggable; actor.get_parent().remove_actor(actor); draggable._dragOrigParent.add_actor(actor); actor.opacity = draggable._dragOrigOpacity; source.metaWindow.delete(global.get_current_time()); } return true; }; this._windowCloseArea._delegate = this._windowCloseArea; this._group.add_actor(this._windowCloseArea); this._group.hide(); global.overlay_group.add_actor(this._group); this._gradient.hide(); this._coverPane.hide(); this._addWorkspaceButton.hide(); this._windowCloseArea.hide(); let ctrlAltMask = Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.MOD1_MASK; this._group.connect('key-press-event', Lang.bind(this, function(actor, event) { if (this._shown) { if (this._expo.handleKeyPressEvent(actor, event)) { return true; } let symbol = event.get_key_symbol(); if (symbol === Clutter.plus || symbol === Clutter.Insert) { this._workspaceOperationPending = true; } let modifiers = Cinnamon.get_event_state(event); if ((symbol === Clutter.Delete && (modifiers & ctrlAltMask) !== ctrlAltMask) || symbol === Clutter.w && modifiers & Clutter.ModifierType.CONTROL_MASK) { this._workspaceOperationPending = true; } if (symbol === Clutter.Escape) { if (!this._workspaceOperationPending) { this.hide(); } this._workspaceOperationPending = false; return true; } } return false; })); this._group.connect('key-release-event', Lang.bind(this, function(actor, event) { if (this._shown) { let symbol = event.get_key_symbol(); if (symbol === Clutter.plus || symbol === Clutter.Insert) { if (this._workspaceOperationPending) { this._workspaceOperationPending = false; Main._addWorkspace(); } return true; } let modifiers = Cinnamon.get_event_state(event); if ((symbol === Clutter.Delete && (modifiers & ctrlAltMask) !== ctrlAltMask) || symbol === Clutter.w && modifiers & Clutter.ModifierType.CONTROL_MASK) { if (this._workspaceOperationPending) { this._workspaceOperationPending = false; this._expo.removeSelectedWorkspace(); } return true; } } return false; })); this._expo = new ExpoThumbnail.ExpoThumbnailsBox(); this._group.add_actor(this._expo.actor); this._relayout(); }, init: function() { }, _relayout: function () { if (!this._expo) { // This function can be called as a response to the monitors-changed event, // when we're not showing. return; } // To avoid updating the position and size of the workspaces // we just hide the overview. The positions will be updated // when it is next shown. this.hide(); let primary = Main.layoutManager.primaryMonitor; let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); let contentY = 0; let contentHeight = primary.height; this._group.set_position(primary.x, primary.y); this._group.set_size(primary.width, primary.height); this._gradient.set_position(0, 0); this._gradient.set_size(primary.width, primary.height); this._coverPane.set_position(0, 0); this._coverPane.set_size(primary.width, contentHeight); let viewWidth = primary.width - this._spacing; let viewHeight = contentHeight - 2 * this._spacing; let viewY = contentY + this._spacing; let viewX = rtl ? 0 : this._spacing; let node = this._addWorkspaceButton.get_theme_node(); let buttonWidth = node.get_length('width'); let buttonHeight = node.get_length('height'); node = this._windowCloseArea.get_theme_node(); this._windowCloseArea.height = node.get_length('height'); this._windowCloseArea.width = node.get_length('width'); this._expo.actor.set_position(0, 0); this._expo.actor.set_size((primary.width - buttonWidth), primary.height); let buttonY = (primary.height - buttonHeight) / 2; this._addWorkspaceButton.set_position((primary.width - buttonWidth), buttonY); this._addWorkspaceButton.set_size(buttonWidth, buttonHeight); if (this._addWorkspaceButton.get_theme_node().get_background_image() == null) this._addWorkspaceButton.set_style('background-image: url("/usr/share/cinnamon/theme/add-workspace.png");'); this._windowCloseArea.set_position((primary.width - this._windowCloseArea.width) / 2 , primary.height); this._windowCloseArea.set_size(this._windowCloseArea.width, this._windowCloseArea.height); this._windowCloseArea.raise_top(); }, _showCloseArea : function() { let primary = Main.layoutManager.primaryMonitor; this._windowCloseArea.show(); Tweener.addTween(this._windowCloseArea, { y: primary.height - this._windowCloseArea.height, time: ANIMATION_TIME, transition: 'easeOutQuad'}); }, _hideCloseArea : function() { let primary = Main.layoutManager.primaryMonitor; Tweener.addTween(this._windowCloseArea, { y: primary.height, time: ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this.hide}); }, //// Public methods //// // show: // // Animates the overview visible and grabs mouse and keyboard input show : function() { if (this._shown) return; this.beforeShow(); // Do this manually instead of using _syncInputMode, to handle failure if (!Main.pushModal(this._group)) return; this._modal = true; this._animateVisible(); this._shown = true; }, _animateVisible: function() { if (this.visible || this.animationInProgress) return; this.visible = true; this.animationInProgress = true; // All the the actors in the window group are completely obscured, // hiding the group holding them while the Overview is displayed greatly // increases performance of the Overview especially when there are many // windows visible. // // If we switched to displaying the actors in the Overview rather than // clones of them, this would obviously no longer be necessary. // // Disable unredirection while in the overview Meta.disable_unredirect_for_screen(global.screen); global.window_group.hide(); this._group.show(); this._background.show(); this._addWorkspaceButton.show(); this._expo.show(); this._expo.connect('drag-begin', Lang.bind(this, this._showCloseArea)); this._expo.connect('drag-end', Lang.bind(this, this._hideCloseArea)); let activeWorkspace = this._expo.lastActiveWorkspace; let activeWorkspaceActor = activeWorkspace.actor; // should not create new actors and work with them within an allocation cycle let clones = []; Main.layoutManager.monitors.forEach(function(monitor,index) { let clone = new Clutter.Clone({source: activeWorkspaceActor}); global.overlay_group.add_actor(clone); clone.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); clones.push(clone); }, this); //We need to allocate activeWorkspace before we begin its clone animation let allocateID = this._expo.connect('allocated', Lang.bind(this, function() { this._expo.disconnect(allocateID); Main.layoutManager.monitors.forEach(function(monitor,index) { let clone = clones[index]; Tweener.addTween(clone, { x: Main.layoutManager.primaryMonitor.x + activeWorkspaceActor.allocation.x1, y: Main.layoutManager.primaryMonitor.y + activeWorkspaceActor.allocation.y1, scale_x: activeWorkspaceActor.get_scale()[0] , scale_y: activeWorkspaceActor.get_scale()[1], time: ANIMATION_TIME, transition: 'easeOutQuad', onComplete: function() { global.overlay_group.remove_actor(clone); clone.destroy(); if (index == Main.layoutManager.monitors.length < 1) { this._showDone(); } }, onCompleteScope: this }); }, this); })); this._gradient.show(); Main.panelManager.disablePanels(); this._background.dim_factor = 1; Tweener.addTween(this._background, { dim_factor: 0.4, transition: 'easeOutQuad', time: ANIMATION_TIME}); this._coverPane.raise_top(); this._coverPane.show(); this.emit('showing'); }, // hide: // // Reverses the effect of show() hide: function(options) { if (!this._shown) return; this._animateNotVisible(options); this._shown = false; this._syncInputMode(); }, toggle: function() { if (this._shown) this.hide(); else this.show(); }, //// Private methods //// _syncInputMode: function() { // We delay input mode changes during animation so that when removing the // overview we don't have a problem with the release of a press/release // going to an application. if (this.animationInProgress) return; if (this._shown) { if (!this._modal) { if (Main.pushModal(this._group)) this._modal = true; else this.hide(); } } else { if (this._modal) { Main.popModal(this._group); this._modal = false; } else if (global.stage_input_mode == Cinnamon.StageInputMode.FULLSCREEN) global.stage_input_mode = Cinnamon.StageInputMode.NORMAL; } }, _animateNotVisible: function(options) { if (!this.visible || this.animationInProgress) return; let animationTime = ANIMATION_TIME; this.animationInProgress = true; this._hideInProgress = true; let activeWorkspace = this._expo.lastActiveWorkspace; if (!options || !options.toScale ) { Main.panelManager.enablePanels(); activeWorkspace.overviewModeOff(true, true); } let activeWorkspaceActor = activeWorkspace.actor; Main.layoutManager.monitors.forEach(function(monitor,index) { let cover = new Clutter.Group(); global.overlay_group.add_actor(cover); cover.set_position(0, 0); cover.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); let clone = new Clutter.Clone({source: activeWorkspaceActor}); cover.add_actor(clone); clone.set_position(Main.layoutManager.primaryMonitor.x + activeWorkspaceActor.allocation.x1, Main.layoutManager.primaryMonitor.y + activeWorkspaceActor.allocation.y1); clone.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); clone.set_scale(activeWorkspaceActor.get_scale()[0], activeWorkspaceActor.get_scale()[1]); Tweener.addTween(clone, { x: 0, y: 0, scale_x: 1, scale_y: 1, time: animationTime, transition: 'easeOutQuad', onCompleteScope: this, onComplete: function() { global.overlay_group.remove_actor(cover); cover.destroy(); if (index == Main.layoutManager.monitors.length < 1) { this._group.hide(); this._hideDone(); } } }); }, this); this.emit('hiding'); }, _showDone: function() { this.animationInProgress = false; this._coverPane.hide(); this.emit('shown'); // Handle any calls to hide* while we were showing if (!this._shown) this._animateNotVisible(); this._syncInputMode(); global.sync_pointer(); }, _hideDone: function() { // Re-enable unredirection Meta.enable_unredirect_for_screen(global.screen); global.window_group.show(); this._expo.hide(); this._expo = null; this._addWorkspaceButton.hide(); this._windowCloseArea.hide(); this._background.hide(); this._gradient.hide(); this.visible = false; this.animationInProgress = false; this._hideInProgress = false; this._coverPane.hide(); this.emit('hidden'); // Handle any calls to show* while we were hiding if (this._shown) this._animateVisible(); this._syncInputMode(); global.overlay_group.remove_actor(this._group); this._group.destroy(); global.overlay_group.remove_actor(this._background); this._background.destroy(); Main.layoutManager._chrome.updateRegions(); } }; Signals.addSignalMethods(Expo.prototype); Cinnamon-2.8.6/js/ui/lightbox.js0000664000175000017500000001576012625666430015447 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Meta = imports.gi.Meta; const St = imports.gi.St; const Params = imports.misc.params; const Tweener = imports.ui.tweener; /** * Lightbox: * @container: parent Clutter.Container * @params: (optional) additional parameters: * - inhibitEvents: whether to inhibit events for @container * - width: shade actor width * - height: shade actor height * - fadeTime: seconds used to fade in/out * * Lightbox creates a dark translucent "shade" actor to hide the * contents of @container, and allows you to specify particular actors * in @container to highlight by bringing them above the shade. It * tracks added and removed actors in @container while the lightboxing * is active, and ensures that all actors are returned to their * original stacking order when the lightboxing is removed. (However, * if actors are restacked by outside code while the lightboxing is * active, the lightbox may later revert them back to their original * order.) * * By default, the shade window will have the height and width of * @container and will track any changes in its size. You can override * this by passing an explicit width and height in @params. */ function Lightbox(container, params) { this._init(container, params); } Lightbox.prototype = { _init : function(container, params) { params = Params.parse(params, { inhibitEvents: false, width: null, height: null, fadeTime: null }); this._container = container; this._children = container.get_children(); this._fadeTime = params.fadeTime; this.actor = new St.Bin({ x: 0, y: 0, style_class: 'lightbox', reactive: params.inhibitEvents }); container.add_actor(this.actor); this.actor.raise_top(); this.actor.hide(); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); if (params.width && params.height) { this.actor.width = params.width; this.actor.height = params.height; this._allocationChangedSignalId = 0; } else { this.actor.width = container.width; this.actor.height = container.height; this._allocationChangedSignalId = container.connect('allocation-changed', Lang.bind(this, this._allocationChanged)); } this._actorAddedSignalId = container.connect('actor-added', Lang.bind(this, this._actorAdded)); this._actorRemovedSignalId = container.connect('actor-removed', Lang.bind(this, this._actorRemoved)); this._highlighted = null; }, _allocationChanged : function(container, box, flags) { Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this.actor.width = this.width; this.actor.height = this.height; return false; })); this.width = this._container.width; this.height = this._container.height; }, _actorAdded : function(container, newChild) { let children = this._container.get_children(); let myIndex = children.indexOf(this.actor); let newChildIndex = children.indexOf(newChild); if (newChildIndex > myIndex) { // The child was added above the shade (presumably it was // made the new top-most child). Move it below the shade, // and add it to this._children as the new topmost actor. newChild.lower(this.actor); this._children.push(newChild); } else if (newChildIndex == 0) { // Bottom of stack this._children.unshift(newChild); } else { // Somewhere else; insert it into the correct spot let prevChild = this._children.indexOf(children[newChildIndex - 1]); if (prevChild != -1) // paranoia this._children.splice(prevChild + 1, 0, newChild); } }, show: function() { if (this._fadeTime) { this.actor.opacity = 0; Tweener.addTween(this.actor, { opacity: 255, time: this._fadeTime, transition: 'easeOutQuad' }); } else { this.actor.opacity = 255; } this.actor.show(); }, hide: function() { if (this._fadeTime) { Tweener.addTween(this.actor, { opacity: 0, time: this._fadeTime, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.actor.hide(); }) }); } else { this.actor.hide(); } }, _actorRemoved : function(container, child) { let index = this._children.indexOf(child); if (index != -1) // paranoia this._children.splice(index, 1); if (child == this._highlighted) this._highlighted = null; }, /** * highlight: * @window: actor to highlight * * Highlights the indicated actor and unhighlights any other * currently-highlighted actor. With no arguments or a false/null * argument, all actors will be unhighlighted. */ highlight : function(window) { if (this._highlighted == window) return; // Walk this._children raising and lowering actors as needed. // Things get a little tricky if the to-be-raised and // to-be-lowered actors were originally adjacent, in which // case we may need to indicate some *other* actor as the new // sibling of the to-be-lowered one. let below = this.actor; for (let i = this._children.length - 1; i >= 0; i--) { if (this._children[i] == window) this._children[i].raise_top(); else if (this._children[i] == this._highlighted) this._children[i].lower(below); else below = this._children[i]; } this._highlighted = window; }, /** * destroy: * * Destroys the lightbox. */ destroy : function() { this.actor.destroy(); }, /** * _onDestroy: * * This is called when the lightbox' actor is destroyed, either * by destroying its container or by explicitly calling this.destroy(). */ _onDestroy: function() { if (this._allocationChangedSignalId != 0) this._container.disconnect(this._allocationChangedSignalId); this._container.disconnect(this._actorAddedSignalId); this._container.disconnect(this._actorRemovedSignalId); this.highlight(null); } }; Cinnamon-2.8.6/js/ui/magnifierDBus.js0000664000175000017500000003623712625666430016350 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const Main = imports.ui.main; const MAG_SERVICE_NAME = 'org.gnome.Magnifier'; const MAG_SERVICE_PATH = '/org/gnome/Magnifier'; const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion'; const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion'; // Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See: // http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml const MagnifierIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; // Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See: // http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml const ZoomRegionIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ /node'; // For making unique ZoomRegion DBus proxy object paths of the form: // '/org/gnome/Magnifier/ZoomRegion/zoomer0', // '/org/gnome/Magnifier/ZoomRegion/zoomer1', etc. let _zoomRegionInstanceCount = 0; function CinnamonMagnifier() { this._init(); } CinnamonMagnifier.prototype = { _init: function() { this._zoomers = {}; this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(MagnifierIface, this); this._dbusImpl.export(Gio.DBus.session, MAG_SERVICE_PATH); }, /** * setActive: * @activate: Boolean to activate or de-activate the magnifier. */ setActive: function(activate) { Main.magnifier.setActive(activate); }, /** * isActive: * @return Whether the magnifier is active (boolean). */ isActive: function() { return Main.magnifier.isActive(); }, /** * showCursor: * Show the system mouse pointer. */ showCursor: function() { Main.magnifier.showSystemCursor(); }, /** * hideCursor: * Hide the system mouse pointer. */ hideCursor: function() { Main.magnifier.hideSystemCursor(); }, /** * createZoomRegion: * Create a new ZoomRegion and return its object path. * @xMagFactor: The power to set horizontal magnification of the * ZoomRegion. A value of 1.0 means no magnification. A * value of 2.0 doubles the size. * @yMagFactor: The power to set the vertical magnification of the * ZoomRegion. * @roi Array of integers defining the region of the * screen/desktop to magnify. The array has the form * [left, top, right, bottom]. * @viewPort Array of integers, [left, top, right, bottom] that defines * the position of the ZoomRegion on screen. * * FIXME: The arguments here are redundant, since the width and height of * the ROI are determined by the viewport and magnification factors. * We ignore the passed in width and height. * * @return The newly created ZoomRegion. */ createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) { let ROI = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] }; let viewBox = { x: viewPort[0], y: viewPort[1], width: viewPort[2] - viewPort[0], height: viewPort[3] - viewPort[1] }; let realZoomRegion = Main.magnifier.createZoomRegion(xMagFactor, yMagFactor, ROI, viewBox); let objectPath = ZOOM_SERVICE_PATH + '/zoomer' + _zoomRegionInstanceCount; _zoomRegionInstanceCount++; let zoomRegionProxy = new CinnamonMagnifierZoomRegion(objectPath, realZoomRegion); let proxyAndZoomRegion = {}; proxyAndZoomRegion.proxy = zoomRegionProxy; proxyAndZoomRegion.zoomRegion = realZoomRegion; this._zoomers[objectPath] = proxyAndZoomRegion; return objectPath; }, /** * addZoomRegion: * Append the given ZoomRegion to the magnifier's list of ZoomRegions. * @zoomerObjectPath: The object path for the zoom region proxy. */ addZoomRegion: function(zoomerObjectPath) { let proxyAndZoomRegion = this._zoomers[zoomerObjectPath]; if (proxyAndZoomRegion && proxyAndZoomRegion.zoomRegion) { Main.magnifier.addZoomRegion(proxyAndZoomRegion.zoomRegion); return true; } else return false; }, /** * getZoomRegions: * Return a list of ZoomRegion object paths for this Magnifier. * @return: The Magnifier's zoom region list as an array of DBus object * paths. */ getZoomRegions: function() { // There may be more ZoomRegions in the magnifier itself than have // been added through dbus. Make sure all of them are associated with // an object path and proxy. let zoomRegions = Main.magnifier.getZoomRegions(); let objectPaths = []; let thoseZoomers = this._zoomers; zoomRegions.forEach (function(aZoomRegion, index, array) { let found = false; for (let objectPath in thoseZoomers) { let proxyAndZoomRegion = thoseZoomers[objectPath]; if (proxyAndZoomRegion.zoomRegion === aZoomRegion) { objectPaths.push(objectPath); found = true; break; } } if (!found) { // Got a ZoomRegion with no DBus proxy, make one. let newPath = ZOOM_SERVICE_PATH + '/zoomer' + _zoomRegionInstanceCount; _zoomRegionInstanceCount++; let zoomRegionProxy = new CinnamonMagnifierZoomRegion(newPath, aZoomRegion); let proxyAndZoomer = {}; proxyAndZoomer.proxy = zoomRegionProxy; proxyAndZoomer.zoomRegion = aZoomRegion; thoseZoomers[newPath] = proxyAndZoomer; objectPaths.push(newPath); } }); return objectPaths; }, /** * clearAllZoomRegions: * Remove all the zoom regions from this Magnfier's ZoomRegion list. */ clearAllZoomRegions: function() { Main.magnifier.clearAllZoomRegions(); for (let objectPath in this._zoomers) { let proxyAndZoomer = this._zoomers[objectPath]; proxyAndZoomer.proxy.destroy(); proxyAndZoomer.proxy = null; proxyAndZoomer.zoomRegion = null; delete this._zoomers[objectPath]; } this._zoomers = {}; }, /** * fullScreenCapable: * Consult if the Magnifier can magnify in full-screen mode. * @return Always return true. */ fullScreenCapable: function() { return true; }, /** * setCrosswireSize: * Set the crosswire size of all ZoomRegions. * @size: The thickness of each line in the cross wire. */ setCrosswireSize: function(size) { Main.magnifier.setCrosshairsThickness(size); }, /** * getCrosswireSize: * Get the crosswire size of all ZoomRegions. * @return: The thickness of each line in the cross wire. */ getCrosswireSize: function() { return Main.magnifier.getCrosshairsThickness(); }, /** * setCrosswireLength: * Set the crosswire length of all zoom-regions.. * @size: The length of each line in the cross wire. */ setCrosswireLength: function(length) { Main.magnifier.setCrosshairsLength(length); }, /** * setCrosswireSize: * Set the crosswire size of all zoom-regions. * @size: The thickness of each line in the cross wire. */ getCrosswireLength: function() { return Main.magnifier.getCrosshairsLength(); }, /** * setCrosswireClip: * Set if the crosswire will be clipped by the cursor image.. * @clip: Flag to indicate whether to clip the crosswire. */ setCrosswireClip: function(clip) { Main.magnifier.setCrosshairsClip(clip); }, /** * getCrosswireClip: * Get the crosswire clip value. * @return: Whether the crosswire is clipped by the cursor image. */ getCrosswireClip: function() { return Main.magnifier.getCrosshairsClip(); }, /** * setCrosswireColor: * Set the crosswire color of all ZoomRegions. * @color: Unsigned int of the form rrggbbaa. */ setCrosswireColor: function(color) { Main.magnifier.setCrosshairsColor('#%08x'.format(color)); }, /** * getCrosswireClip: * Get the crosswire color of all ZoomRegions. * @return: The crosswire color as an unsigned int in the form rrggbbaa. */ getCrosswireColor: function() { let colorString = Main.magnifier.getCrosshairsColor(); // Drop the leading '#'. return parseInt(colorString.slice(1), 16); } }; /** * CinnamonMagnifierZoomRegion: * Object that implements the DBus ZoomRegion interface. * @zoomerObjectPath: String that is the path to a DBus ZoomRegion. * @zoomRegion: The actual zoom region associated with the object path. */ function CinnamonMagnifierZoomRegion(zoomerObjectPath, zoomRegion) { this._init(zoomerObjectPath, zoomRegion); } CinnamonMagnifierZoomRegion.prototype = { _init: function(zoomerObjectPath, zoomRegion) { this._zoomRegion = zoomRegion; this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ZoomRegionIface, this); this._dbusImpl.export(Gio.DBus.session, zoomerObjectPath); }, /** * setMagFactor: * @xMagFactor: The power to set the horizontal magnification factor to * of the magnified view. A value of 1.0 means no * magnification. A value of 2.0 doubles the size. * @yMagFactor: The power to set the vertical magnification factor to * of the magnified view. */ setMagFactor: function(xMagFactor, yMagFactor) { this._zoomRegion.setMagFactor(xMagFactor, yMagFactor); }, /** * getMagFactor: * @return an array, [xMagFactor, yMagFactor], containing the horizontal * and vertical magnification powers. A value of 1.0 means no * magnification. A value of 2.0 means the contents are doubled * in size, and so on. */ getMagFactor: function() { return this._zoomRegion.getMagFactor(); }, /** * setRoi: * Sets the "region of interest" that the ZoomRegion is magnifying. * @roi Array, [left, top, right, bottom], defining the region of the * screen to magnify. The values are in screen (unmagnified) * coordinate space. */ setRoi: function(roi) { let roiObject = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] }; this._zoomRegion.setROI(roiObject); }, /** * getRoi: * Retrieves the "region of interest" -- the rectangular bounds of that part * of the desktop that the magnified view is showing (x, y, width, height). * The bounds are given in non-magnified coordinates. * @return an array, [left, top, right, bottom], representing the bounding * rectangle of what is shown in the magnified view. */ getRoi: function() { let roi = this._zoomRegion.getROI(); roi[2] += roi[0]; roi[3] += roi[1]; return roi; }, /** * Set the "region of interest" by centering the given screen coordinate * within the zoom region. * @x The x-coord of the point to place at the center of the zoom region. * @y The y-coord. * @return Whether the shift was successful (for GS-mag, this is always * true). */ shiftContentsTo: function(x, y) { this._zoomRegion.scrollContentsTo(x, y); return true; }, /** * moveResize * Sets the position and size of the ZoomRegion on screen. * @viewPort Array, [left, top, right, bottom], defining the position and * size on screen to place the zoom region. */ moveResize: function(viewPort) { let viewRect = { x: viewPort[0], y: viewPort[1], width: viewPort[2] - viewPort[0], height: viewPort[3] - viewPort[1] }; this._zoomRegion.setViewPort(viewRect); }, destroy: function() { this._dbusImpl.unexport(); } };Cinnamon-2.8.6/js/ui/appFavorites.js0000664000175000017500000000610012625666430016256 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Signals = imports.signals; function AppFavorites() { this._init(); } AppFavorites.prototype = { FAVORITE_APPS_KEY: 'favorite-apps', _init: function() { this._favorites = {}; global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged)); this._reload(); }, _onFavsChanged: function() { this._reload(); this.emit('changed'); }, _reload: function() { let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY); let appSys = Cinnamon.AppSystem.get_default(); let apps = ids.map(function (id) { let app = appSys.lookup_app(id); return app; }).filter(function (app) { return app != null; }); this._favorites = {}; for (let i = 0; i < apps.length; i++) { let app = apps[i]; this._favorites[app.get_id()] = app; } }, _getIds: function() { let ret = []; for (let id in this._favorites) ret.push(id); return ret; }, getFavoriteMap: function() { return this._favorites; }, getFavorites: function() { let ret = []; for (let id in this._favorites) ret.push(this._favorites[id]); return ret; }, isFavorite: function(appId) { return appId in this._favorites; }, _addFavorite: function(appId, pos) { if (appId in this._favorites) return false; let app = Cinnamon.AppSystem.get_default().lookup_app(appId); if (!app) app = Cinnamon.AppSystem.get_default().lookup_settings_app(appId); if (!app) return false; let ids = this._getIds(); if (pos == -1) ids.push(appId); else ids.splice(pos, 0, appId); global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); this._favorites[appId] = app; return true; }, addFavoriteAtPos: function(appId, pos) { this._addFavorite(appId, pos); }, addFavorite: function(appId) { this.addFavoriteAtPos(appId, -1); }, moveFavoriteToPos: function(appId, pos) { this._removeFavorite(appId); this._addFavorite(appId, pos); }, _removeFavorite: function(appId) { if (!appId in this._favorites) return false; let ids = this._getIds().filter(function (id) { return id != appId; }); global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); return true; }, removeFavorite: function(appId) { let app = this._favorites[appId]; this._removeFavorite(appId); } }; Signals.addSignalMethods(AppFavorites.prototype); var appFavoritesInstance = null; function getAppFavorites() { if (appFavoritesInstance == null) appFavoritesInstance = new AppFavorites(); return appFavoritesInstance; } Cinnamon-2.8.6/js/ui/search.js0000664000175000017500000003124112625666430015064 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Signals = imports.signals; const Cinnamon = imports.gi.Cinnamon; const Util = imports.misc.util; const FileUtils = imports.misc.fileUtils; const Main = imports.ui.main; const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers'; // Not currently referenced by the search API, but // this enumeration can be useful for provider // implementations. const MatchType = { NONE: 0, SUBSTRING: 1, MULTIPLE_SUBSTRING: 2, PREFIX: 3, MULTIPLE_PREFIX: 4 }; function SearchResultDisplay(provider) { this._init(provider); } SearchResultDisplay.prototype = { _init: function(provider) { this.provider = provider; this.actor = null; this.selectionIndex = -1; }, /** * renderResults: * @results: List of identifier strings * @terms: List of search term strings * * Display the given search matches which resulted * from the given terms. It's expected that not * all results will fit in the space for the container * actor; in this case, show as many as makes sense * for your result type. * * The terms are useful for search match highlighting. */ renderResults: function(results, terms) { throw new Error('Not implemented'); }, /** * clear: * Remove all results from this display and reset the selection index. */ clear: function() { this.actor.get_children().forEach(function (actor) { actor.destroy(); }); this.selectionIndex = -1; }, /** * getSelectionIndex: * * Returns the index of the selected actor, or -1 if none. */ getSelectionIndex: function() { return this.selectionIndex; }, /** * getVisibleResultCount: * * Returns: The number of actors visible. */ getVisibleResultCount: function() { throw new Error('Not implemented'); }, /** * selectIndex: * @index: Integer index * * Move selection to the given index. * Return true if successful, false if no more results * available. */ selectIndex: function() { throw new Error('Not implemented'); }, /** * Activate the currently selected search result. */ activateSelected: function() { throw new Error('Not implemented'); } }; /** * SearchProvider: * * Subclass this object to add a new result type * to the search system, then call registerProvider() * in SearchSystem with an instance. */ function SearchProvider(title) { this._init(title); } SearchProvider.prototype = { _init: function(title) { this.title = title; this.searchSystem = null; this.searchAsync = false; }, _asyncCancelled: function() { }, startAsync: function() { this.searchAsync = true; }, tryCancelAsync: function() { if (!this.searchAsync) return; this._asyncCancelled(); this.searchAsync = false; }, /** * addItems: * @items: an array of result identifier strings representing * items which match the last given search terms. * * This should be used for something that requires a bit more * logic; it's designed to be an asyncronous way to add a result * to the current search. */ addItems: function(items) { if (!this.searchSystem) throw new Error('Search provider not registered'); if (!items.length) return; this.tryCancelAsync(); this.searchSystem.addProviderItems(this, items); }, /** * getInitialResultSet: * @terms: Array of search terms, treated as logical AND * * Called when the user first begins a search (most likely * therefore a single term of length one or two), or when * a new term is added. * * Should return an array of result identifier strings representing * items which match the given search terms. This * is expected to be a substring match on the metadata for a given * item. Ordering of returned results is up to the discretion of the provider, * but you should follow these heruistics: * * * Put items where the term matches multiple criteria (e.g. name and * description) before single matches * * Put items which match on a prefix before non-prefix substring matches * * This function should be fast; do not perform unindexed full-text searches * or network queries. */ getInitialResultSet: function(terms) { throw new Error('Not implemented'); }, /** * getSubsearchResultSet: * @previousResults: Array of item identifiers * @newTerms: Updated search terms * * Called when a search is performed which is a "subsearch" of * the previous search; i.e. when every search term has exactly * one corresponding term in the previous search which is a prefix * of the new term. * * This allows search providers to only search through the previous * result set, rather than possibly performing a full re-query. */ getSubsearchResultSet: function(previousResults, newTerms) { throw new Error('Not implemented'); }, /** * getResultMeta: * @id: Result identifier string * * Return an object with 'id', 'name', (both strings) and 'createIcon' * (function(size) returning a Clutter.Texture) properties which describe * the given search result. */ getResultMeta: function(id) { throw new Error('Not implemented'); }, /** * createResultContainer: * * Search providers may optionally override this to render their * results in a custom fashion. The default implementation * will create a vertical list. * * Returns: An instance of SearchResultDisplay. */ createResultContainerActor: function() { return null; }, /** * createResultActor: * @resultMeta: Object with result metadata * @terms: Array of search terms, should be used for highlighting * * Search providers may optionally override this to render a * particular serch result in a custom fashion. The default * implementation will show the icon next to the name. * * The actor should be an instance of St.Widget, with the style class * 'search-result-content'. */ createResultActor: function(resultMeta, terms) { return null; }, /** * activateResult: * @id: Result identifier string * * Called when the user chooses a given result. */ activateResult: function(id) { throw new Error('Not implemented'); } }; Signals.addSignalMethods(SearchProvider.prototype); function OpenSearchSystem() { this._init(); } OpenSearchSystem.prototype = { _init: function() { this._providers = []; global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh)); this._refresh(); }, getProviders: function() { let res = []; for (let i = 0; i < this._providers.length; i++) res.push({ id: i, name: this._providers[i].name }); return res; }, setSearchTerms: function(terms) { this._terms = terms; }, _checkSupportedProviderLanguage: function(provider) { if (provider.url.search(/{language}/) == -1) return true; let langs = GLib.get_language_names(); langs.push('en'); let lang = null; for (let i = 0; i < langs.length; i++) { for (let k = 0; k < provider.langs.length; k++) { if (langs[i] == provider.langs[k]) lang = langs[i]; } if (lang) break; } provider.lang = lang; return lang != null; }, activateResult: function(id, params) { let searchTerms = this._terms.join(' '); let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms)); if (url.match('{language}')) url = url.replace('{language}', this._providers[id].lang); try { Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); } catch (e) { // TODO: remove this after glib will be removed from moduleset // In the default jhbuild, gio is in our prefix but gvfs is not Util.spawn(['gvfs-open', url]) } Main.overview.hide(); }, _addProvider: function(fileName) { let path = global.datadir + '/search_providers/' + fileName; let source = Cinnamon.get_file_contents_utf8_sync(path); let [success, name, url, langs, icon_uri] = Cinnamon.parse_search_provider(source); let provider ={ name: name, url: url, id: this._providers.length, icon_uri: icon_uri, langs: langs }; if (this._checkSupportedProviderLanguage(provider)) { this._providers.push(provider); this.emit('changed'); } }, _refresh: function() { this._providers = []; let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY); let file = Gio.file_new_for_path(global.datadir + '/search_providers'); FileUtils.listDirAsync(file, Lang.bind(this, function(files) { for (let i = 0; i < files.length; i++) { let enabled = true; let name = files[i].get_name(); for (let k = 0; k < names.length; k++) if (names[k] == name) enabled = false; if (enabled) this._addProvider(name); } })); } } Signals.addSignalMethods(OpenSearchSystem.prototype); function SearchSystem() { this._init(); } SearchSystem.prototype = { _init: function() { this._providers = []; this.reset(); }, registerProvider: function (provider) { provider.searchSystem = this; this._providers.push(provider); }, unregisterProvider: function (provider) { let index = this._providers.indexOf(provider); if (index == -1) return; provider.searchSystem = null; this._providers.splice(index, 1); }, getProviders: function() { return this._providers; }, getTerms: function() { return this._previousTerms; }, reset: function() { this._previousTerms = []; this._previousResults = []; }, addProviderItems: function(provider, items) { this.emit('search-updated', provider, items); }, updateSearch: function(searchString) { searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, ''); if (searchString == '') return; let terms = searchString.split(/\s+/); this.updateSearchResults(terms); }, updateSearchResults: function(terms) { if (!terms) return; let isSubSearch = terms.length == this._previousTerms.length; if (isSubSearch) { for (let i = 0; i < terms.length; i++) { if (terms[i].indexOf(this._previousTerms[i]) != 0) { isSubSearch = false; break; } } } let results = []; if (isSubSearch) { for (let i = 0; i < this._providers.length; i++) { let [provider, previousResults] = this._previousResults[i]; provider.tryCancelAsync(); try { let providerResults = provider.getSubsearchResultSet(previousResults, terms); results.push([provider, providerResults]); } catch (error) { global.logError('A ' + error.name + ' has occured in ' + provider.title, error); } } } else { for (let i = 0; i < this._providers.length; i++) { let provider = this._providers[i]; provider.tryCancelAsync(); try { let providerResults = provider.getInitialResultSet(terms); results.push([provider, providerResults]); } catch (error) { global.logError('A ' + error.name + ' has occured in ' + provider.title, error); } } } this._previousTerms = terms; this._previousResults = results; this.emit('search-completed', results); }, }; Signals.addSignalMethods(SearchSystem.prototype); Cinnamon-2.8.6/js/ui/placesManager.js0000664000175000017500000004360412625666430016367 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Mainloop = imports.mainloop; const Signals = imports.signals; const St = imports.gi.St; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Params = imports.misc.params; const Search = imports.ui.search; const Util = imports.misc.util; /** * Represents a place object, which is most normally a bookmark entry, * a mount/volume, or a special place like the Home Folder, Computer, and Network. * * @name: String title * @iconFactory: A JavaScript callback which will create an icon texture given a size parameter * @launch: A JavaScript callback to launch the entry */ function PlaceInfo(id, name, iconFactory, launch) { this._init(id, name, iconFactory, launch); } PlaceInfo.prototype = { _init: function(id, name, iconFactory, launch) { this.id = id; this.name = name; this._lowerName = name.toLowerCase(); this.iconFactory = iconFactory; this.launch = launch; }, matchTerms: function(terms) { let mtype = Search.MatchType.NONE; for (let i = 0; i < terms.length; i++) { let term = terms[i]; let idx = this._lowerName.indexOf(term); if (idx == 0) { mtype = Search.MatchType.PREFIX; } else if (idx > 0) { if (mtype == Search.MatchType.NONE) mtype = Search.MatchType.SUBSTRING; } else { return Search.MatchType.NONE; } } return mtype; }, isRemovable: function() { return false; } }; // Helper function to translate launch parameters into a GAppLaunchContext function _makeLaunchContext(params) { params = Params.parse(params, { workspace: -1, timestamp: 0 }); let launchContext = global.create_app_launch_context(); if (params.workspace != -1) launchContext.set_desktop(params.workspace); if (params.timestamp != 0) launchContext.set_timestamp(params.timestamp); return launchContext; } function PlaceDeviceInfo(mount) { this._init(mount); } PlaceDeviceInfo.prototype = { __proto__: PlaceInfo.prototype, _init: function(mount) { this._mount = mount; this.name = mount.get_name(); this._lowerName = this.name.toLowerCase(); this.id = 'mount:' + mount.get_root().get_uri(); }, iconFactory: function(size) { let icon = this._mount.get_icon(); return St.TextureCache.get_default().load_gicon(null, icon, size); }, launch: function(params) { Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(), _makeLaunchContext(params)); }, isRemovable: function() { return this._mount.can_unmount(); }, remove: function() { if (!this.isRemovable()) return; if (this._mount.can_eject()) this._mount.eject(0, null, Lang.bind(this, this._removeFinish)); else this._mount.unmount(0, null, Lang.bind(this, this._removeFinish)); }, _removeFinish: function(o, res, data) { try { if (this._mount.can_eject()) this._mount.eject_finish(res); else this._mount.unmount_finish(res); } catch (e) { let message = _("Failed to unmount '%s'").format(o.get_name()); let source = new MessageTray.SystemNotificationSource(); if (Main.messageTray) { Main.messageTray.add(source); let notification = new MessageTray.Notification(source, message, null); notification.setTransient(true); notification.addButton('system-undo', _("Retry")); notification.connect('action-invoked', Lang.bind(this, this.remove)); source.notify(notification); } } } }; function PlacesManager() { this._init(); } PlacesManager.prototype = { _init: function() { this._defaultPlaces = []; this._mounts = []; this._bookmarks = []; let homeFile = Gio.file_new_for_path (GLib.get_home_dir()); let homeUri = homeFile.get_uri(); let homeLabel = Cinnamon.util_get_label_for_uri (homeUri); let homeIcon = Cinnamon.util_get_icon_for_uri (homeUri); this._home = new PlaceInfo('special:home', homeLabel, function(size) { return St.TextureCache.get_default().load_gicon(null, homeIcon, size); }, function(params) { Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params)); }); let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); let desktopFile = Gio.file_new_for_path (desktopPath); let desktopUri = desktopFile.get_uri(); let desktopLabel = Cinnamon.util_get_label_for_uri (desktopUri); let desktopIcon = Cinnamon.util_get_icon_for_uri (desktopUri); this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel, function(size) { return St.TextureCache.get_default().load_gicon(null, desktopIcon, size); }, function(params) { Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params)); }); this._connect = new PlaceInfo('special:connect', _("Connect to..."), function (size) { return new St.Icon({ icon_name: 'applications-internet', icon_type: St.IconType.FULLCOLOR, icon_size: size }); }, function (params) { // BUG: nemo-connect-server doesn't have a desktop file, so we can't // launch it with the workspace from params. It's probably pretty rare // and odd to drag this place onto a workspace in any case Util.spawn(['nemo-connect-server']); }); this._defaultPlaces.push(this._home); this._defaultPlaces.push(this._desktopMenu); this._defaultPlaces.push(this._connect); /* * Show devices, code more or less ported from nemo-places-sidebar.c */ this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded)); this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._onVolumeRemoved)); this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._onVolumeChanged)); this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded)); this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved)); this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._onMountChanged)); this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected)); this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected)); this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._onDriveChanged)); this._deviceUpdateAwaiting = false; this._updateDevices(); this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']); this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath); if (!this._bookmarksFile.query_exists(null)) { this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']); this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath); } this.monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); this._bookmarkTimeoutId = 0; this.monitor.connect('changed', Lang.bind(this, function () { if (this._bookmarkTimeoutId > 0) return; /* Defensive event compression */ this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () { this._bookmarkTimeoutId = 0; this._reloadBookmarks(); return false; })); })); this._reloadBookmarks(); }, // Mounting a device triggers a lot of different events, wait 3 seconds and try to only call this._updateDevices() once _updateDevicesAsync: function() { if (this._deviceUpdateAwaiting == false) { this._deviceUpdateAwaiting = true; Mainloop.timeout_add(3000, Lang.bind(this, function () { this._updateDevices(); })); } }, _onVolumeAdded: function() { this._updateDevicesAsync(); }, _onVolumeRemoved: function() { this._updateDevicesAsync(); }, _onVolumeChanged: function() { this._updateDevicesAsync(); }, _onMountAdded: function() { this._updateDevicesAsync(); }, _onMountRemoved: function() { this._updateDevicesAsync(); }, _onMountChanged: function() { this._updateDevicesAsync(); }, _onDriveConnected: function() { Main.soundManager.play('plug'); this._updateDevicesAsync(); }, _onDriveDisconnected: function() { Main.soundManager.play('unplug'); this._updateDevicesAsync(); }, _onDriveChanged: function() { this._updateDevicesAsync(); }, _updateDevices: function() { this._deviceUpdateAwaiting = false; global.log("PlacesManager: Updating devices"); this._mounts = []; /* first go through all connected drives */ let drives = this._volumeMonitor.get_connected_drives(); for (let i = 0; i < drives.length; i++) { let volumes = drives[i].get_volumes(); for(let j = 0; j < volumes.length; j++) { let mount = volumes[j].get_mount(); if(mount != null) { this._addMount(mount); } } } /* add all volumes that is not associated with a drive */ let volumes = this._volumeMonitor.get_volumes(); for(let i = 0; i < volumes.length; i++) { if(volumes[i].get_drive() != null) continue; let mount = volumes[i].get_mount(); if(mount != null) { this._addMount(mount); } } /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ let mounts = this._volumeMonitor.get_mounts(); for(let i = 0; i < mounts.length; i++) { if(mounts[i].is_shadowed()) continue; if(mounts[i].get_volume()) continue; this._addMount(mounts[i]); } /* We emit two signals, one for a generic 'all places' update * and the other for one specific to mounts. We do this because * clients like PlaceDisplay may only care about places in general * being updated while clients like DashPlaceDisplay care which * specific type of place got updated. */ this.emit('mounts-updated'); this.emit('places-updated'); }, _reloadBookmarks: function() { this._bookmarks = []; if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS)) return; let bookmarksContent = Cinnamon.get_file_contents_utf8_sync(this._bookmarksPath); let bookmarks = bookmarksContent.split('\n'); let bookmarksToLabel = {}; let bookmarksOrder = []; for (let i = 0; i < bookmarks.length; i++) { let bookmarkLine = bookmarks[i]; let components = bookmarkLine.split(' '); let bookmark = components[0]; if (bookmark in bookmarksToLabel) continue; let label = null; if (components.length > 1) label = components.slice(1).join(' '); bookmarksToLabel[bookmark] = label; bookmarksOrder.push(bookmark); } for (let i = 0; i < bookmarksOrder.length; i++) { let bookmark = bookmarksOrder[i]; let label = bookmarksToLabel[bookmark]; let file = Gio.file_new_for_uri(bookmark); if (label == null) label = Cinnamon.util_get_label_for_uri(bookmark); if (label == null) continue; let item; if (file.query_exists(null)) { let icon = Cinnamon.util_get_icon_for_uri(bookmark); item = new PlaceInfo('bookmark:' + bookmark, label, function(size) { return St.TextureCache.get_default().load_gicon(null, icon, size); }, function(params) { Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params)); }); } else { // Asume the bookmark is an unmounted network location // try to mount and open by the default file manager let icon = Gio.ThemedIcon.new('network-workgroup'); item = new PlaceInfo('bookmark:' + bookmark, label, function(size) { return St.TextureCache.get_default().load_gicon(null, icon, size); }, function(params) { let fileapp = Gio.app_info_get_default_for_uri_scheme('file'); if (fileapp) { fileapp.launch_uris([bookmark], _makeLaunchContext(params)); } }); } this._bookmarks.push(item); } /* See comment in _updateDevices for explanation why there are two signals. */ this.emit('bookmarks-updated'); this.emit('places-updated'); }, _addMount: function(mount) { let devItem = new PlaceDeviceInfo(mount); this._mounts.push(devItem); }, getAllPlaces: function () { return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts()); }, getDefaultPlaces: function () { return this._defaultPlaces; }, getBookmarks: function () { return this._bookmarks; }, getMounts: function () { return this._mounts; }, _lookupIndexById: function(sourceArray, id) { for (let i = 0; i < sourceArray.length; i++) { let place = sourceArray[i]; if (place.id == id) return i; } return -1; }, lookupPlaceById: function(id) { let colonIdx = id.indexOf(':'); let type = id.substring(0, colonIdx); let sourceArray = null; if (type == 'special') sourceArray = this._defaultPlaces; else if (type == 'mount') sourceArray = this._mounts; else if (type == 'bookmark') sourceArray = this._bookmarks; return sourceArray[this._lookupIndexById(sourceArray, id)]; }, _removeById: function(sourceArray, id) { sourceArray.splice(this._lookupIndexById(sourceArray, id), 1); } }; Signals.addSignalMethods(PlacesManager.prototype); function PlaceSearchProvider() { this._init(); } PlaceSearchProvider.prototype = { __proto__: Search.SearchProvider.prototype, _init: function() { Search.SearchProvider.prototype._init.call(this, "PLACES & DEVICES"); }, getResultMeta: function(resultId) { let placeInfo = Main.placesManager.lookupPlaceById(resultId); if (!placeInfo) return null; return { 'id': resultId, 'name': placeInfo.name, 'createIcon': function(size) { return placeInfo.iconFactory(size); } }; }, activateResult: function(id, params) { let placeInfo = Main.placesManager.lookupPlaceById(id); placeInfo.launch(params); }, _compareResultMeta: function (idA, idB) { let infoA = Main.placesManager.lookupPlaceById(idA); let infoB = Main.placesManager.lookupPlaceById(idB); return infoA.name.localeCompare(infoB.name); }, _searchPlaces: function(places, terms) { let multiplePrefixResults = []; let prefixResults = []; let multipleSubstringResults = []; let substringResults = []; terms = terms.map(String.toLowerCase); for (let i = 0; i < places.length; i++) { let place = places[i]; let mtype = place.matchTerms(terms); if (mtype == Search.MatchType.MULTIPLE_PREFIX) multiplePrefixResults.push(place.id); else if (mtype == Search.MatchType.PREFIX) prefixResults.push(place.id); else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING) multipleSubstringResults.push(place.id); else if (mtype == Search.MatchType.SUBSTRING) substringResults.push(place.id); } multiplePrefixResults.sort(this._compareResultMeta); prefixResults.sort(this._compareResultMeta); multipleSubstringResults.sort(this._compareResultMeta); substringResults.sort(this._compareResultMeta); return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults))); }, getInitialResultSet: function(terms) { let places = Main.placesManager.getAllPlaces(); return this._searchPlaces(places, terms); }, getSubsearchResultSet: function(previousResults, terms) { let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); }); return this._searchPlaces(places, terms); } }; Cinnamon-2.8.6/js/ui/main.js0000664000175000017500000014630312625666430014551 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /** * FILE:main.js * @short_description: This is the heart of Cinnamon, the mother of everything. * @placesManager (PlacesManager.PlacesManager): The places manager * @overview (Overview.Overview): The "scale" overview * @expo (Expo.Expo): The "expo" overview * @runDialog (RunDialog.RunDialog): The run dialog * @lookingGlass (LookingGlass.Melange): The looking glass object * @wm (WindowManager.WindowManager): The window manager * @messageTray (MessageTray.MessageTray): The mesesage tray * @indicatorManager (IndicatorManager.IndicatorManager): The indicator manager * @notificationDaemon (NotificationDaemon.NotificationDaemon): The notification daemon * @windowAttentionHandler (WindowAttentionHandler.WindowAttentionHandler): The window attention handler * @recorder (Cinnamon.Recorder): The recorder * @cinnamonDBusService (CinnamonDBus.Cinnamon): The cinnamon dbus object * @modalCount (int): The number of modals "pushed" * @modalActorFocusStack (array): Array of pushed modal actors * @uiGroup (Cinnamon.GenericContainer): The group containing all Cinnamon and * Muffin actors * * @magnifier (Magnifier.Magnifier): The magnifier * @xdndHandler (XdndHandler.XdndHandler): The X DND handler * @statusIconDispatcher (StatusIconDispatcher.StatusIconDispatcher): The status icon dispatcher * @keyboard (Keyboard.Keyboard): The keyboard object * @layoutManager (Layout.LayoutManager): The layout manager. * \ * All actors that are part of the Cinnamon UI ar handled by the layout * manager, which will determine when to show and hide the actors etc. * * @panelManager (Panel.PanelManager): The panel manager. * \ * This is responsible for handling events relating to panels, eg. showing all * panels. * * @themeManager (ThemeManager.ThemeManager): The theme manager * @soundManager (SoundManager.SoundManager): The sound manager * @settingsManager (Settings.SettingsManager): The manager of the xlet Settings API * * @backgroundManager (BackgroundManager.BackgroundManager): The background * manager. * \ * This listens to changes in the GNOME background settings and mirrors them to * the Cinnamon settings, since many applications have a "Set background" * button that modifies the GNOME background settings. * * @slideshowManager (SlideshowManager.SlideshowManager): The slideshow manager. * \ * This is responsible for managing the background slideshow, since the * background "slideshow" is created by cinnamon changing the active background * gsetting every x minutes. * * @keybindingManager (KeybindingManager.KeybindingManager): The keybinding manager * @systrayManager (Systray.SystrayManager): The systray manager * * @osdWindow (OsdWindow.OsdWindow): Osd window that pops up when you use media * keys. * @tracker (Cinnamon.WindowTracker): The window tracker * @workspace_names (array): Names of workspace * @deskletContainer (DeskletManager.DeskletContainer): The desklet container. * \ * This is a container that contains all the desklets as childs. Its actor is * put between @global.bottom_window_group and @global.uiGroup. * @software_rendering (boolean): Whether software rendering is used * @lg_log_file (Gio.FileOutputStream): The stream used to log looking messages * to ~/.cinnamon/glass.log * @can_log (boolean): Whether looking glass log to file can be used * @popup_rendering_actor (Clutter.Actor): The popup actor that is in the process of rendering * @xlet_startup_error (boolean): Whether there was at least one xlet that did * not manage to load * * The main file is responsible for launching Cinnamon as well as creating its * components. The C part of cinnamon calls the @start() function, which then * initializes all of cinnamon. Most components of Cinnamon can be accessed * through main. */ const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const PointerTracker = imports.misc.pointerTracker; const Lang = imports.lang; const SoundManager = imports.ui.soundManager; const BackgroundManager = imports.ui.backgroundManager; const SlideshowManager = imports.ui.slideshowManager; const AppletManager = imports.ui.appletManager; const SearchProviderManager = imports.ui.searchProviderManager; const DeskletManager = imports.ui.deskletManager; const ExtensionSystem = imports.ui.extensionSystem; const Keyboard = imports.ui.keyboard; const MessageTray = imports.ui.messageTray; const IndicatorManager = imports.ui.indicatorManager; const OsdWindow = imports.ui.osdWindow; const Overview = imports.ui.overview; const Expo = imports.ui.expo; const Panel = imports.ui.panel; const PlacesManager = imports.ui.placesManager; const RunDialog = imports.ui.runDialog; const Layout = imports.ui.layout; const LookingGlass = imports.ui.lookingGlass; const NotificationDaemon = imports.ui.notificationDaemon; const WindowAttentionHandler = imports.ui.windowAttentionHandler; const Scripting = imports.ui.scripting; const CinnamonDBus = imports.ui.cinnamonDBus; const WindowManager = imports.ui.windowManager; const ThemeManager = imports.ui.themeManager; const Magnifier = imports.ui.magnifier; const XdndHandler = imports.ui.xdndHandler; const StatusIconDispatcher = imports.ui.statusIconDispatcher; const Util = imports.misc.util; const Keybindings = imports.ui.keybindings; const Settings = imports.ui.settings; const Systray = imports.ui.systray; const DEFAULT_BACKGROUND_COLOR = new Clutter.Color(); DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff); const LAYOUT_TRADITIONAL = "traditional"; const LAYOUT_FLIPPED = "flipped"; const LAYOUT_CLASSIC = "classic"; const CIN_LOG_FOLDER = GLib.get_home_dir() + '/.cinnamon/'; let panel = null; let soundManager = null; let backgroundManager = null; let slideshowManager = null; let placesManager = null; let panelManager = null; let osdWindow = null; let overview = null; let expo = null; let runDialog = null; let lookingGlass = null; let wm = null; let messageTray = null; let indicatorManager = null; let notificationDaemon = null; let windowAttentionHandler = null; let recorder = null; let cinnamonDBusService = null; let modalCount = 0; let modalActorFocusStack = []; let uiGroup = null; let magnifier = null; let xdndHandler = null; let statusIconDispatcher = null; let keyboard = null; let layoutManager = null; let themeManager = null; let keybindingManager = null; let _errorLogStack = []; let _startDate; let _defaultCssStylesheet = null; let _cssStylesheet = null; let dynamicWorkspaces = null; let tracker = null; let settingsManager = null; let systrayManager = null; let wmSettings = null; let workspace_names = []; let applet_side = St.Side.TOP; // Kept to maintain compatibility. Doesn't seem to be used anywhere let deskletContainer = null; let software_rendering = false; let lg_log_file; let can_log = false; let popup_rendering_actor = null; let xlet_startup_error = false; // Override Gettext localization const Gettext = imports.gettext; Gettext.bindtextdomain('cinnamon', '/usr/share/locale'); Gettext.textdomain('cinnamon'); const _ = Gettext.gettext; function _initRecorder() { let recorderSettings = new Gio.Settings({ schema_id: 'org.cinnamon.recorder' }); global.screen.connect('toggle-recording', function() { if (recorder == null) { recorder = new Cinnamon.Recorder({ stage: global.stage }); } if (recorder.is_recording()) { recorder.pause(); Meta.enable_unredirect_for_screen(global.screen); } else { // read the parameters from GSettings always in case they have changed recorder.set_framerate(recorderSettings.get_int('framerate')); recorder.set_filename('cinnamon-%d%u-%c.' + recorderSettings.get_string('file-extension')); let pipeline = recorderSettings.get_string('pipeline'); if (!pipeline.match(/^\s*$/)) recorder.set_pipeline(pipeline); else recorder.set_pipeline(null); Meta.disable_unredirect_for_screen(global.screen); recorder.record(); } }); } function _initUserSession() { _initRecorder(); global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, 1, -1); systrayManager = new Systray.SystrayManager(); ExtensionSystem.init(); Meta.keybindings_set_custom_handler('panel-run-dialog', function() { getRunDialog().open(); }); } function do_shutdown_sequence() { panelManager.panels.forEach(function (panel) { panel.actor.hide(); }); } function _reparentActor(actor, newParent) { let parent = actor.get_parent(); if (parent) parent.remove_actor(actor); if(newParent) newParent.add_actor(actor); } /** * start: * * Starts cinnamon. Should not be called in JavaScript code */ function start() { global.reparentActor = _reparentActor; // Monkey patch utility functions into the global proxy; // This is easier and faster than indirecting down into global // if we want to call back up into JS. global.logTrace = _logTrace; global.logWarning = _logWarning; global.logError = _logError; global.log = _logInfo; let cinnamonStartTime = new Date().getTime(); if (global.settings.get_boolean("enable-looking-glass-logs")) { try { let log_filename = Gio.file_parse_name(CIN_LOG_FOLDER + '/glass.log'); let log_backup_filename = Gio.file_parse_name(CIN_LOG_FOLDER + '/glass.log.last'); let log_dir = Gio.file_new_for_path(CIN_LOG_FOLDER); if (!log_filename.query_exists(null)) { if (!log_dir.query_exists(null)) log_dir.make_directory_with_parents(null); lg_log_file = log_filename.append_to(0, null); } else { log_filename.copy(log_backup_filename, 1, null, null, null); log_filename.delete(null); lg_log_file = log_filename.append_to(0, null); } can_log = true; } catch (e) { global.logError("Error during looking-glass log initialization", e); } } log("About to start Cinnamon"); if (GLib.getenv('CINNAMON_SOFTWARE_RENDERING')) { log("ACTIVATING SOFTWARE RENDERING"); global.logError("Cinnamon Software Rendering mode enabled"); software_rendering = true; } // Chain up async errors reported from C global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); Gio.DesktopAppInfo.set_desktop_env('X-Cinnamon'); cinnamonDBusService = new CinnamonDBus.Cinnamon(); // Ensure CinnamonWindowTracker and CinnamonAppUsage are initialized; this will // also initialize CinnamonAppSystem first. CinnamonAppSystem // needs to load all the .desktop files, and CinnamonWindowTracker // will use those to associate with windows. Right now // the Monitor doesn't listen for installed app changes // and recalculate application associations, so to avoid // races for now we initialize it here. It's better to // be predictable anyways. tracker = Cinnamon.WindowTracker.get_default(); let startTime = new Date().getTime(); Cinnamon.AppSystem.get_default(); global.log('Cinnamon.AppSystem.get_default() started in %d ms'.format(new Date().getTime() - startTime)); // The stage is always covered so Clutter doesn't need to clear it; however // the color is used as the default contents for the Muffin root background // actor so set it anyways. global.stage.color = DEFAULT_BACKGROUND_COLOR; global.stage.no_clear_hint = true; Gtk.IconTheme.get_default().append_search_path("/usr/share/cinnamon/icons/"); _defaultCssStylesheet = global.datadir + '/theme/cinnamon.css'; soundManager = new SoundManager.SoundManager(); themeManager = new ThemeManager.ThemeManager(); settingsManager = new Settings.SettingsManager(); backgroundManager = new BackgroundManager.BackgroundManager(); slideshowManager = new SlideshowManager.SlideshowManager(); deskletContainer = new DeskletManager.DeskletContainer(); // Set up stage hierarchy to group all UI actors under one container. uiGroup = new Cinnamon.GenericContainer({ name: 'uiGroup' }); uiGroup.connect('allocate', function (actor, box, flags) { let children = uiGroup.get_children(); for (let i = 0; i < children.length; i++) children[i].allocate_preferred_size(flags); }); uiGroup.connect('get-preferred-width', function(actor, forHeight, alloc) { let width = global.stage.width; [alloc.min_size, alloc.natural_size] = [width, width]; }); uiGroup.connect('get-preferred-height', function(actor, forWidth, alloc) { let height = global.stage.height; [alloc.min_size, alloc.natural_size] = [height, height]; }); global.reparentActor(global.background_actor, uiGroup); global.background_actor.hide(); global.reparentActor(global.bottom_window_group, uiGroup); uiGroup.add_actor(deskletContainer.actor); global.reparentActor(global.window_group, uiGroup); global.reparentActor(global.overlay_group, uiGroup); let stage_bg = new Clutter.Actor(); let constraint = new Clutter.BindConstraint({ source: global.stage, coordinate: Clutter.BindCoordinate.ALL, offset: 0 }) stage_bg.add_constraint(constraint); stage_bg.set_background_color(new Clutter.Color({red: 0, green: 0, blue: 0, alpha: 255})); stage_bg.set_size(global.screen_width, global.screen_height); global.stage.add_actor(stage_bg); stage_bg.add_actor(uiGroup); global.reparentActor(global.top_window_group, global.stage); layoutManager = new Layout.LayoutManager(); Panel.checkPanelUpgrade(); panelManager = new Panel.PanelManager(); let startupAnimationEnabled = global.settings.get_boolean("startup-animation"); let do_animation = startupAnimationEnabled && !GLib.getenv('CINNAMON_SOFTWARE_RENDERING') && !GLib.getenv('CINNAMON_2D'); if (do_animation) { layoutManager._prepareStartupAnimation(); } let pointerTracker = new PointerTracker.PointerTracker(); pointerTracker.setPosition(layoutManager.primaryMonitor.x + layoutManager.primaryMonitor.width/2, layoutManager.primaryMonitor.y + layoutManager.primaryMonitor.height/2); xdndHandler = new XdndHandler.XdndHandler(); osdWindow = new OsdWindow.OsdWindow(); // This overview object is just a stub for non-user sessions overview = new Overview.Overview(); expo = new Expo.Expo(); statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher(); layoutManager._updateBoxes(); wm = new WindowManager.WindowManager(); messageTray = new MessageTray.MessageTray(); indicatorManager = new IndicatorManager.IndicatorManager(); keyboard = new Keyboard.Keyboard(); notificationDaemon = new NotificationDaemon.NotificationDaemon(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); placesManager = new PlacesManager.PlacesManager(); keybindingManager = new Keybindings.KeybindingManager(); magnifier = new Magnifier.Magnifier(); Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces); dynamicWorkspaces = false; // This should be configurable layoutManager.init(); keyboard.init(); overview.init(); expo.init(); _initUserSession(); // Provide the bus object for gnome-session to // initiate logouts. //EndSessionDialog.init(); _startDate = new Date(); global.stage.connect('captured-event', _stageEventHandler); global.log('loaded at ' + _startDate); log('Cinnamon started at ' + _startDate); let perfModuleName = GLib.getenv("CINNAMON_PERF_MODULE"); if (perfModuleName) { let perfOutput = GLib.getenv("CINNAMON_PERF_OUTPUT"); let module = eval('imports.perf.' + perfModuleName + ';'); Scripting.runPerfScript(module, perfOutput); } wmSettings = new Gio.Settings({schema_id: "org.cinnamon.desktop.wm.preferences"}) workspace_names = wmSettings.get_strv("workspace-names"); global.screen.connect('notify::n-workspaces', _nWorkspacesChanged); global.screen.connect('window-entered-monitor', _windowEnteredMonitor); global.screen.connect('window-left-monitor', _windowLeftMonitor); global.screen.connect('restacked', _windowsRestacked); _nWorkspacesChanged(); startTime = new Date().getTime(); AppletManager.init(); global.log('AppletManager.init() started in %d ms'.format(new Date().getTime() - startTime)); DeskletManager.init(); SearchProviderManager.init(); createLookingGlass(); if (software_rendering && !GLib.getenv('CINNAMON_2D')) { notifyCinnamon2d(); } if (xlet_startup_error) Mainloop.timeout_add_seconds(3, notifyXletStartupError); let sound_settings = new Gio.Settings( {schema_id: "org.cinnamon.sounds"} ); let do_login_sound = sound_settings.get_boolean("login-enabled"); // We're mostly prepared for the startup animation // now, but since a lot is going on asynchronously // during startup, let's defer the startup animation // until the event loop is uncontended and idle. // This helps to prevent us from running the animation // when the system is bogged down if (do_animation) { let id = GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() { if (do_login_sound) soundManager.play_once_per_session('login'); layoutManager._startupAnimation(); return GLib.SOURCE_REMOVE; })); } else { global.background_actor.show(); if (do_login_sound) soundManager.play_once_per_session('login'); } global.connect('shutdown', do_shutdown_sequence); global.log('Cinnamon took %d ms to start'.format(new Date().getTime() - cinnamonStartTime)); } function notifyCinnamon2d() { let icon = new St.Icon({ icon_name: 'display', icon_type: St.IconType.FULLCOLOR, icon_size: 36 }); criticalNotify(_("Running in software rendering mode"), _("Cinnamon is currently running without video hardware acceleration and, as a result, you may observe much higher than normal CPU usage.\n\n") + _("There could be a problem with your drivers or some other issue. For the best experience, it is recommended that you only use this mode for") + _(" troubleshooting purposes."), icon); } function notifyXletStartupError() { let icon = new St.Icon({ icon_name: 'dialog-warning', icon_type: St.IconType.FULLCOLOR, icon_size: 36 }); warningNotify(_("Problems during Cinnamon startup"), _("Cinnamon started successfully, but one or more applets, desklets or extensions failed to load.\n\n") + _("Check your system log and the Cinnamon LookingGlass log for any issues. ") + _("You can disable the offending extension(s) in Cinnamon Settings to prevent this message from recurring. ") + _("Please contact the developer."), icon); } /* Provided by panelManager now, but kept here for xlet compatibility */ function enablePanels() { panelManager.enablePanels(); } function disablePanels() { panelManager.disablePanels(); } function getPanels() { return panelManager.getPanels(); } let _workspaces = []; let _checkWorkspacesId = 0; /* * When the last window closed on a workspace is a dialog or splash * screen, we assume that it might be an initial window shown before * the main window of an application, and give the app a grace period * where it can map another window before we remove the workspace. */ const LAST_WINDOW_GRACE_TIME = 1000; function _fillWorkspaceNames(index) { // ensure that we have workspace names up to index for (let i = index - workspace_names.length; i > 0; --i) { workspace_names.push(''); } } function _trimWorkspaceNames(index) { // trim empty or out-of-bounds names from the end. for (let i = workspace_names.length - 1; i >= 0 && (i >= global.screen.n_workspaces || !workspace_names[i].length); --i) { workspace_names.pop(); } } function _makeDefaultWorkspaceName(index) { return _("WORKSPACE") + " " + (index + 1).toString(); } /** * setWorkspaceName: * @index (int): index of workspace * @name (string): name of workspace * * Sets the name of the workspace @index to @name */ function setWorkspaceName(index, name) { name.trim(); if (name != getWorkspaceName(index)) { _fillWorkspaceNames(index); workspace_names[index] = (name == _makeDefaultWorkspaceName(index) ? "" : name); _trimWorkspaceNames(); wmSettings.set_strv("workspace-names", workspace_names); } } /** * getWorkspaceName: * @index (int): index of workspace * * Retrieves the name of the workspace @index * * Returns (string): name of workspace */ function getWorkspaceName(index) { let wsName = index < workspace_names.length ? workspace_names[index] : ""; wsName.trim(); return wsName.length > 0 ? wsName : _makeDefaultWorkspaceName(index); } /** * hasDefaultWorkspaceName: * @index (int): index of workspace * * Whether the workspace uses the default name * * Returns (boolean): whether the workspace uses the default name */ function hasDefaultWorkspaceName(index) { return getWorkspaceName(index) == _makeDefaultWorkspaceName(index); } function _addWorkspace() { if (dynamicWorkspaces) return false; global.screen.append_new_workspace(false, global.get_current_time()); return true; } function _removeWorkspace(workspace) { if (global.screen.n_workspaces == 1 || dynamicWorkspaces) return false; let index = workspace.index(); if (index < workspace_names.length) { workspace_names.splice (index,1); } _trimWorkspaceNames(); wmSettings.set_strv("workspace-names", workspace_names); global.screen.remove_workspace(workspace, global.get_current_time()); return true; } /** * moveWindowToNewWorkspace: * @metaWindow (Meta.Window): the window to be moved * @switchToNewWorkspace (boolean): whether or not to switch to the * new created workspace * * Moves the window to a new workspace. * * If @switchToNewWorkspace is true, it will switch to the new workspace * after moving the window */ function moveWindowToNewWorkspace(metaWindow, switchToNewWorkspace) { if (switchToNewWorkspace) { let targetCount = global.screen.n_workspaces + 1; let nnwId = global.screen.connect('notify::n-workspaces', function() { global.screen.disconnect(nnwId); if (global.screen.n_workspaces === targetCount) { let newWs = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1); newWs.activate(global.get_current_time()); } }); } metaWindow.change_workspace_by_index(global.screen.n_workspaces, true, global.get_current_time()); } function _checkWorkspaces() { if (!dynamicWorkspaces) return false; let i; let emptyWorkspaces = []; for (let i = 0; i < _workspaces.length; i++) { let lastRemoved = _workspaces[i]._lastRemovedWindow; if (lastRemoved && (lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN || lastRemoved.get_window_type() == Meta.WindowType.DIALOG || lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) emptyWorkspaces[i] = false; else emptyWorkspaces[i] = true; } let windows = global.get_window_actors(); for (let i = 0; i < windows.length; i++) { let win = windows[i]; if (win.get_meta_window().is_on_all_workspaces()) continue; let workspaceIndex = win.get_workspace(); emptyWorkspaces[workspaceIndex] = false; } // If we don't have an empty workspace at the end, add one if (!emptyWorkspaces[emptyWorkspaces.length -1]) { global.screen.append_new_workspace(false, global.get_current_time()); emptyWorkspaces.push(false); } let activeWorkspaceIndex = global.screen.get_active_workspace_index(); let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] && activeWorkspaceIndex < emptyWorkspaces.length - 1); // Don't enter the overview when removing multiple empty workspaces at startup let showOverview = (removingCurrentWorkspace && !emptyWorkspaces.every(function(x) { return x; })); if (removingCurrentWorkspace) { // "Merge" the empty workspace we are removing with the one at the end wm.blockAnimations(); } // Delete other empty workspaces; do it from the end to avoid index changes for (let i = emptyWorkspaces.length - 2; i >= 0; i--) { if (emptyWorkspaces[i]) global.screen.remove_workspace(_workspaces[i], global.get_current_time()); } if (removingCurrentWorkspace) { global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time()); wm.unblockAnimations(); } _checkWorkspacesId = 0; return false; } function _windowRemoved(workspace, window) { workspace._lastRemovedWindow = window; _queueCheckWorkspaces(); Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() { if (workspace._lastRemovedWindow == window) { workspace._lastRemovedWindow = null; _queueCheckWorkspaces(); } }); } function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) { // If the window left the primary monitor, that // might make that workspace empty if (monitorIndex == layoutManager.primaryIndex) _queueCheckWorkspaces(); } function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) { // If the window entered the primary monitor, that // might make that workspace non-empty if (monitorIndex == layoutManager.primaryIndex) _queueCheckWorkspaces(); } function _windowsRestacked() { // Figure out where the pointer is in case we lost track of // it during a grab. (In particular, if a trayicon popup menu // is dismissed, see if we need to close the message tray.) global.sync_pointer(); } function _queueCheckWorkspaces() { if (!dynamicWorkspaces) return false; if (_checkWorkspacesId == 0) _checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces); return true; } function _nWorkspacesChanged() { if (!dynamicWorkspaces) return false; let oldNumWorkspaces = _workspaces.length; let newNumWorkspaces = global.screen.n_workspaces; if (oldNumWorkspaces == newNumWorkspaces) return false; let lostWorkspaces = []; if (newNumWorkspaces > oldNumWorkspaces) { // Assume workspaces are only added at the end for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) _workspaces[w] = global.screen.get_workspace_by_index(w); for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { let workspace = _workspaces[w]; workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces); workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved); } } else { // Assume workspaces are only removed sequentially // (e.g. 2,3,4 - not 2,4,7) let removedIndex; let removedNum = oldNumWorkspaces - newNumWorkspaces; for (let w = 0; w < oldNumWorkspaces; w++) { let workspace = global.screen.get_workspace_by_index(w); if (_workspaces[w] != workspace) { removedIndex = w; break; } } let lostWorkspaces = _workspaces.splice(removedIndex, removedNum); lostWorkspaces.forEach(function(workspace) { workspace.disconnect(workspace._windowAddedId); workspace.disconnect(workspace._windowRemovedId); }); } _queueCheckWorkspaces(); return false; } /** * getThemeStylesheet: * * Get the theme CSS file that Cinnamon will load * * Returns (string): A file path that contains the theme CSS, * null if using the default */ function getThemeStylesheet() { return _cssStylesheet; } /** * setThemeStylesheet: * @cssStylesheet (string): A file path that contains the theme CSS, * set it to null to use the default * * Set the theme CSS file that Cinnamon will load */ function setThemeStylesheet(cssStylesheet) { _cssStylesheet = cssStylesheet; } /** * loadTheme: * * Reloads the theme CSS file */ function loadTheme() { let themeContext = St.ThemeContext.get_for_stage (global.stage); let theme = new St.Theme ({ fallback_stylesheet: _defaultCssStylesheet }); let stylesheetLoaded = false; if (_cssStylesheet != null) { stylesheetLoaded = theme.load_stylesheet(_cssStylesheet); } if (!stylesheetLoaded) { theme.load_stylesheet(_defaultCssStylesheet); if (_cssStylesheet != null) { global.logError("There was some problem parsing the theme: " + _cssStylesheet + ". Falling back to the default theme."); } } themeContext.set_theme (theme); } /** * notify: * @msg (string): A message * @details (string): Additional information to be * * Sends a notification */ function notify(msg, details) { let source = new MessageTray.SystemNotificationSource(); messageTray.add(source); let notification = new MessageTray.Notification(source, msg, details); notification.setTransient(true); source.notify(notification); } /** * criticalNotify: * @msg: A critical message * @details: Additional information */ function criticalNotify(msg, details, icon) { let source = new MessageTray.SystemNotificationSource(); messageTray.add(source); let notification = new MessageTray.Notification(source, msg, details, { icon: icon }); notification.setTransient(false); notification.setUrgency(MessageTray.Urgency.CRITICAL); source.notify(notification); } /** * warningNotify: * @msg: A warning message * @details: Additional information */ function warningNotify(msg, details, icon) { let source = new MessageTray.SystemNotificationSource(); messageTray.add(source); let notification = new MessageTray.Notification(source, msg, details, { icon: icon }); notification.setTransient(false); notification.setUrgency(MessageTray.Urgency.WARNING); source.notify(notification); } /** * notifyError: * @msg (string): An error message * @details (string): Additional information * * See cinnamon_global_notify_problem(). */ function notifyError(msg, details) { // Also print to stderr so it's logged somewhere if (details) log('error: ' + msg + ': ' + details); else log('error: ' + msg); notify(msg, details); } /** * _log: * @category (string): string message type ('info', 'error') * @msg (string): A message string * @...: Any further arguments are converted into JSON notation, * and appended to the log message, separated by spaces. * * Log a message into the LookingGlass error * stream. This is primarily intended for use by the * extension system as well as debugging. */ function _log(category, msg) { let text = msg; if (arguments.length > 2) { text += ': '; for (let i = 2; i < arguments.length; i++) { text += JSON.stringify(arguments[i]); if (i < arguments.length - 1) text += ' '; } } let out = {timestamp: new Date().getTime().toString(), category: category, message: text }; _errorLogStack.push(out); if (lookingGlass) lookingGlass.emitLogUpdate(); if (can_log) lg_log_file.write(renderLogLine(out), null); } /** * isError: * @obj (Object): the object to be tested * * Tests whether @obj is an error object * * Returns (boolean): whether @obj is an error object */ function isError(obj) { return typeof(obj) == 'object' && 'message' in obj && 'stack' in obj; } /** * _LogTraceFormatted: * @stack (string): the stack trace * * Prints the stack trace to the LookingGlass * error stream in a predefined format */ function _LogTraceFormatted(stack) { _log('trace', '\n<----------------\n' + stack + '---------------->'); } /** * _logTrace: * @msg (Error): An error object * * Prints a stack trace of the given object. * * If msg is an error, its stack-trace will be * printed. Otherwise, a stack-trace of the call * will be generated * * If you want to print the message of an Error * as well, use the other log functions instead. */ function _logTrace(msg) { if(isError(msg)) { _LogTraceFormatted(msg.stack); } else { try { throw new Error(); } catch (e) { // e.stack must have at least two lines, with the first being // _logTrace() (which we strip off), and the second being // our caller. let trace = e.stack.substr(e.stack.indexOf('\n') + 1); _LogTraceFormatted(trace); } } } /** * _logWarning: * @msg (Error/string): An error object or the message string * * Logs the message to the LookingGlass error * stream. * * If msg is an error, its stack-trace will be * printed. */ function _logWarning(msg) { if(isError(msg)) { _log('warning', msg.message); _LogTraceFormatted(msg.stack); } else { _log('warning', msg); } } /** * _logError: * @msg (string): (optional) The message string * @error (Error): (optional) The error object * * Logs the following (if present) to the * LookingGlass error stream: * - The message from the error object * - The stack trace of the error object * - The message @msg * * It can be called in the form of either _logError(msg), * _logError(error) or _logError(msg, error). */ function _logError(msg, error) { if(error && isError(error)) { _log('error', error.message); _LogTraceFormatted(error.stack); _log('error', msg); } else if(isError(msg)) { _log('error', msg.message); _LogTraceFormatted(msg.stack); } else { _log('error', msg); } } // If msg is an Error, its message will be printed as 'info' and its stack-trace will be printed as 'trace' /** * _logInfo: * @msg (Error/string): The error object or the message string * * Logs the message to the LookingGlass * error stream. If @msg is an Error object, * its stack trace will also be printed */ function _logInfo(msg) { if(isError(msg)) { _log('info', msg.message); _LogTraceFormatted(msg.stack); } else { _log('info', msg); } } /** * formatTime: * @d (Date): date object to be formatted * * Formats a date object into a ISO-8601 format (YYYY-MM-DDTHH:MM:SSZ) in UTC+0 * * Returns (string): a formatted string showing the date */ function formatTime(d) { return d.toISOString(); } /** * renderLogLine: * @line (dictionary): a log line * * Converts a log line object into a string * * Returns (string): line in the format CATEGORY t=TIME MESSAGE */ function renderLogLine(line) { return line.category + ' t=' + formatTime(new Date(parseInt(line.timestamp))) + ' ' + line.message + '\n'; } /** * logStackTrace: * @msg (string): message * * Logs the message @msg to stdout with backtrace */ function logStackTrace(msg) { try { throw new Error(); } catch (e) { // e.stack must have at least two lines, with the first being // logStackTrace() (which we strip off), and the second being // our caller. let trace = e.stack.substr(e.stack.indexOf('\n') + 1); log(msg ? (msg + '\n' + trace) : trace); } } /** * isWindowActorDisplayedOnWorkspace: * @win (Meta.WindowActor): window actor * @workspaceIndex (int): index of workspace * * Determines whether the window actor belongs to a specific workspace * * Returns (boolean): whether the window is on the workspace */ function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) { if (win.get_workspace() == workspaceIndex) {return true;} let mwin = win.get_meta_window(); return mwin && (mwin.is_on_all_workspaces() || (wm.workspacesOnlyOnPrimary && mwin.get_monitor() != layoutManager.primaryIndex) ); } /** * getWindowActorsForWorkspace: * @workspaceIndex (int): index of workspace * * Gets a list of actors on a workspace * * Returns (array): the array of window actors */ function getWindowActorsForWorkspace(workspaceIndex) { return global.get_window_actors().filter(function (win) { return isWindowActorDisplayedOnWorkspace(win, workspaceIndex); }); } // This function encapsulates hacks to make certain global keybindings // work even when we are in one of our modes where global keybindings // are disabled with a global grab. (When there is a global grab, then // all key events will be delivered to the stage, so ::captured-event // on the stage can be used for global keybindings.) function _stageEventHandler(actor, event) { if (modalCount == 0) return false; if (event.type() != Clutter.EventType.KEY_PRESS) { if(!popup_rendering_actor || event.type() != Clutter.EventType.BUTTON_RELEASE) return false; return (event.get_source() && popup_rendering_actor.contains(event.get_source())); } let symbol = event.get_key_symbol(); let keyCode = event.get_key_code(); let modifierState = Cinnamon.get_event_state(event); // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType let action = global.display.get_keybinding_action(keyCode, modifierState); if (action == Meta.KeyBindingAction.CUSTOM) { global.display.keybinding_action_invoke_by_code(keyCode, modifierState); } // Other bindings are only available when the overview is up and no modal dialog is present if (((!overview.visible && !expo.visible) || modalCount > 1)) return false; // This isn't a Meta.KeyBindingAction yet if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) { overview.hide(); expo.hide(); return true; } if (action == Meta.KeyBindingAction.SWITCH_PANELS) { //Used to call the ctrlalttabmanager in Gnome Shell return true; } switch (action) { // left/right would effectively act as synonyms for up/down if we enabled them; // but that could be considered confusing; we also disable them in the main view. // case Meta.KeyBindingAction.WORKSPACE_LEFT: wm.actionMoveWorkspaceLeft(); return true; case Meta.KeyBindingAction.WORKSPACE_RIGHT: wm.actionMoveWorkspaceRight(); return true; case Meta.KeyBindingAction.WORKSPACE_UP: overview.hide(); expo.hide(); return true; case Meta.KeyBindingAction.WORKSPACE_DOWN: overview.hide(); expo.hide(); return true; case Meta.KeyBindingAction.PANEL_RUN_DIALOG: case Meta.KeyBindingAction.COMMAND_2: getRunDialog().open(); return true; case Meta.KeyBindingAction.PANEL_MAIN_MENU: overview.hide(); expo.hide(); return true; } return false; } function _findModal(actor) { for (let i = 0; i < modalActorFocusStack.length; i++) { if (modalActorFocusStack[i].actor == actor) return i; } return -1; } /** * pushModal: * @actor (Clutter.Actor): actor which will be given keyboard focus * @timestamp (int): optional timestamp * @options (Meta.ModalOptions): (optional) flags to indicate that the pointer * is alrady grabbed * * Ensure we are in a mode where all keyboard and mouse input goes to * the stage, and focus @actor. Multiple calls to this function act in * a stacking fashion; the effect will be undone when an equal number * of popModal() invocations have been made. * * Next, record the current Clutter keyboard focus on a stack. If the * modal stack returns to this actor, reset the focus to the actor * which was focused at the time pushModal() was invoked. * * @timestamp is optionally used to associate the call with a specific user * initiated event. If not provided then the value of * global.get_current_time() is assumed. * * Returns (boolean): true iff we successfully acquired a grab or already had one */ function pushModal(actor, timestamp, options) { if (timestamp == undefined) timestamp = global.get_current_time(); if (modalCount == 0) { if (!global.begin_modal(timestamp, options ? options : 0)) { log('pushModal: invocation of begin_modal failed'); return false; } Meta.disable_unredirect_for_screen(global.screen); } global.set_stage_input_mode(Cinnamon.StageInputMode.FULLSCREEN); modalCount += 1; let actorDestroyId = actor.connect('destroy', function() { let index = _findModal(actor); if (index >= 0) popModal(actor); }); let record = { actor: actor, focus: global.stage.get_key_focus(), destroyId: actorDestroyId }; if (record.focus != null) { record.focusDestroyId = record.focus.connect('destroy', function() { record.focus = null; record.focusDestroyId = null; }); } modalActorFocusStack.push(record); global.stage.set_key_focus(actor); return true; } /** * popModal: * @actor (Clutter.Actor): actor passed to original invocation of pushModal(). * @timestamp (int): optional timestamp * * Reverse the effect of pushModal(). If this invocation is undoing * the topmost invocation, then the focus will be restored to the * previous focus at the time when pushModal() was invoked. * * @timestamp is optionally used to associate the call with a specific user * initiated event. If not provided then the value of * global.get_current_time() is assumed. */ function popModal(actor, timestamp) { if (timestamp == undefined) timestamp = global.get_current_time(); let focusIndex = _findModal(actor); if (focusIndex < 0) { global.stage.set_key_focus(null); global.end_modal(timestamp); global.set_stage_input_mode(Cinnamon.StageInputMode.NORMAL); throw new Error('incorrect pop'); } modalCount -= 1; let record = modalActorFocusStack[focusIndex]; record.actor.disconnect(record.destroyId); if (focusIndex == modalActorFocusStack.length - 1) { if (record.focus) record.focus.disconnect(record.focusDestroyId); global.stage.set_key_focus(record.focus); } else { let t = modalActorFocusStack[modalActorFocusStack.length - 1]; if (t.focus) t.focus.disconnect(t.focusDestroyId); // Remove from the middle, shift the focus chain up for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) { modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus; modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId; } } modalActorFocusStack.splice(focusIndex, 1); if (modalCount > 0) return; global.end_modal(timestamp); global.set_stage_input_mode(Cinnamon.StageInputMode.NORMAL); Meta.enable_unredirect_for_screen(global.screen); } /** * createLookingGlass: * * Obtains the looking glass object. Create if it does not exist * * Returns (LookingGlass.Melange): looking glass object */ function createLookingGlass() { if (lookingGlass == null) { lookingGlass = new LookingGlass.Melange(); } return lookingGlass; } /** * getRunDialog: * * Obtains the run dialog object. Create if it does not exist * * Returns (RunDialog.RunDialog): run dialog object */ function getRunDialog() { if (runDialog == null) { runDialog = new RunDialog.RunDialog(); } return runDialog; } /** * activateWindow: * @window (Meta.Window): the Meta.Window to activate * @time (int): (optional) current event time * @workspaceNum (int): (optional) window's workspace number * * Activates @window, switching to its workspace first if necessary, * and switching out of the overview if it's currently active */ function activateWindow(window, time, workspaceNum) { let activeWorkspaceNum = global.screen.get_active_workspace_index(); let windowWorkspaceNum = (workspaceNum !== undefined) ? workspaceNum : window.get_workspace().index(); if (!time) time = global.get_current_time(); if (windowWorkspaceNum != activeWorkspaceNum) { let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum); workspace.activate_with_focus(window, time); } else { window.activate(time); Mainloop.idle_add(function() { window.foreach_transient(function(win) { win.activate(time); }); }); } overview.hide(); expo.hide(); } // TODO - replace this timeout with some system to guess when the user might // be e.g. just reading the screen and not likely to interact. const DEFERRED_TIMEOUT_SECONDS = 20; var _deferredWorkData = {}; // Work scheduled for some point in the future var _deferredWorkQueue = []; // Work we need to process before the next redraw var _beforeRedrawQueue = []; // Counter to assign work ids var _deferredWorkSequence = 0; var _deferredTimeoutId = 0; function _runDeferredWork(workId) { if (!_deferredWorkData[workId]) return; let index = _deferredWorkQueue.indexOf(workId); if (index < 0) return; _deferredWorkQueue.splice(index, 1); _deferredWorkData[workId].callback(); if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) { Mainloop.source_remove(_deferredTimeoutId); _deferredTimeoutId = 0; } } function _runAllDeferredWork() { while (_deferredWorkQueue.length > 0) _runDeferredWork(_deferredWorkQueue[0]); } function _runBeforeRedrawQueue() { for (let i = 0; i < _beforeRedrawQueue.length; i++) { let workId = _beforeRedrawQueue[i]; _runDeferredWork(workId); } _beforeRedrawQueue = []; } function _queueBeforeRedraw(workId) { _beforeRedrawQueue.push(workId); if (_beforeRedrawQueue.length == 1) { Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () { _runBeforeRedrawQueue(); return false; }); } } /** * initializeDeferredWork: * @actor (Clutter.Actor): A #ClutterActor * @callback (function): Function to invoke to perform work * * This function sets up a callback to be invoked when either the * given actor is mapped, or after some period of time when the machine * is idle. This is useful if your actor isn't always visible on the * screen (for example, all actors in the overview), and you don't want * to consume resources updating if the actor isn't actually going to be * displaying to the user. * * Note that queueDeferredWork is called by default immediately on * initialization as well, under the assumption that new actors * will need it. * * Returns (string): A string work identifer */ function initializeDeferredWork(actor, callback, props) { // Turn into a string so we can use as an object property let workId = '' + (++_deferredWorkSequence); _deferredWorkData[workId] = { 'actor': actor, 'callback': callback }; actor.connect('notify::mapped', function () { if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0)) return; _queueBeforeRedraw(workId); }); actor.connect('destroy', function() { let index = _deferredWorkQueue.indexOf(workId); if (index >= 0) _deferredWorkQueue.splice(index, 1); delete _deferredWorkData[workId]; }); queueDeferredWork(workId); return workId; } /** * queueDeferredWork: * @workId (string): work identifier * * Ensure that the work identified by @workId will be * run on map or timeout. You should call this function * for example when data being displayed by the actor has * changed. */ function queueDeferredWork(workId) { let data = _deferredWorkData[workId]; if (!data) { global.logError('invalid work id: ' + workId); return; } if (_deferredWorkQueue.indexOf(workId) < 0) _deferredWorkQueue.push(workId); if (data.actor.mapped) { _queueBeforeRedraw(workId); return; } else if (_deferredTimeoutId == 0) { _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () { _runAllDeferredWork(); _deferredTimeoutId = 0; return false; }); } } /** * isInteresting: * @metaWindow (Meta.Window): the window to be tested * * Determines whether a window is "interesting", i.e. * ones to be displayed in alt-tab, window list etc. * * Returns (boolean): whether the window is interesting */ function isInteresting(metaWindow) { if (metaWindow.get_title() == "JavaEmbeddedFrame") return false; if (tracker.is_window_interesting(metaWindow)) { // The nominal case. return true; } // The rest of this function is devoted to discovering "orphan" windows // (dialogs without an associated app, e.g., the Logout dialog). if (tracker.get_window_app(metaWindow)) { // orphans don't have an app! return false; } let type = metaWindow.get_window_type(); return type === Meta.WindowType.DIALOG || type === Meta.WindowType.MODAL_DIALOG; } /** * getTabList: * @workspaceOpt (Meta.Workspace): (optional) workspace, defaults to global.screen.get_active_workspace() * @screenOpt (Meta.Screen): (optional) screen, defaults to global.screen * * Return a list of the interesting windows on a workspace (by default, * the active workspace). * The list will include app-less dialogs. * * Returns (array): list of windows */ function getTabList(workspaceOpt, screenOpt) { let screen = screenOpt || global.screen; let display = screen.get_display(); let workspace = workspaceOpt || screen.get_active_workspace(); let windows = []; // the array to return let allwindows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen, workspace); let registry = {}; // to avoid duplicates for (let i = 0; i < allwindows.length; ++i) { let window = allwindows[i]; if (isInteresting(window)) { let seqno = window.get_stable_sequence(); if (!registry[seqno]) { windows.push(window); registry[seqno] = true; // there may be duplicates in the list (rare) } } } return windows; } Cinnamon-2.8.6/js/ui/appSwitcher/0000775000175000017500000000000012625666430015551 5ustar fabiofabioCinnamon-2.8.6/js/ui/appSwitcher/appSwitcher3D.js0000664000175000017500000002622012625666430020571 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const Mainloop = imports.mainloop; const AppSwitcher = imports.ui.appSwitcher.appSwitcher; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const INITIAL_DELAY_TIMEOUT = 150; const CHECK_DESTROYED_TIMEOUT = 100; const TRANSITION_TYPE = 'easeOutQuad'; const ICON_SIZE = 64; const ICON_SIZE_BIG = 128; const ICON_TITLE_SPACING = 10; const PREVIEW_SCALE = 0.5; const TITLE_POSITION = 7/8; // percent position const ANIMATION_TIME = 0.25; // seconds const SWITCH_TIME_DELAY = 100; // milliseconds const DIM_FACTOR = 0.4; // percent function AppSwitcher3D() { this._init.apply(this, arguments); } AppSwitcher3D.prototype = { __proto__: AppSwitcher.AppSwitcher.prototype, _init: function() { AppSwitcher.AppSwitcher.prototype._init.apply(this, arguments); this._windowTitle = null; this._icon = null; this._lastTime = 0; this._background = Meta.BackgroundActor.new_for_screen(global.screen); this._background.hide(); global.overlay_group.add_actor(this._background); // create a container for all our widgets this.actor = new St.Group({ visible: true, reactive: true, }); this.actor.hide(); this.previewActor = new St.Group({ visible: true, reactive: true, }); this.actor.add_actor(this.previewActor); Main.uiGroup.add_actor(this.actor); this._setupModal(); }, _show: function() { this._enableMonitorFix(); let monitor = this._activeMonitor; this.actor.set_position(monitor.x, monitor.y); this.actor.set_size(monitor.width, monitor.height); // create previews this._createList(); // hide windows and show Coverflow actors global.window_group.hide(); this.actor.show(); this._background.show(); Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(false); }); Tweener.addTween(this._background, { dim_factor: DIM_FACTOR, time: ANIMATION_TIME, transition: TRANSITION_TYPE }); this._initialDelayTimeoutId = 0; this._next(); }, _hidePreviews: function(endOpacity) { let monitor = this._activeMonitor; // preview windows let currentWorkspace = global.screen.get_active_workspace(); for (let i in this._previews) { let preview = this._previews[i]; let metaWin = this._windows[i]; let compositor = this._windows[i].get_compositor_private(); if (i != this._currentIndex) preview.lower_bottom(); let rotation_vertex_x = 0.0; if (preview.get_anchor_point_gravity() == Clutter.Gravity.EAST) { rotation_vertex_x = preview.width / 2; } else if (preview.get_anchor_point_gravity() == Clutter.Gravity.WEST) { rotation_vertex_x = -preview.width / 2; } preview.move_anchor_point_from_gravity(compositor.get_anchor_point_gravity()); preview.rotation_center_y = new Clutter.Vertex({ x: rotation_vertex_x, y: 0.0, z: 0.0 }); Tweener.addTween(preview, { opacity: (!metaWin.minimized && metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) ? endOpacity : 0, x: ((metaWin.minimized) ? 0 : compositor.x) - monitor.x, y: ((metaWin.minimized) ? 0 : compositor.y) - monitor.y, width: (metaWin.minimized) ? 0 : compositor.width, height: (metaWin.minimized) ? 0 : compositor.height, rotation_angle_y: 0.0, time: ANIMATION_TIME, transition: TRANSITION_TYPE, onComplete: Lang.bind(preview, preview.destroy), }); } }, _hide: function() { this._hidePreviews(255); // window title and icon if(this._windowTitle) { this._windowTitle.hide(); this._applicationIconBox.hide(); } // panels Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(true); }); // background Tweener.removeTweens(this._background); Tweener.addTween(this._background, { dim_factor: 1.0, time: ANIMATION_TIME, transition: TRANSITION_TYPE, onComplete: Lang.bind(this, this._destroyActors), }); this._disableMonitorFix(); }, _checkSwitchTime: function() { let t = new Date().getTime(); if(t - this._lastTime < SWITCH_TIME_DELAY) return false; this._lastTime = t; return true; }, _onWorkspaceSelected: function() { this._hidePreviews(0); this._windows = AppSwitcher.getWindowsForBinding(this._binding); this._currentIndex = this._windows.indexOf(global.display.focus_window); // create previews this._createList(); this._next(); }, _createList: function() { let monitor = this._activeMonitor; let currentWorkspace = global.screen.get_active_workspace(); this._previews = []; for (let i in this._windows) { let metaWin = this._windows[i]; let compositor = this._windows[i].get_compositor_private(); if (compositor) { let texture = compositor.get_texture(); let [width, height] = texture.get_size(); let scale = 1.0; let previewWidth = monitor.width * PREVIEW_SCALE; let previewHeight = monitor.height * PREVIEW_SCALE; if (width > previewWidth || height > previewHeight) scale = Math.min(previewWidth / width, previewHeight / height); let preview = new St.Button({ opacity: (!metaWin.minimized && metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) ? 255 : 0, reactive: true, anchor_gravity: Clutter.Gravity.CENTER, x: ((metaWin.minimized) ? 0 : compositor.x + compositor.width / 2) - monitor.x, y: ((metaWin.minimized) ? 0 : compositor.y + compositor.height / 2) - monitor.y }); preview.target_width = Math.round(width * scale); preview.target_height = Math.round(height * scale); preview.target_width_side = preview.target_width * 2/3; preview.target_height_side = preview.target_height; preview.set_child(new Clutter.Clone({ source: texture })); preview.metaWindow = metaWin; preview.connect('clicked', Lang.bind(this, this._cloneClicked)); this._previews.push(preview); this.previewActor.add_actor(preview); } } this._adaptClones(); }, _adaptClones: function() { }, _cloneClicked: function(actor) { this._currentIndex = this._previews.indexOf(actor); this._activateSelected(); }, _setCurrentWindow: function(window) { let monitor = this._activeMonitor; // window title label if (this._windowTitle) { Tweener.addTween(this._windowTitle, { opacity: 0, time: ANIMATION_TIME, transition: TRANSITION_TYPE, onComplete: Lang.bind(this.actor, this.actor.remove_actor, this._windowTitle), }); } this._windowTitle = new St.Label({ style_class: 'switcher-list', text: this._windows[this._currentIndex].get_title(), opacity: 0 }); // ellipsize if title is too long this._windowTitle.set_style("max-width:" + (monitor.width - 200) + "px;font-size: 14px;font-weight: bold; padding: 14px;"); this._windowTitle.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.actor.add_actor(this._windowTitle); Tweener.addTween(this._windowTitle, { opacity: 255, time: ANIMATION_TIME, transition: TRANSITION_TYPE, }); let cx = Math.round((monitor.width + (ICON_SIZE * global.ui_scale) + (ICON_TITLE_SPACING * global.ui_scale)) / 2); let cy = Math.round(monitor.height * TITLE_POSITION); this._windowTitle.x = cx - Math.round(this._windowTitle.get_width()/2); this._windowTitle.y = cy - Math.round(this._windowTitle.get_height()/2); // window icon if (this._applicationIconBox) { Tweener.addTween(this._applicationIconBox, { opacity: 0, time: ANIMATION_TIME, transition: TRANSITION_TYPE, onComplete: Lang.bind(this.actor, this.actor.remove_actor, this._applicationIconBox), }); } let app = this._tracker.get_window_app(this._windows[this._currentIndex]); this._icon = app ? app.create_icon_texture(ICON_SIZE) : null; if (!this._icon) { this._icon = new St.Icon({ icon_name: 'applications-other', icon_type: St.IconType.FULLCOLOR, icon_size: ICON_SIZE }); } this._applicationIconBox = new St.Bin({ style_class: 'window-iconbox', opacity: 0, x: Math.round(this._windowTitle.x - (ICON_SIZE * global.ui_scale) - (ICON_TITLE_SPACING * global.ui_scale)), y: Math.round(cy - (ICON_SIZE * global.ui_scale) / 2 ) }); this._applicationIconBox.add_actor(this._icon); this.actor.add_actor(this._applicationIconBox); Tweener.addTween(this._applicationIconBox, { opacity: 255, time: ANIMATION_TIME, transition: TRANSITION_TYPE, }); }, _destroyActors: function() { global.overlay_group.remove_actor(this._background); Main.uiGroup.remove_actor(this.actor); this.actor.destroy(); // show all window actors global.window_group.show(); }, _onDestroy: function() { this._windowTitle = null; this._icon = null; this._applicationIconBox = null; this._previews = null; }, _enableMonitorFix: function() { if(global.screen.get_n_monitors() < 2) return; this._monitorFix = true; this._oldWidth = global.stage.width; this._oldHeight = global.stage.height; let width = 2 * (this._activeMonitor.x + this._activeMonitor.width/2); let height = 2 * (this._activeMonitor.y + this._activeMonitor.height/2); global.stage.set_size(width, height); }, _disableMonitorFix: function() { if(this._monitorFix) { global.stage.set_size(this._oldWidth, this._oldHeight); this._monitorFix = false; } } }; Cinnamon-2.8.6/js/ui/appSwitcher/coverflowSwitcher.js0000664000175000017500000001660612625666430021637 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; const Config = imports.misc.config; const Tweener = imports.ui.tweener; const AppSwitcher3D = imports.ui.appSwitcher.appSwitcher3D; const TRANSITION_TYPE = 'easeOutQuad'; const SIDE_ANGLE = 60; const BLEND_OUT_ANGLE = 30; function appendParams(base, extra) { for (let key in extra) { base[key] = extra[key]; } } function CoverflowSwitcher() { this._init.apply(this, arguments); } CoverflowSwitcher.prototype = { __proto__: AppSwitcher3D.AppSwitcher3D.prototype, _init: function() { AppSwitcher3D.AppSwitcher3D.prototype._init.apply(this, arguments); let monitor = this._activeMonitor; this._yOffset = monitor.height / 2; this._xOffsetLeft = monitor.width * 0.1; this._xOffsetRight = monitor.width - this._xOffsetLeft; this._xOffsetCenter = monitor.width / 2; }, _selectNext: function() { if (this._currentIndex == this._windows.length - 1) { this._currentIndex = 0; this._flipStack(Clutter.Gravity.WEST); } else { this._currentIndex = this._currentIndex + 1; this._updateList(); } }, _selectPrevious: function() { if (this._currentIndex == 0) { this._currentIndex = this._windows.length-1; this._flipStack(Clutter.Gravity.EAST); } else { this._currentIndex = this._currentIndex - 1; this._updateList(); } }, _flipStack: function(gravity) { this._looping = true; let xOffset, angle; if(gravity == Clutter.Gravity.WEST) { xOffset = -this._xOffsetLeft; angle = BLEND_OUT_ANGLE; } else { xOffset = this._activeMonitor.width + this._xOffsetLeft; angle = -BLEND_OUT_ANGLE; } let animation_time = AppSwitcher3D.ANIMATION_TIME * 2/3; for (let i in this._previews) { let preview = this._previews[i]; preview._cfIsLast = (i == this._windows.length-1); this._animatePreviewToSide(preview, i, gravity, xOffset, { opacity: 0, rotation_angle_y: angle, time: animation_time, transition: TRANSITION_TYPE, onCompleteParams: [preview, i, gravity], onComplete: this._onFlipIn, onCompleteScope: this, }); } }, _onFlipIn: function(preview, index, gravity) { let xOffsetStart, xOffsetEnd, angleStart, angleEnd; if(gravity == Clutter.Gravity.WEST) { xOffsetStart = this._activeMonitor.width + this._xOffsetLeft; xOffsetEnd = this._xOffsetRight; angleStart = -BLEND_OUT_ANGLE; angleEnd = -SIDE_ANGLE; } else { xOffsetStart = -this._xOffsetLeft; xOffsetEnd = this._xOffsetLeft; angleStart = BLEND_OUT_ANGLE; angleEnd = SIDE_ANGLE; } let animation_time = AppSwitcher3D.ANIMATION_TIME * 2/3; preview.rotation_angle_y = angleStart; preview.x = xOffsetStart + 50 * (index - this._currentIndex); let lastExtraParams = { onCompleteParams: [], onComplete: this._onFlipComplete, onCompleteScope: this }; let oppositeGravity = (gravity == Clutter.Gravity.WEST) ? Clutter.Gravity.EAST : Clutter.Gravity.WEST; if (index == this._currentIndex) { preview.raise_top(); let extraParams = preview._cfIsLast ? lastExtraParams : null; this._animatePreviewToMid(preview, oppositeGravity, animation_time, extraParams); } else { if(gravity == Clutter.Gravity.EAST) preview.raise_top(); else preview.lower_bottom(); let extraParams = { opacity: 255, rotation_angle_y: angleEnd, time: animation_time, transition: TRANSITION_TYPE }; if (preview._cfIsLast) appendParams(extraParams, lastExtraParams); this._animatePreviewToSide(preview, index, oppositeGravity, xOffsetEnd, extraParams); } }, _onFlipComplete: function() { this._looping = false; if(this._requiresUpdate == true) { this._requiresUpdate = false; this._updateList(); } }, _animatePreviewToMid: function(preview, oldGravity, animation_time, extraParams) { let rotation_vertex_x = (oldGravity == Clutter.Gravity.EAST) ? preview.width / 2 : -preview.width / 2; preview.move_anchor_point_from_gravity(Clutter.Gravity.CENTER); preview.rotation_center_y = new Clutter.Vertex({ x: rotation_vertex_x, y: 0.0, z: 0.0 }); preview.raise_top(); let tweenParams = { opacity: 255, x: this._xOffsetCenter, y: this._yOffset, width: preview.target_width, height: preview.target_height, rotation_angle_y: 0.0, time: animation_time, transition: TRANSITION_TYPE }; if(extraParams) appendParams(tweenParams, extraParams); Tweener.addTween(preview, tweenParams); }, _animatePreviewToSide: function(preview, index, gravity, xOffset, extraParams) { preview.move_anchor_point_from_gravity(gravity); preview.rotation_center_y = new Clutter.Vertex({ x: 0.0, y: 0.0, z: 0.0 }); let tweenParams = { x: xOffset + 50 * (index - this._currentIndex), y: this._yOffset, width: Math.max(preview.target_width_side * (10 - Math.abs(index - this._currentIndex)) / 10, 0), height: Math.max(preview.target_height_side * (10 - Math.abs(index - this._currentIndex)) / 10, 0), }; appendParams(tweenParams, extraParams); Tweener.addTween(preview, tweenParams); }, _updateList: function() { if(this._looping) { this._requiresUpdate = true; return; } let monitor = this._activeMonitor; let animation_time = AppSwitcher3D.ANIMATION_TIME; // preview windows for (let i in this._previews) { let preview = this._previews[i]; if (i == this._currentIndex) { this._animatePreviewToMid(preview, preview.get_anchor_point_gravity(), animation_time); } else if (i < this._currentIndex) { preview.raise_top(); this._animatePreviewToSide(preview, i, Clutter.Gravity.WEST, this._xOffsetLeft, { opacity: 255, rotation_angle_y: SIDE_ANGLE, time: animation_time, transition: TRANSITION_TYPE }); } else if (i > this._currentIndex) { preview.lower_bottom(); this._animatePreviewToSide(preview, i, Clutter.Gravity.EAST, this._xOffsetRight, { opacity: 255, rotation_angle_y: -SIDE_ANGLE, time: animation_time, transition: TRANSITION_TYPE }); } } }, }; Cinnamon-2.8.6/js/ui/appSwitcher/classicSwitcher.js0000664000175000017500000011737112625666430021253 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const Mainloop = imports.mainloop; const AppSwitcher = imports.ui.appSwitcher.appSwitcher; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const WindowUtils = imports.misc.windowUtils; const POPUP_APPICON_SIZE = 96; const POPUP_SCROLL_TIME = 0.10; // seconds const POPUP_DELAY_TIMEOUT = 150; // milliseconds const POPUP_FADE_OUT_TIME = 0.1; // seconds const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds const THUMBNAIL_DEFAULT_SIZE = 256; const THUMBNAIL_POPUP_TIME = 300; // milliseconds const THUMBNAIL_FADE_TIME = 0.1; // seconds const PREVIEW_DELAY_TIMEOUT = 0; // milliseconds var PREVIEW_SWITCHER_FADEOUT_TIME = 0.2; // seconds const iconSizes = [96, 64, 48, 32, 22]; function mod(a, b) { return (a + b) % b; } function ClassicSwitcher() { this._init.apply(this, arguments); } ClassicSwitcher.prototype = { __proto__: AppSwitcher.AppSwitcher.prototype, _init: function() { AppSwitcher.AppSwitcher.prototype._init.apply(this, arguments); this.actor = new Cinnamon.GenericContainer({ name: 'altTabPopup', reactive: true, visible: false }); this._thumbnailTimeoutId = 0; this.thumbnailsVisible = false; this._displayPreviewTimeoutId = 0; Main.uiGroup.add_actor(this.actor); if (!this._setupModal()) return; let styleSettings = global.settings.get_string("alttab-switcher-style"); let features = styleSettings.split('+'); this._iconsEnabled = features.indexOf('icons') !== -1; this._previewEnabled = features.indexOf('preview') !== -1; this._thumbnailsEnabled = features.indexOf('thumbnails') !== -1; if (!this._iconsEnabled && !this._previewEnabled && !this._thumbnailsEnabled) this._iconsEnabled = true; this._showThumbnails = this._thumbnailsEnabled && !this._iconsEnabled; this._showArrows = this._thumbnailsEnabled && this._iconsEnabled; this._updateList(0); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); // Need to force an allocation so we can figure out whether we // need to scroll when selecting this.actor.opacity = 0; this.actor.show(); this.actor.get_allocation_box(); }, _getPreferredWidth: function (actor, forHeight, alloc) { alloc.min_size = global.screen_width; alloc.natural_size = global.screen_width; }, _getPreferredHeight: function (actor, forWidth, alloc) { alloc.min_size = global.screen_height; alloc.natural_size = global.screen_height; }, _allocate: function (actor, box, flags) { let childBox = new Clutter.ActorBox(); let monitor = this._activeMonitor; let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT); let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT); let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM); let vPadding = this.actor.get_theme_node().get_vertical_padding(); let hPadding = leftPadding + rightPadding; // Allocate the appSwitcher // We select a size based on an icon size that does not overflow the screen let [childMinHeight, childNaturalHeight] = this._appList.actor.get_preferred_height(monitor.width - hPadding); let [childMinWidth, childNaturalWidth] = this._appList.actor.get_preferred_width(childNaturalHeight); childBox.x1 = Math.max(monitor.x + leftPadding, monitor.x + Math.floor((monitor.width - childNaturalWidth) / 2)); childBox.x2 = Math.min(monitor.x + monitor.width - rightPadding, childBox.x1 + childNaturalWidth); childBox.y1 = monitor.y + Math.floor((monitor.height - childNaturalHeight) / 2); childBox.y2 = childBox.y1 + childNaturalHeight; this._appList.actor.allocate(childBox, flags); // Allocate the thumbnails // We try to avoid overflowing the screen so we base the resulting size on // those calculations if (this._thumbnails && this._appIcons.length > 0) { let icon = this._appIcons[this._currentIndex].actor; let [posX, posY] = icon.get_transformed_position(); let thumbnailCenter = posX + icon.width / 2; let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1); childBox.x1 = Math.max(monitor.x + leftPadding, Math.floor(thumbnailCenter - childNaturalWidth / 2)); if (childBox.x1 + childNaturalWidth > monitor.x + monitor.width - hPadding) { let offset = childBox.x1 + childNaturalWidth - monitor.width + hPadding; childBox.x1 = Math.max(monitor.x + leftPadding, childBox.x1 - offset - hPadding); } let spacing = this.actor.get_theme_node().get_length('spacing'); childBox.x2 = childBox.x1 + childNaturalWidth; if (childBox.x2 > monitor.x + monitor.width - rightPadding) childBox.x2 = monitor.x + monitor.width - rightPadding; childBox.y1 = this._appList.actor.allocation.y2 + spacing; this._thumbnails.addClones(monitor.y + monitor.height - bottomPadding - childBox.y1); let [childMinHeight, childNaturalHeight] = this._thumbnails.actor.get_preferred_height(-1); childBox.y2 = childBox.y1 + childNaturalHeight; this._thumbnails.actor.allocate(childBox, flags); } }, _show: function() { Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(false); }); this.actor.opacity = 255; this._initialDelayTimeoutId = 0; this._next(); }, _hide: function() { // window title and icon if(this._windowTitle) { this._windowTitle.hide(); this._applicationIconBox.hide(); } // panels Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(true); }); Tweener.addTween(this.actor, { opacity: 0, time: POPUP_FADE_OUT_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, this._destroyActors) }); }, _destroyActors: function() { Main.uiGroup.remove_actor(this.actor); this.actor.destroy(); }, _updateList: function(direction) { if(direction !== 0) return; if (this._appList) { this._clearPreview(); this._destroyThumbnails(); this.actor.remove_actor(this._appList.actor); this._appList.actor.destroy(); } this._appList = new AppList(this._windows, this._showThumbnails, this._showArrows, this._activeMonitor); this.actor.add_actor(this._appList.actor); if (!this._iconsEnabled && !this._thumbnailsEnabled) { this._appList.actor.hide(); } this._appList.connect('item-activated', Lang.bind(this, this._appActivated)); this._appList.connect('item-entered', Lang.bind(this, this._appEntered)); this._appIcons = this._appList.icons; this.actor.get_allocation_box(); }, _selectNext: function() { if (this._currentIndex == this._windows.length - 1) { this._currentIndex = 0; } else { this._currentIndex = this._currentIndex + 1; } }, _selectPrevious: function() { if (this._currentIndex == 0) { this._currentIndex = this._windows.length-1; } else { this._currentIndex = this._currentIndex - 1; } }, _onWorkspaceSelected: function() { this._windows = AppSwitcher.getWindowsForBinding(this._binding); this._currentIndex = 0; this._updateList(0); this._select(0); }, _setCurrentWindow: function(window) { this._appList.highlight(this._currentIndex, false); this._doWindowPreview(); this._destroyThumbnails(); if (this._thumbnailTimeoutId != 0) { Mainloop.source_remove(this._thumbnailTimeoutId); this._thumbnailTimeoutId = 0; } if (this._showArrows) { this._thumbnailTimeoutId = Mainloop.timeout_add( THUMBNAIL_POPUP_TIME, Lang.bind(this, function() { if (!this._thumbnails) this._createThumbnails(); this._thumbnails.highlight(0, false); this._thumbnailTimeoutId = 0; })); } }, _onDestroy: function() { if (this._thumbnailTimeoutId != 0) { Mainloop.source_remove(this._thumbnailTimeoutId); this._thumbnailTimeoutId = 0; } if (this._displayPreviewTimeoutId != 0) { Mainloop.source_remove(this._displayPreviewTimeoutId); this._displayPreviewTimeoutId = 0; } }, _appActivated : function(appSwitcher, n) { this._activateSelected(); }, _appEntered : function(appSwitcher, n) { if (!this._mouseActive) return; this._select(n); }, _windowActivated : function(thumbnailList, n) { this._activateSelected(); }, _clearPreview: function() { if (this._previewClones) { for (let i = 0; i < this._previewClones.length; ++i) { let clone = this._previewClones[i]; Tweener.addTween(clone, { opacity: 0, time: PREVIEW_SWITCHER_FADEOUT_TIME / 4, transition: 'linear', onCompleteScope: this, onComplete: function() { this.actor.remove_actor(clone); clone.destroy(); } }); } this._previewClones = null; } }, _doWindowPreview: function() { if (!this._previewEnabled || this._windows.length < 1) { return; } // Use a cancellable timeout to avoid flickering effect when tabbing rapidly through the set. if (this._displayPreviewTimeoutId) { Mainloop.source_remove(this._displayPreviewTimeoutId); this._displayPreviewTimeoutId = 0; } let delay = PREVIEW_DELAY_TIMEOUT; this._displayPreviewTimeoutId = Mainloop.timeout_add(delay, Lang.bind(this, this._showWindowPreview)); }, _showWindowPreview: function() { this._displayPreviewTimeoutId = 0; let childBox = new Clutter.ActorBox(); let lastClone = null; let previewClones = []; let window = this._windows[this._currentIndex]; let clones = WindowUtils.createWindowClone(window, 0, 0, true, false); for (let i = 0; i < clones.length; i++) { let clone = clones[i]; previewClones.push(clone.actor); this.actor.add_actor(clone.actor); let [width, height] = clone.actor.get_size(); childBox.x1 = clone.x; childBox.x2 = clone.x + width; childBox.y1 = clone.y; childBox.y2 = clone.y + height; clone.actor.allocate(childBox, 0); clone.actor.lower(this._appList.actor); if (lastClone) { lastClone.lower(clone.actor); } lastClone = clone.actor; } this._clearPreview(); this._previewClones = previewClones; if (!this._previewBackdrop) { let backdrop = this._previewBackdrop = new St.Bin({style_class: 'switcher-preview-backdrop'}); this.actor.add_actor(backdrop); // Make sure that the backdrop does not overlap the switcher. backdrop.lower(this._appList.actor); backdrop.lower(lastClone); childBox.x1 = this.actor.x; childBox.x2 = this.actor.x + this.actor.width; childBox.y1 = this.actor.y; childBox.y2 = this.actor.y + this.actor.height; backdrop.allocate(childBox, 0); backdrop.opacity = 0; Tweener.addTween(backdrop, { opacity: 255, time: PREVIEW_SWITCHER_FADEOUT_TIME / 4, transition: 'linear' }); } }, _destroyThumbnails : function() { if (!this._thumbnails) { return; } let thumbnailsActor = this._thumbnails.actor; this._thumbnails = null; this.actor.remove_actor(thumbnailsActor); thumbnailsActor.destroy(); this.thumbnailsVisible = false; }, _createThumbnails : function() { this._thumbnails = new ThumbnailList ([this._windows[this._currentIndex]], this._activeMonitor); this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated)); this.actor.add_actor(this._thumbnails.actor); // Need to force an allocation so we can figure out whether we // need to scroll when selecting this._thumbnails.actor.get_allocation_box(); this._thumbnails.actor.opacity = 0; Tweener.addTween(this._thumbnails.actor, { opacity: 255, time: THUMBNAIL_FADE_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; }) }); } }; function AppIcon(window, showThumbnail) { this._init(window, showThumbnail); } AppIcon.prototype = { _init: function(window, showThumbnail) { this.window = window; this.showThumbnail = showThumbnail; let tracker = Cinnamon.WindowTracker.get_default(); this.app = tracker.get_window_app(window); this.actor = new St.BoxLayout({ style_class: 'alt-tab-app', vertical: true }); this.icon = null; this._iconBin = new St.Bin(); this.actor.add(this._iconBin, { x_fill: false, y_fill: false } ); let title = window.get_title(); if (title) { if (window.minimized) { this.label = new St.Label({ text: "[" + title + "]"}); let contrast_effect = new Clutter.BrightnessContrastEffect(); contrast_effect.set_brightness(-0.5, -0.5, -0.5); this._iconBin.add_effect(contrast_effect); } else if (window.tile_type == Meta.WindowTileType.TILED) { this.label = new St.Label({ text: "|" + title }); } else if (window.tile_type == Meta.WindowTileType.SNAPPED) { this.label = new St.Label({ text: "||" + title }); } else { this.label = new St.Label({ text: title }); } let bin = new St.Bin({ x_align: St.Align.MIDDLE }); bin.add_actor(this.label); this.actor.add(bin); } else { this.label = new St.Label({ text: this.app ? this.app.get_name() : window.title }); this.actor.add(this.label, { x_fill: false }); } }, set_size: function(size) { if (this.showThumbnail){ this.icon = new St.Group(); let clones = WindowUtils.createWindowClone(this.window, size, size, true, true); for (let i in clones) { let clone = clones[i]; this.icon.add_actor(clone.actor); // the following 2 lines are used when cloning without positions (param #4 = false) //let [width, height] = clone.actor.get_size(); //clone.actor.set_position(Math.round((size - width) / 2), Math.round((size - height) / 2)); clone.actor.set_position(clone.x, clone.y); } } else { this.icon = this.app ? this.app.create_icon_texture(size) : new St.Icon({ icon_name: 'application-default-icon', icon_type: St.IconType.FULLCOLOR, icon_size: size }); } this._iconBin.set_size(size, size); this._iconBin.child = this.icon; } }; function SwitcherList(squareItems, activeMonitor) { this._init(squareItems, activeMonitor); } SwitcherList.prototype = { _init : function(squareItems, activeMonitor) { this.actor = new Cinnamon.GenericContainer({ style_class: 'switcher-list' }); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocateTop)); // Here we use a GenericContainer so that we can force all the // children except the separator to have the same width. this._list = new Cinnamon.GenericContainer({ style_class: 'switcher-list-item-container' }); this._list.spacing = 0; this._list.connect('style-changed', Lang.bind(this, function() { this._list.spacing = this._list.get_theme_node().get_length('spacing'); })); this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this._list.connect('allocate', Lang.bind(this, this._allocate)); this._clipBin = new St.Bin({style_class: 'cbin'}); this._clipBin.child = this._list; this.actor.add_actor(this._clipBin); this._leftGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-left', vertical: true}); this._rightGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-right', vertical: true}); this.actor.add_actor(this._leftGradient); this.actor.add_actor(this._rightGradient); // Those arrows indicate whether scrolling in one direction is possible this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow', pseudo_class: 'highlighted' }); this._leftArrow.connect('repaint', Lang.bind(this, function() { _drawArrow(this._leftArrow, St.Side.LEFT); })); this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow', pseudo_class: 'highlighted' }); this._rightArrow.connect('repaint', Lang.bind(this, function() { _drawArrow(this._rightArrow, St.Side.RIGHT); })); this.actor.add_actor(this._leftArrow); this.actor.add_actor(this._rightArrow); this._items = []; this._highlighted = -1; this._separator = null; this._squareItems = squareItems; this._minSize = 0; this._scrollableRight = true; this._scrollableLeft = false; this._activeMonitor = activeMonitor; }, _allocateTop: function(actor, box, flags) { let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT); let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT); let childBox = new Clutter.ActorBox(); let scrollable = this._minSize > box.x2 - box.x1; this._clipBin.allocate(box, flags); childBox.x1 = 0; childBox.y1 = 0; childBox.x2 = this._leftGradient.width; childBox.y2 = this.actor.height; this._leftGradient.allocate(childBox, flags); this._leftGradient.opacity = (this._scrollableLeft && scrollable) ? 255 : 0; childBox.x1 = (this.actor.allocation.x2 - this.actor.allocation.x1) - this._rightGradient.width; childBox.y1 = 0; childBox.x2 = childBox.x1 + this._rightGradient.width; childBox.y2 = this.actor.height; this._rightGradient.allocate(childBox, flags); this._rightGradient.opacity = (this._scrollableRight && scrollable) ? 255 : 0; let arrowWidth = Math.floor(leftPadding / 3); let arrowHeight = arrowWidth * 2; childBox.x1 = leftPadding / 2; childBox.y1 = this.actor.height / 2 - arrowWidth; childBox.x2 = childBox.x1 + arrowWidth; childBox.y2 = childBox.y1 + arrowHeight; this._leftArrow.allocate(childBox, flags); this._leftArrow.opacity = this._leftGradient.opacity; arrowWidth = Math.floor(rightPadding / 3); arrowHeight = arrowWidth * 2; childBox.x1 = this.actor.width - arrowWidth - rightPadding / 2; childBox.y1 = this.actor.height / 2 - arrowWidth; childBox.x2 = childBox.x1 + arrowWidth; childBox.y2 = childBox.y1 + arrowHeight; this._rightArrow.allocate(childBox, flags); this._rightArrow.opacity = this._rightGradient.opacity; }, addItem : function(item, label) { let bbox = new St.Button({ style_class: 'item-box', reactive: true }); bbox.set_child(item); this._list.add_actor(bbox); let n = this._items.length; bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); })); bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); })); bbox.label_actor = label; this._items.push(bbox); }, _onItemClicked: function (index) { this._itemActivated(index); }, _onItemEnter: function (index) { this._itemEntered(index); }, addSeparator: function () { let box = new St.Bin({ style_class: 'separator' }); this._separator = box; this._list.add_actor(box); }, highlight: function(index, justOutline) { if (this._highlighted != -1) { this._items[this._highlighted].remove_style_pseudo_class('outlined'); this._items[this._highlighted].remove_style_pseudo_class('selected'); } this._highlighted = index; if (this._highlighted != -1) { if (justOutline) this._items[this._highlighted].add_style_pseudo_class('outlined'); else this._items[this._highlighted].add_style_pseudo_class('selected'); } let [absItemX, absItemY] = this._items[index].get_transformed_position(); let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0); let [containerWidth, containerHeight] = this.actor.get_transformed_size(); if (posX + this._items[index].get_width() > containerWidth) this._scrollToRight(); else if (posX < 0) this._scrollToLeft(); }, _scrollToLeft : function() { let x = this._items[this._highlighted].allocation.x1; this._scrollableRight = true; Tweener.addTween(this._list, { anchor_x: x, time: POPUP_SCROLL_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function () { if (this._highlighted == 0) { this._scrollableLeft = false; this.actor.queue_relayout(); } }) }); }, _scrollToRight : function() { this._scrollableLeft = true; let monitor = this._activeMonitor; let padding = this.actor.get_theme_node().get_horizontal_padding(); let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding(); let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding; Tweener.addTween(this._list, { anchor_x: x, time: POPUP_SCROLL_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function () { if (this._highlighted == this._items.length - 1) { this._scrollableRight = false; this.actor.queue_relayout(); } }) }); }, _itemActivated: function(n) { this.emit('item-activated', n); }, _itemEntered: function(n) { this.emit('item-entered', n); }, _maxChildWidth: function (forHeight) { let maxChildMin = 0; let maxChildNat = 0; for (let i = 0; i < this._items.length; i++) { let [childMin, childNat] = this._items[i].get_preferred_width(forHeight); maxChildMin = Math.max(childMin, maxChildMin); maxChildNat = Math.max(childNat, maxChildNat); if (this._squareItems) { let [childMin, childNat] = this._items[i].get_preferred_height(-1); maxChildMin = Math.max(childMin, maxChildMin); maxChildNat = Math.max(childNat, maxChildNat); } } return [maxChildMin, maxChildNat]; }, _getPreferredWidth: function (actor, forHeight, alloc) { let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight); let separatorWidth = 0; if (this._separator) { let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight); separatorWidth = sepNat + this._list.spacing; } let totalSpacing = this._list.spacing * Math.max(1, (this._items.length - 1)); alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing; alloc.natural_size = alloc.min_size; this._minSize = alloc.min_size; }, _getPreferredHeight: function (actor, forWidth, alloc) { let maxChildMin = 0; let maxChildNat = 0; for (let i = 0; i < this._items.length; i++) { let [childMin, childNat] = this._items[i].get_preferred_height(-1); maxChildMin = Math.max(childMin, maxChildMin); maxChildNat = Math.max(childNat, maxChildNat); } if (this._squareItems) { let [childMin, childNat] = this._maxChildWidth(-1); maxChildMin = Math.max(childMin, maxChildMin); maxChildNat = maxChildMin; } alloc.min_size = maxChildMin; alloc.natural_size = maxChildNat; }, _allocate: function (actor, box, flags) { let childHeight = box.y2 - box.y1; let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight); let totalSpacing = this._list.spacing * (this._items.length - 1); let separatorWidth = 0; if (this._separator) { let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight); separatorWidth = sepNat; totalSpacing += this._list.spacing; } let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length); let x = 0; let children = this._list.get_children(); let childBox = new Clutter.ActorBox(); let monitor = this._activeMonitor; let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT); if (this.actor.allocation.x2 == monitor.x + monitor.width - parentRightPadding) { if (this._squareItems) childWidth = childHeight; else { let [childMin, childNat] = children[0].get_preferred_width(childHeight); childWidth = childMin; } } for (let i = 0; i < children.length; i++) { if (this._items.indexOf(children[i]) != -1) { let [childMin, childNat] = children[i].get_preferred_height(childWidth); let vSpacing = (childHeight - childNat) / 2; childBox.x1 = x; childBox.y1 = vSpacing; childBox.x2 = x + childWidth; childBox.y2 = childBox.y1 + childNat; children[i].allocate(childBox, flags); x += this._list.spacing + childWidth; } else if (children[i] == this._separator) { // We want the separator to be more compact than the rest. childBox.x1 = x; childBox.y1 = 0; childBox.x2 = x + separatorWidth; childBox.y2 = childHeight; children[i].allocate(childBox, flags); x += this._list.spacing + separatorWidth; } else { // Something else, eg, AppList's arrows; // we don't allocate it. } } let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT); let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT); let topPadding = this.actor.get_theme_node().get_padding(St.Side.TOP); let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM); // Clip the area for scrolling this._clipBin.set_clip(0, -topPadding, (this.actor.allocation.x2 - this.actor.allocation.x1) - leftPadding - rightPadding, this.actor.height + bottomPadding); } }; Signals.addSignalMethods(SwitcherList.prototype); function AppList() { this._init.apply(this, arguments); } AppList.prototype = { __proto__ : SwitcherList.prototype, _init : function(windows, showThumbnails, showArrows, activeMonitor) { SwitcherList.prototype._init.call(this, true, activeMonitor); // Construct the AppIcons, add to the popup let activeWorkspace = global.screen.get_active_workspace(); let workspaceIcons = []; let otherIcons = []; for (let i = 0; i < windows.length; i++) { workspaceIcons.push(new AppIcon(windows[i], showThumbnails)); } this.icons = []; this._arrows = []; for (let i = 0; i < workspaceIcons.length; i++) this._addIcon(workspaceIcons[i]); if (workspaceIcons.length > 0 && otherIcons.length > 0) this.addSeparator(); for (let i = 0; i < otherIcons.length; i++) this._addIcon(otherIcons[i]); this._curApp = -1; this._iconSize = 0; this._showArrows = showArrows; this._mouseTimeOutId = 0; this._activeMonitor = activeMonitor; }, _getPreferredHeight: function (actor, forWidth, alloc) { if (this._items.length < 1) { alloc.min_size = alloc.natural_size = 32; return; } let j = 0; while(this._items.length > 1 && this._items[j].style_class != 'item-box') { j++; } let themeNode = this._items[j].get_theme_node(); let iconPadding = themeNode.get_horizontal_padding(); let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT); let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1); let iconSpacing = iconNaturalHeight + iconPadding + iconBorder; let totalSpacing = this._list.spacing * (this._items.length - 1); if (this._separator) totalSpacing += this._separator.width + this._list.spacing; // We just assume the whole screen here due to weirdness happing with the passed width let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding(); let availWidth = this._activeMonitor.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding(); let height = 0; for(let i = 0; i < iconSizes.length; i++) { this._iconSize = iconSizes[i]; height = iconSizes[i] + iconSpacing; let w = height * this._items.length + totalSpacing; if (w <= availWidth) break; } if (this._items.length == 1) { this._iconSize = iconSizes[0]; height = iconSizes[0] + iconSpacing; } for(let i = 0; i < this.icons.length; i++) { if (this.icons[i].icon != null) break; this.icons[i].set_size(this._iconSize); } alloc.min_size = height; alloc.natural_size = height; }, _allocate: function (actor, box, flags) { // Allocate the main list items SwitcherList.prototype._allocate.call(this, actor, box, flags); if (this._showArrows) { let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3); let arrowWidth = arrowHeight * 2; // Now allocate each arrow underneath its item let childBox = new Clutter.ActorBox(); for (let i = 0; i < this._items.length; i++) { let itemBox = this._items[i].allocation; childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2); childBox.x2 = childBox.x1 + arrowWidth; childBox.y1 = itemBox.y2 + arrowHeight; childBox.y2 = childBox.y1 + arrowHeight; this._arrows[i].allocate(childBox, flags); } } }, // We override SwitcherList's _onItemEnter method to delay // activation when the thumbnail list is open _onItemEnter: function (index) { if (this._mouseTimeOutId != 0) Mainloop.source_remove(this._mouseTimeOutId); this._itemEntered(index); }, _enterItem: function(index) { let [x, y, mask] = global.get_pointer(); let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); if (this._items[index].contains(pickedActor)) this._itemEntered(index); }, // We override SwitcherList's highlight() method to also deal with // the AppList->ThumbnailList arrows. highlight : function(n, justOutline) { if (this._curApp != -1) { this._arrows[this._curApp].hide(); } SwitcherList.prototype.highlight.call(this, n, justOutline); this._curApp = n; if (n != -1 && this._showArrows) { this._arrows[n].show(); } }, _addIcon : function(appIcon) { this.icons.push(appIcon); this.addItem(appIcon.actor, appIcon.label); let n = this._arrows.length; let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' }); arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); }); this._list.add_actor(arrow); this._arrows.push(arrow); arrow.hide(); } }; function ThumbnailList(windows, activeMonitor) { this._init(windows, activeMonitor); } ThumbnailList.prototype = { __proto__ : SwitcherList.prototype, _init : function(windows, activeMonitor) { SwitcherList.prototype._init.call(this, false, activeMonitor); let activeWorkspace = global.screen.get_active_workspace(); // We fake the value of 'separatorAdded' when the app has no window // on the current workspace, to avoid displaying a useless separator in // that case. let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace; this._labels = new Array(); this._thumbnailBins = new Array(); this._clones = new Array(); this._windows = windows; for (let i = 0; i < windows.length; i++) { if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) { this.addSeparator(); separatorAdded = true; } let box = new St.BoxLayout({ style_class: 'thumbnail-box', vertical: true }); let bin = new St.Bin({ style_class: 'thumbnail' }); box.add_actor(bin); this._thumbnailBins.push(bin); let title = windows[i].get_title(); if (title) { let name = new St.Label({ text: title }); // St.Label doesn't support text-align so use a Bin let bin = new St.Bin({ x_align: St.Align.MIDDLE }); this._labels.push(bin); bin.add_actor(name); box.add_actor(bin); this.addItem(box, name); } else { this.addItem(box, null); } } }, addClones : function (availHeight) { if (!this._thumbnailBins.length) return; let totalPadding = this._items[0].get_theme_node().get_horizontal_padding() + this._items[0].get_theme_node().get_vertical_padding(); totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding(); let [labelMinHeight, labelNaturalHeight] = this._labels.length > 0 ? this._labels[0].get_preferred_height(-1) : [0, 0]; let spacing = this._items[0].child.get_theme_node().get_length('spacing'); availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE * global.ui_scale); let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing; binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE * global.ui_scale, binHeight); for (let i = 0; i < this._thumbnailBins.length; i++) { let metaWindow = this._windows[i]; let container = new St.Group(); let clones = WindowUtils.createWindowClone(metaWindow, availHeight, availHeight, true, true); for (let j = 0; j < clones.length; j++) { let clone = clones[j]; container.add_actor(clone.actor); clone.actor.set_position(clone.x, clone.y); } this._thumbnailBins[i].set_height(binHeight); this._thumbnailBins[i].add_actor(container); this._clones.push(container); } // Make sure we only do this once this._thumbnailBins = new Array(); } }; function _drawArrow(area, side) { let themeNode = area.get_theme_node(); let borderColor = themeNode.get_border_color(side); let bodyColor = themeNode.get_foreground_color(); let [width, height] = area.get_surface_size (); let cr = area.get_context(); cr.setLineWidth(1.0); Clutter.cairo_set_source_color(cr, borderColor); switch (side) { case St.Side.TOP: cr.moveTo(0, height); cr.lineTo(Math.floor(width * 0.5), 0); cr.lineTo(width, height); break; case St.Side.BOTTOM: cr.moveTo(width, 0); cr.lineTo(Math.floor(width * 0.5), height); cr.lineTo(0, 0); break; case St.Side.LEFT: cr.moveTo(width, height); cr.lineTo(0, Math.floor(height * 0.5)); cr.lineTo(width, 0); break; case St.Side.RIGHT: cr.moveTo(0, 0); cr.lineTo(width, Math.floor(height * 0.5)); cr.lineTo(0, height); break; } cr.strokePreserve(); Clutter.cairo_set_source_color(cr, bodyColor); cr.fill(); cr.$dispose(); } Cinnamon-2.8.6/js/ui/appSwitcher/appSwitcher.js0000664000175000017500000003466512625666430020416 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const Main = imports.ui.main; const Cinnamon = imports.gi.Cinnamon; const CHECK_DESTROYED_TIMEOUT = 100; const DISABLE_HOVER_TIMEOUT = 500; // milliseconds function sortWindowsByUserTime(win1, win2) { let t1 = win1.get_user_time(); let t2 = win2.get_user_time(); let m1 = win1.minimized; let m2 = win2.minimized; if (m1 == m2) { return (t2 > t1) ? 1 : -1; } else { return m1 ? 1 : -1; } } function matchSkipTaskbar(win) { return !win.is_skip_taskbar(); } function matchWmClass(win) { return win.get_wm_class() == this && !win.is_skip_taskbar(); } function matchWorkspace(win) { return win.get_workspace() == this && !win.is_skip_taskbar(); } function primaryModifier(mask) { if (mask == 0) return 0; let primary = 1; while (mask > 1) { mask >>= 1; primary <<= 1; } return primary; } function getWindowsForBinding(binding) { // Construct a list with all windows let windows = []; let windowActors = global.get_window_actors(); for (let i in windowActors) windows.push(windowActors[i].get_meta_window()); windows = windows.filter( Main.isInteresting ); switch(binding.get_name()) { case 'switch-panels': // Switch between windows of all workspaces windows = windows.filter( matchSkipTaskbar ); break; case 'switch-group': // Switch between windows of same application from all workspaces let focused = global.display.focus_window ? global.display.focus_window : windows[0]; windows = windows.filter( matchWmClass, focused.get_wm_class() ); break; default: // Switch between windows of current workspace windows = windows.filter( matchWorkspace, global.screen.get_active_workspace() ); break; } // Sort by user time windows.sort(sortWindowsByUserTime); return windows; } function AppSwitcher() { this._init.apply(this, arguments); } AppSwitcher.prototype = { _init: function(binding) { this._initialDelayTimeoutId = null; this._binding = binding; this._windows = getWindowsForBinding(binding); this._haveModal = false; this._destroyed = false; this._motionTimeoutId = 0; this._checkDestroyedTimeoutId = 0; this._currentIndex = this._windows.indexOf(global.display.focus_window); if (this._currentIndex < 0) { this._currentIndex = 0; } this._modifierMask = primaryModifier(binding.get_mask()); this._tracker = Cinnamon.WindowTracker.get_default(); this._windowManager = global.window_manager; this._dcid = this._windowManager.connect('destroy', Lang.bind(this, this._windowDestroyed)); this._mcid = this._windowManager.connect('map', Lang.bind(this, this._activateSelected)); this._enforcePrimaryMonitor = global.settings.get_boolean("alttab-switcher-enforce-primary-monitor"); this._updateActiveMonitor(); }, _setupModal: function() { this._haveModal = Main.pushModal(this.actor); if (!this._haveModal) { // Probably someone else has a pointer grab, try again with keyboard only this._haveModal = Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED); } if (!this._haveModal) this._activateSelected(); else { // Initially disable hover so we ignore the enter-event if // the switcher appears underneath the current pointer location this._disableHover(); this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent)); this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent)); this.actor.connect('scroll-event', Lang.bind(this, this._scrollEvent)); this.actor.connect('button-press-event', Lang.bind(this, this.destroy)); // There's a race condition; if the user released Alt before // we got the grab, then we won't be notified. (See // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for // details) So we check now. (Have to do this after updating // selection.) let [x, y, mods] = global.get_pointer(); if (!(mods & this._modifierMask)) { this._activateSelected(); return false; } // We delay showing the popup so that fast Alt+Tab users aren't // disturbed by the popup briefly flashing. let delay = global.settings.get_int("alttab-switcher-delay"); this._initialDelayTimeoutId = Mainloop.timeout_add(delay, Lang.bind(this, this._show)); } return this._haveModal; }, _popModal: function() { if (this._haveModal) { Main.popModal(this.actor); this._haveModal = false; } }, _show: function() { throw new Error("Abstract method _show not implemented"); }, _hide: function() { throw new Error("Abstract method _hide not implemented"); }, _onDestroy: function() { throw new Error("Abstract method _onDestroy not implemented"); }, _createList: function() { throw new Error("Abstract method _createList not implemented"); }, _updateList: function() { throw new Error("Abstract method _updateList not implemented"); }, _selectNext: function() { throw new Error("Abstract method _selectNext not implemented"); }, _selectPrevious: function() { throw new Error("Abstract method _selectPrevious not implemented"); }, _onWorkspaceSelected: function() { throw new Error("Abstract method _onWorkspaceSelected not implemented"); }, _checkSwitchTime: function() { return true; }, _setCurrentWindow: function(window) { }, _next: function() { if(this._windows.length <= 1) { this._currentIndex = 0; this._updateList(0); } else { this.actor.set_reactive(false); this._selectNext(); this.actor.set_reactive(true); } this._setCurrentWindow(this._windows[this._currentIndex]); }, _previous: function() { if(this._windows.length <= 1) { this._currentIndex = 0; this._updateList(0); } else { this.actor.set_reactive(false); this._selectPrevious(); this.actor.set_reactive(true); } this._setCurrentWindow(this._windows[this._currentIndex]); }, _select: function(index) { this._currentIndex = index; this._setCurrentWindow(this._windows[this._currentIndex]); }, _updateActiveMonitor: function() { this._activeMonitor = null; if (!this._enforcePrimaryMonitor) this._activeMonitor = Main.layoutManager.currentMonitor; if (!this._activeMonitor) this._activeMonitor = Main.layoutManager.primaryMonitor; return this._activeMonitor; }, _keyPressEvent: function(actor, event) { let event_state = Cinnamon.get_event_state(event); this._disableHover(); // Switch workspace if(event_state & Clutter.ModifierType.CONTROL_MASK) { switch(event.get_key_symbol()) { case Clutter.Right: if (this._switchWorkspace(1)) return true; break; case Clutter.Left: if (this._switchWorkspace(-1)) return true; break; } } // Extra keys switch(event.get_key_symbol()) { case Clutter.Escape: // Esc -> Close switcher this.destroy(); return true; case Clutter.Return: // Enter -> Select active window this._activateSelected(); return true; case Clutter.d: case Clutter.D: // D -> Show desktop this._showDesktop(); return true; case Clutter.q: case Clutter.Q: // Q -> Close window this._windows[this._currentIndex].delete(global.get_current_time()); this._checkDestroyedTimeoutId = Mainloop.timeout_add(CHECK_DESTROYED_TIMEOUT, Lang.bind(this, this._checkDestroyed, this._windows[this._currentIndex])); return true; case Clutter.Right: case Clutter.Down: // Right/Down -> navigate to next preview if(this._checkSwitchTime()) this._next(); return true; case Clutter.Left: case Clutter.Up: // Left/Up -> navigate to previous preview if(this._checkSwitchTime()) this._previous(); return true; } // Default alt-tab let action = global.display.get_keybinding_action(event.get_key_code(), event_state); switch(action) { case Meta.KeyBindingAction.SWITCH_GROUP: case Meta.KeyBindingAction.SWITCH_WINDOWS: case Meta.KeyBindingAction.SWITCH_PANELS: if(this._checkSwitchTime()) { // shift -> backwards if(event_state & Clutter.ModifierType.SHIFT_MASK) this._previous(); else this._next(); } return true; case Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD: case Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD: case Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD: if(this._checkSwitchTime()) this._previous(); return true; } return true; }, _keyReleaseEvent: function(actor, event) { let [x, y, mods] = global.get_pointer(); let state = mods & this._modifierMask; if (state == 0) { if (this._initialDelayTimeoutId !== 0) this._currentIndex = (this._currentIndex + 1) % this._windows.length; this._activateSelected(); } return true; }, // allow navigating by mouse-wheel scrolling _scrollEvent: function(actor, event) { if(this._checkSwitchTime()) { actor.set_reactive(false); if (event.get_scroll_direction() == Clutter.ScrollDirection.UP) this._previous(); else this._next(); actor.set_reactive(true); } return true; }, _disableHover : function() { this._mouseActive = false; if (this._motionTimeoutId != 0) Mainloop.source_remove(this._motionTimeoutId); this._motionTimeoutId = Mainloop.timeout_add(DISABLE_HOVER_TIMEOUT, Lang.bind(this, this._mouseTimedOut)); }, _mouseTimedOut : function() { this._motionTimeoutId = 0; this._mouseActive = true; }, _switchWorkspace: function(direction) { if (global.screen.n_workspaces < 2) return false; let current = global.screen.get_active_workspace_index(); let nextIndex = (global.screen.n_workspaces + current + direction) % global.screen.n_workspaces; let workspace = global.screen.get_workspace_by_index(nextIndex); workspace.activate(global.get_current_time()); if (current == global.screen.get_active_workspace_index()) return false; Main.wm.showWorkspaceOSD(); this._onWorkspaceSelected(workspace); return true; }, _windowDestroyed: function(wm, actor) { this._removeDestroyedWindow(actor.meta_window); }, _checkDestroyed: function(window) { this._checkDestroyedTimeoutId = 0; this._removeDestroyedWindow(window); }, _removeDestroyedWindow: function(window) { for (let i in this._windows) { if (window == this._windows[i]) { if (this._windows.length == 1) this.destroy(); else { this._windows.splice(i, 1); if (this._previews && this._previews[i]) { this._previews[i].destroy(); this._previews.splice(i, 1); } if (i < this._currentIndex) this._currentIndex--; else this._currentIndex %= this._windows.length; this._updateList(0); this._setCurrentWindow(this._windows[this._currentIndex]); } return; } } }, _activateSelected: function() { Main.activateWindow(this._windows[this._currentIndex], global.get_current_time()); if (!this._destroyed) this.destroy(); }, _showDesktop: function() { for (let i in this._windows) { if (!this._windows[i].minimized) this._windows[i].minimize(); } this.destroy(); }, destroy: function() { this._destroyed = true; this._popModal(); if (this._initialDelayTimeoutId !== 0) this._destroyActors(); else this._hide(); if(this._initialDelayTimeoutId !== null && this._initialDelayTimeoutId > 0) { Mainloop.source_remove(this._initialDelayTimeoutId); this._initialDelayTimeoutId = 0; } this._onDestroy(); this._windows = null; if (this._motionTimeoutId != 0) { Mainloop.source_remove(this._motionTimeoutId); this._motionTimeoutId = 0; } if (this._checkDestroyedTimeoutId != 0) { Mainloop.source_remove(this._checkDestroyedTimeoutId); this._checkDestroyedTimeoutId = 0; } this._windowManager.disconnect(this._dcid); this._windowManager.disconnect(this._mcid); } }; Cinnamon-2.8.6/js/ui/appSwitcher/timelineSwitcher.js0000664000175000017500000001332212625666430021427 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Clutter = imports.gi.Clutter; const Config = imports.misc.config; const Tweener = imports.ui.tweener; const AppSwitcher3D = imports.ui.appSwitcher.appSwitcher3D; const TRANSITION_TYPE = 'easeOutQuad'; function TimelineSwitcher() { this._init.apply(this, arguments); } TimelineSwitcher.prototype = { __proto__: AppSwitcher3D.AppSwitcher3D.prototype, _init: function() { AppSwitcher3D.AppSwitcher3D.prototype._init.apply(this, arguments); }, _adaptClones: function() { let monitor = this._activeMonitor; for (let i in this._previews) { let clone = this._previews[i]; clone.anchor_gravity = Clutter.Gravity.WEST; clone.rotation_angle_y = 12; clone.target_x = Math.round(monitor.width * 0.3); clone.target_y = Math.round(monitor.height * 0.5); clone.lower_bottom(); } }, _selectNext: function() { this._currentIndex = (this._currentIndex + 1) % this._windows.length; this._updateList(1); }, _selectPrevious: function() { this._currentIndex = (this._windows.length + this._currentIndex - 1) % this._windows.length; this._updateList(-1); }, _updateList: function(direction) { if(this._previews.length == 0) return; let monitor = this._activeMonitor; let animation_time = AppSwitcher3D.ANIMATION_TIME; if(this._previews.length == 1) { let preview = this._previews[0]; Tweener.addTween(preview, { opacity: 255, x: preview.target_x, y: preview.target_y, width: preview.target_width, height: preview.target_height, time: animation_time / 2, transition: TRANSITION_TYPE }); return; } // preview windows for (let i in this._previews) { let preview = this._previews[i]; i = parseInt(i); let distance = (this._currentIndex > i) ? this._previews.length - this._currentIndex + i : i - this._currentIndex; if (distance == this._previews.length - 1 && direction > 0) { preview.__looping = true; Tweener.addTween(preview, { opacity: 0, x: preview.target_x + 200, y: preview.target_y + 100, width: preview.target_width, height: preview.target_height, time: animation_time / 2, transition: TRANSITION_TYPE, onCompleteParams: [preview, distance, animation_time], onComplete: this._onFadeForwardComplete, onCompleteScope: this, }); } else if (distance == 0 && direction < 0) { preview.__looping = true; Tweener.addTween(preview, { opacity: 0, time: animation_time / 2, transition: TRANSITION_TYPE, onCompleteParams: [preview, distance, animation_time], onComplete: this._onFadeBackwardsComplete, onCompleteScope: this, }); } else { let tweenparams = { opacity: 255, x: preview.target_x - Math.sqrt(distance) * 150, y: preview.target_y - Math.sqrt(distance) * 100, width: Math.max(preview.target_width * ((20 - 2 * distance) / 20), 0), height: Math.max(preview.target_height * ((20 - 2 * distance) / 20), 0), time: animation_time, transition: TRANSITION_TYPE, }; if(preview.__looping || preview.__finalTween) preview.__finalTween = tweenparams; else Tweener.addTween(preview, tweenparams); } } }, _onFadeBackwardsComplete: function(preview, distance, animation_time) { preview.__looping = false; preview.raise_top(); preview.x = preview.target_x + 200; preview.y = preview.target_y + 100; preview.width = preview.target_width; preview.height = preview.target_height; Tweener.addTween(preview, { opacity: 255, x: preview.target_x, y: preview.target_y, width: preview.target_width, height: preview.target_height, time: animation_time / 2, transition: TRANSITION_TYPE, onCompleteParams: [preview], onComplete: this._onFinishMove, onCompleteScope: this, }); }, _onFadeForwardComplete: function(preview, distance, animation_time) { preview.__looping = false; preview.lower_bottom(); preview.x = preview.target_x - Math.sqrt(distance) * 150; preview.y = preview.target_y - Math.sqrt(distance) * 100; preview.width = Math.max(preview.target_width * ((20 - 2 * distance) / 20), 0); preview.height = Math.max(preview.target_height * ((20 - 2 * distance) / 20), 0); Tweener.addTween(preview, { opacity: 255, time: animation_time / 2, transition: TRANSITION_TYPE, onCompleteParams: [preview], onComplete: this._onFinishMove, onCompleteScope: this, }); }, _onFinishMove: function(preview) { if(preview.__finalTween) { Tweener.addTween(preview, preview.__finalTween); preview.__finalTween = null; } } }; Cinnamon-2.8.6/js/ui/overview.js0000664000175000017500000004176412625666430015500 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gtk = imports.gi.Gtk; const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Gdk = imports.gi.Gdk; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Tweener = imports.ui.tweener; const WorkspacesView = imports.ui.workspacesView; // Time for initial animation going into Overview mode const ANIMATION_TIME = 0.25; const SwipeScrollDirection = WorkspacesView.SwipeScrollDirection; const SwipeScrollResult = WorkspacesView.SwipeScrollResult; function Overview() { this._init.apply(this, arguments); } Overview.prototype = { _init : function() { this._spacing = 0; this._group = new St.Group({ name: 'overview', reactive: true }); this._group._delegate = this; this._group.connect('style-changed', Lang.bind(this, function() { let node = this._group.get_theme_node(); let spacing = node.get_length('spacing'); if (spacing != this._spacing) { this._spacing = spacing; } })); this._group.hide(); global.overlay_group.add_actor(this._group); this._scrollDirection = SwipeScrollDirection.NONE; this._scrollAdjustment = null; this._capturedEventId = 0; this._buttonPressId = 0; this.visible = false; // animating to overview, in overview, animating out this._shown = false; // show() and not hide() this._shownTemporarily = false; // showTemporarily() and not hideTemporarily() this._modal = false; // have a modal grab this.animationInProgress = false; this._hideInProgress = false; this._windowSwitchTimeoutId = 0; this._windowSwitchTimestamp = 0; this._lastActiveWorkspaceIndex = -1; this._lastHoveredWindow = null; }, // The members we construct that are implemented in JS might // want to access the overview as Main.overview to connect // signal handlers and so forth. So we create them after // construction in this init() method. init: function() { Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.hide)); }, setScrollAdjustment: function(adjustment, direction) { this._scrollAdjustment = adjustment; if (this._scrollAdjustment == null) this._scrollDirection = SwipeScrollDirection.NONE; else this._scrollDirection = direction; }, _onButtonPress: function(actor, event) { this.emit('overview-background-button-press', actor, event); if (this._scrollDirection == SwipeScrollDirection.NONE || event.get_button() != 1) return false; let [stageX, stageY] = event.get_coords(); this._dragStartX = this._dragX = stageX; this._dragStartY = this._dragY = stageY; this._dragStartValue = this._scrollAdjustment.value; this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); this.emit('swipe-scroll-begin'); return true; }, _onCapturedEvent: function(actor, event) { let stageX, stageY; let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold; switch(event.type()) { case Clutter.EventType.BUTTON_RELEASE: [stageX, stageY] = event.get_coords(); // default to snapping back to the original value let newValue = this._dragStartValue; let minValue = this._scrollAdjustment.lower; let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size; let direction; if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) { direction = stageX > this._dragStartX ? -1 : 1; if (St.Widget.get_default_direction() == St.TextDirection.RTL) direction *= -1; } else { direction = stageY > this._dragStartY ? -1 : 1; } // We default to scroll a full page size; both the first // and the last page may be smaller though, so we need to // adjust difference in those cases. let difference = direction * this._scrollAdjustment.page_size; if (this._dragStartValue + difference > maxValue) difference = maxValue - this._dragStartValue; else if (this._dragStartValue + difference < minValue) difference = minValue - this._dragStartValue; // If the user has moved more than half the scroll // difference, we want to "settle" to the new value // even if the user stops dragging rather "throws" by // releasing during the drag. let distance = this._dragStartValue - this._scrollAdjustment.value; let noStop = Math.abs(distance / difference) > 0.5; // We detect if the user is stopped by comparing the // timestamp of the button release with the timestamp of // the last motion. Experimentally, a difference of 0 or 1 // millisecond indicates that the mouse is in motion, a // larger difference indicates that the mouse is stopped. if ((this._lastMotionTime > 0 && this._lastMotionTime > event.get_time() - 2) || noStop) { if (this._dragStartValue + difference >= minValue && this._dragStartValue + difference <= maxValue) newValue += difference; } let result; // See if the user has moved the mouse enough to trigger // a drag if (Math.abs(stageX - this._dragStartX) < threshold && Math.abs(stageY - this._dragStartY) < threshold) { // no motion? It's a click! result = SwipeScrollResult.CLICK; this.emit('swipe-scroll-end', result); } else { if (newValue == this._dragStartValue) result = SwipeScrollResult.CANCEL; else result = SwipeScrollResult.SWIPE; // The event capture handler is disconnected // while scrolling to the final position, so // to avoid undesired prelights we raise // the cover pane. this._coverPane.raise_top(); this._coverPane.show(); Tweener.addTween(this._scrollAdjustment, { value: newValue, time: ANIMATION_TIME, transition: 'easeOutQuad', onCompleteScope: this, onComplete: function() { this._coverPane.hide(); this.emit('swipe-scroll-end', result); } }); } global.stage.disconnect(this._capturedEventId); this._capturedEventId = 0; return result != SwipeScrollResult.CLICK; case Clutter.EventType.MOTION: [stageX, stageY] = event.get_coords(); let dx = this._dragX - stageX; let dy = this._dragY - stageY; let primary = Main.layoutManager.primaryMonitor; this._dragX = stageX; this._dragY = stageY; this._lastMotionTime = event.get_time(); // See if the user has moved the mouse enough to trigger // a drag if (Math.abs(stageX - this._dragStartX) < threshold && Math.abs(stageY - this._dragStartY) < threshold) return true; if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) { if (St.Widget.get_default_direction() == St.TextDirection.RTL) this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size; else this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size; } else { this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size; } return true; // Block enter/leave events to avoid prelights // during swipe-scroll case Clutter.EventType.ENTER: case Clutter.EventType.LEAVE: return true; } return false; }, //// Public methods //// // show: // // Animates the overview visible and grabs mouse and keyboard input show : function() { if (this._shown) return; // Do this manually instead of using _syncInputMode, to handle failure if (!Main.pushModal(this._group)) return; this._modal = true; this._animateVisible(); this._shown = true; this._buttonPressId = this._group.connect('button-press-event', Lang.bind(this, this._onButtonPress)); }, _animateVisible: function() { if (this.visible || this.animationInProgress) return; // The main BackgroundActor is inside global.window_group which is // hidden when displaying the overview, so we create a new // one. Instances of this class share a single CoglTexture behind the // scenes which allows us to show the background with different // rendering options without duplicating the texture data. this._background = new Clutter.Group(); this._background.hide(); global.overlay_group.add_actor(this._background); this._desktopBackground = Meta.BackgroundActor.new_for_screen(global.screen); this._background.add_actor(this._desktopBackground); this._backgroundShade = new St.Bin({style_class: 'workspace-overview-background-shade'}); this._background.add_actor(this._backgroundShade); this._backgroundShade.set_size(global.screen_width, global.screen_height); this.visible = true; this.animationInProgress = true; // During transitions, we raise this to the top to avoid having the overview // area be reactive; it causes too many issues such as mouseover handlers in the workspaces. this._coverPane = new Clutter.Rectangle({ opacity: 0, reactive: true }); this._group.add_actor(this._coverPane); this._coverPane.set_position(0, 0); this._coverPane.set_size(global.screen_width, global.screen_height); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._coverPane.hide(); // All the the actors in the window group are completely obscured, // hiding the group holding them while the Overview is displayed greatly // increases performance of the Overview especially when there are many // windows visible. // // If we switched to displaying the actors in the Overview rather than // clones of them, this would obviously no longer be necessary. // // Disable unredirection while in the overview Meta.disable_unredirect_for_screen(global.screen); global.window_group.hide(); this._group.show(); this._background.show(); this.workspacesView = new WorkspacesView.WorkspacesView(); global.overlay_group.add_actor(this.workspacesView.actor); Main.panelManager.disablePanels(); this._group.opacity = 0; Tweener.addTween(this._group, { opacity: 255, transition: 'easeOutQuad', time: ANIMATION_TIME, onComplete: this._showDone, onCompleteScope: this }); this._coverPane.raise_top(); this._coverPane.show(); this.emit('showing'); }, // showTemporarily: // // Animates the overview visible without grabbing mouse and keyboard input; // if show() has already been called, this has no immediate effect, but // will result in the overview not being hidden until hideTemporarily() is // called. showTemporarily: function() { if (this._shownTemporarily) return; this._syncInputMode(); this._animateVisible(); this._shownTemporarily = true; }, // hide: // // Reverses the effect of show() hide: function() { if (!this._shown) return; if (!this._shownTemporarily) this._animateNotVisible(); this._shown = false; this._syncInputMode(); if (this._buttonPressId > 0) this._group.disconnect(this._buttonPressId); this._buttonPressId = 0; }, // hideTemporarily: // // Reverses the effect of showTemporarily() hideTemporarily: function() { if (!this._shownTemporarily) return; if (!this._shown) this._animateNotVisible(); this._shownTemporarily = false; this._syncInputMode(); }, toggle: function() { if (this._shown) this.hide(); else this.show(); }, //// Private methods //// _syncInputMode: function() { // We delay input mode changes during animation so that when removing the // overview we don't have a problem with the release of a press/release // going to an application. if (this.animationInProgress) return; if (this._shown) { if (!this._modal) { if (Main.pushModal(this._group)) this._modal = true; else this.hide(); } } else if (this._shownTemporarily) { if (this._modal) { Main.popModal(this._group); this._modal = false; } global.stage_input_mode = Cinnamon.StageInputMode.FULLSCREEN; } else { if (this._modal) { Main.popModal(this._group); this._modal = false; } else if (global.stage_input_mode == Cinnamon.StageInputMode.FULLSCREEN) global.stage_input_mode = Cinnamon.StageInputMode.NORMAL; } }, _animateNotVisible: function() { if (!this.visible || this.animationInProgress) return; this.animationInProgress = true; this._hideInProgress = true; Main.panelManager.enablePanels(); this.workspacesView.hide(); // Make other elements fade out. Tweener.addTween(this._group, { opacity: 0, transition: 'easeOutQuad', time: ANIMATION_TIME, onComplete: this._hideDone, onCompleteScope: this }); this._coverPane.raise_top(); this._coverPane.show(); this.emit('hiding'); }, _showDone: function() { this.animationInProgress = false; this._coverPane.hide(); this.emit('shown'); // Handle any calls to hide* while we were showing if (!this._shown && !this._shownTemporarily) this._animateNotVisible(); this._syncInputMode(); global.sync_pointer(); }, _hideDone: function() { this._group.remove_actor(this._coverPane); this._coverPane.destroy(); global.overlay_group.remove_actor(this._background); this._background.destroy(); // Re-enable unredirection Meta.enable_unredirect_for_screen(global.screen); global.window_group.show(); this.workspacesView.destroy(); this.workspacesView = null; this._group.hide(); this.visible = false; this.animationInProgress = false; this._hideInProgress = false; this.emit('hidden'); // Handle any calls to show* while we were hiding if (this._shown || this._shownTemporarily) this._animateVisible(); this._syncInputMode(); Main.layoutManager._chrome.updateRegions(); } }; Signals.addSignalMethods(Overview.prototype); Cinnamon-2.8.6/js/ui/scripting.js0000664000175000017500000003034512625666430015625 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Cinnamon = imports.gi.Cinnamon; const Main = imports.ui.main; /** * FILE:scripting.js * @short_description: Scripts for driving cinnamon in an automated fasion * * This module provides functionality for driving Cinnamon user interface * in an automated fashion. The primary current use case for this is * automated performance testing (see runPerfScript()), but it could * be applied to other forms of automation, such as testing for * correctness as well. * * When scripting an automated test we want to make a series of calls * in a linear fashion, but we also want to be able to let the main * loop run so actions can finish. For this reason we write the script * as a generator function that yields when it want to let the main * loop run. * * yield Scripting.sleep(1000); * main.overview.show(); * yield Scripting.waitLeisure(); * * While it isn't important to the person writing the script, the actual * yielded result is a function that the caller uses to provide the * callback for resuming the script. */ /** * sleep: * @milliseconds (int): number of milliseconds to wait * * Used within an automation script to pause the the execution of the * current script for the specified amount of time. Use as * 'yield Scripting.sleep(500);' */ function sleep(milliseconds) { let cb; Mainloop.timeout_add(milliseconds, function() { if (cb) cb(); return false; }); return function(callback) { cb = callback; }; } /** * waitLeisure: * * Used within an automation script to pause the the execution of the * current script until Cinnamon is completely idle. Use as * 'yield Scripting.waitLeisure();' */ function waitLeisure() { let cb; global.run_at_leisure(function() { if (cb) cb(); }); return function(callback) { cb = callback; }; } const PerfHelperIface = ' \ \ \ \ \ \ \ \ \ \ \ '; var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface); function PerfHelper() { return new PerfHelperProxy(Gio.DBus.session, 'org.Cinnamon.PerfHelper', '/org/Cinnamon/PerfHelper'); } let _perfHelper = null; function _getPerfHelper() { if (_perfHelper == null) _perfHelper = new PerfHelper(); return _perfHelper; } /** * createTestWindow: * @width (int): width of window, in pixels * @height (int): height of window, in pixels * @alpha (boolean): whether the window should be alpha transparent * @maximized (boolean): whethe the window should be created maximized * * Creates a window using cinnamon-perf-helper for testing purposes. * While this function can be used with yield in an automation * script to pause until the D-Bus call to the helper process returns, * because of the normal X asynchronous mapping process, to actually wait * until the window has been mapped and exposed, use waitTestWindows(). */ function createTestWindow(width, height, alpha, maximized) { let cb; let perfHelper = _getPerfHelper(); perfHelper.CreateWindowRemote(width, height, alpha, maximized, function(result, excp) { if (cb) cb(); }); return function(callback) { cb = callback; }; } /** * waitTestWindows: * * Used within an automation script to pause until all windows previously * created with createTestWindow have been mapped and exposed. */ function waitTestWindows() { let cb; let perfHelper = _getPerfHelper(); perfHelper.WaitWindowsRemote(function(result, excp) { if (cb) cb(); }); return function(callback) { cb = callback; }; } /** * destroyTestWindows: * * Destroys all windows previously created with createTestWindow(). * While this function can be used with yield in an automation * script to pause until the D-Bus call to the helper process returns, * this doesn't guarantee that Muffin has actually finished the destroy * process because of normal X asynchronicity. */ function destroyTestWindows() { let cb; let perfHelper = _getPerfHelper(); perfHelper.DestroyWindowsRemote(function(result, excp) { if (cb) cb(); }); return function(callback) { cb = callback; }; } /** * defineScriptEvent * @name (string): The event will be called script. * @description (string): Short human-readable description of the event * * Convenience function to define a zero-argument performance event * within the 'script' namespace that is reserved for events defined locally * within a performance automation script */ function defineScriptEvent(name, description) { Cinnamon.PerfLog.get_default().define_event("script." + name, description, ""); } /** * scriptEvent * @name (string): Name registered with defineScriptEvent() * * Convenience function to record a script-local performance event * previously defined with defineScriptEvent */ function scriptEvent(name) { Cinnamon.PerfLog.get_default().event("script." + name); } /** * collectStatistics * * Convenience function to trigger statistics collection */ function collectStatistics() { Cinnamon.PerfLog.get_default().collect_statistics(); } function _step(g, finish, onError) { try { let waitFunction = g.next(); waitFunction(function() { _step(g, finish, onError); }); } catch (err if err instanceof StopIteration) { if (finish) finish(); } catch (err) { if (onError) onError(err); } } function _collect(scriptModule, outputFile) { let eventHandlers = {}; for (let f in scriptModule) { let m = /([A-Za-z]+)_([A-Za-z]+)/.exec(f); if (m) eventHandlers[m[1] + "." + m[2]] = scriptModule[f]; } Cinnamon.PerfLog.get_default().replay( function(time, eventName, signature, arg) { if (eventName in eventHandlers) eventHandlers[eventName](time, arg); }); if ('finish' in scriptModule) scriptModule.finish(); if (outputFile) { let f = Gio.file_new_for_path(outputFile); let raw = f.replace(null, false, Gio.FileCreateFlags.NONE, null); let out = Gio.BufferedOutputStream.new_sized (raw, 4096); Cinnamon.write_string_to_stream (out, "{\n"); Cinnamon.write_string_to_stream(out, '"events":\n'); Cinnamon.PerfLog.get_default().dump_events(out); let monitors = Main.layoutManager.monitors; let primary = Main.layoutManager.primaryIndex; Cinnamon.write_string_to_stream(out, ',\n"monitors":\n['); for (let i = 0; i < monitors.length; i++) { let monitor = monitors[i]; if (i != 0) Cinnamon.write_string_to_stream(out, ', '); Cinnamon.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(i == primary ? "*" : "", monitor.width, monitor.height, monitor.x, monitor.y)); } Cinnamon.write_string_to_stream(out, ' ]'); Cinnamon.write_string_to_stream(out, ',\n"metrics":\n[ '); let first = true; for (let name in scriptModule.METRICS) { let metric = scriptModule.METRICS[name]; // Extra checks here because JSON.stringify generates // invalid JSON for undefined values if (metric.description == null) { log("Error: No description found for metric " + name); continue; } if (metric.units == null) { log("Error: No units found for metric " + name); continue; } if (metric.value == null) { log("Error: No value found for metric " + name); continue; } if (!first) Cinnamon.write_string_to_stream(out, ',\n '); first = false; Cinnamon.write_string_to_stream(out, '{ "name": ' + JSON.stringify(name) + ',\n' + ' "description": ' + JSON.stringify(metric.description) + ',\n' + ' "units": ' + JSON.stringify(metric.units) + ',\n' + ' "value": ' + JSON.stringify(metric.value) + ' }'); } Cinnamon.write_string_to_stream(out, ' ]'); Cinnamon.write_string_to_stream (out, ',\n"log":\n'); Cinnamon.PerfLog.get_default().dump_log(out); Cinnamon.write_string_to_stream (out, '\n}\n'); out.close(null); } else { let metrics = []; for (let metric in scriptModule.METRICS) metrics.push(metric); metrics.sort(); print ('------------------------------------------------------------'); for (let i = 0; i < metrics.length; i++) { let metric = metrics[i]; print ('# ' + scriptModule.METRIC_DESCRIPTIONS[metric]); print (metric + ': ' + scriptModule.METRICS[metric]); } print ('------------------------------------------------------------'); } } /** * runPerfScript: * @scriptModule: module object with run and finish functions * and event handlers * * Runs a script for automated collection of performance data. The * script is defined as a Javascript module with specified contents. * * First the run() function within the module will be called as a * generator to automate a series of actions. These actions will * trigger performance events and the script can also record its * own performance events. * * Then the recorded event log is replayed using handler functions * within the module. The handler for the event 'foo.bar' is called * foo_bar(). * * Finally if the module has a function called finish(), that will * be called. * * The event handler and finish functions are expected to fill in * metrics to an object within the module called METRICS. Each * property of this object represents an individual metric. The * name of the property is the name of the metric, the value * of the property is an object with the following properties: * * description: human readable description of the metric * units: a string representing the units of the metric. It has * the form ' ... / / ...'. Certain * unit values are recognized: s, ms, us, B, KiB, MiB. Other * values can appear but are uninterpreted. Examples 's', * '/ s', 'frames', 'frames / s', 'MiB / s / frame' * value: computed value of the metric * * The resulting metrics will be written to @outputFile as JSON, or, * if @outputFile is not provided, logged. * * After running the script and collecting statistics from the * event log, Cinnamon will exit. */ function runPerfScript(scriptModule, outputFile) { Cinnamon.PerfLog.get_default().set_enabled(true); let g = scriptModule.run(); _step(g, function() { _collect(scriptModule, outputFile); Meta.exit(Meta.ExitCode.SUCCESS); }, function(err) { log("Script failed: " + err + "\n" + err.stack); Meta.exit(Meta.ExitCode.ERROR); }); } Cinnamon-2.8.6/js/ui/deskletManager.js0000664000175000017500000003761012625666430016553 0ustar fabiofabio// -*- indent-tabs-mode: nil -*- const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const St = imports.gi.St; const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const Desklet = imports.ui.desklet; const DND = imports.ui.dnd; const Extension = imports.ui.extension; const Main = imports.ui.main; // Maps uuid -> metadata object var deskletMeta; // Maps uuid -> importer object (desklet directory tree) var desklets; // Maps uuid -> desklet objects var deskletObj = {}; var enabledDeskletDefinitions; var deskletsLoaded = false; var deskletsDragging = false; let userDeskletsDir; let mouseTrackEnabled = false; let mouseTrackTimoutId = 0; const ENABLED_DESKLETS_KEY = 'enabled-desklets'; const DESKLET_SNAP_KEY = 'desklet-snap'; const DESKLET_SNAP_INTERVAL_KEY = 'desklet-snap-interval'; /** * init: * * Initialize desklet manager */ function init(){ desklets = Extension.Type.DESKLET.maps.importObjects; deskletMeta = Extension.Type.DESKLET.maps.meta; deskletsLoaded = false enabledDeskletDefinitions = getEnabledDeskletDefinitions(); let hasDesklets = false; for (let uuid in enabledDeskletDefinitions.uuidMap) { if(Extension.loadExtension(uuid, Extension.Type.DESKLET)) hasDesklets = true; } global.settings.connect('changed::' + ENABLED_DESKLETS_KEY, _onEnabledDeskletsChanged); global.settings.connect('changed::' + DESKLET_SNAP_KEY, _onDeskletSnapChanged); global.settings.connect('changed::' + DESKLET_SNAP_INTERVAL_KEY, _onDeskletSnapChanged); deskletsLoaded = true; enableMouseTracking(true); } function enableMouseTracking(enable) { if(enable && !mouseTrackTimoutId) mouseTrackTimoutId = Mainloop.timeout_add(500, checkMouseTracking); else if (!enable && mouseTrackTimoutId) { Mainloop.source_remove(mouseTrackTimoutId); mouseTrackTimoutId = 0; for(let desklet_id in deskletObj) { deskletObj[desklet_id]._untrackMouse(); } } } function hasMouseWindow(){ let window = global.screen.get_mouse_window(null); return window && window.window_type != Meta.WindowType.DESKTOP; } function checkMouseTracking() { let enable = !hasMouseWindow(); if(mouseTrackEnabled != enable) { mouseTrackEnabled = enable; if(enable) { for(let desklet_id in deskletObj) { deskletObj[desklet_id]._trackMouse(); } } else { for(let desklet_id in deskletObj) { deskletObj[desklet_id]._untrackMouse(); } } } return true; } /** * removeDesklet: * @uuid (string): uuid of the desklet * @desklet_id (int): id of the desklet * * Disable and remove the desklet @uuid:@desklet_id */ function removeDesklet(uuid, desklet_id){ let list = global.settings.get_strv(ENABLED_DESKLETS_KEY); for (let i = 0; i < list.length; i++){ let definition = list[i]; let elements = definition.split(":"); if (uuid == elements[0] && desklet_id == elements[1]) list.splice(i, 1); } global.settings.set_strv(ENABLED_DESKLETS_KEY, list); } /** * getEnabledDeskletDefinitons: * * Gets the list of enabled desklets. Returns an associative array of three items: * raw: the unprocessed array from gsettings * uuidMap: maps uuid -> list of desklet definitions * idMap: maps desklet_id -> single desklet definition * * Returns (dictionary): Associative array of three items */ function getEnabledDeskletDefinitions() { let result = { // the raw list from gsettings raw: global.settings.get_strv(ENABLED_DESKLETS_KEY), // maps uuid -> list of desklet definitions uuidMap: {}, // maps desklet_id -> single desklet definition idMap: {} }; // Parse all definitions for (let i=0; i interval / 2; let y_next = Math.abs(actor.get_y() - this.last_y) > interval / 2; if (actor.get_x() < this.last_x) { if (x_next) { x = Math.floor(actor.get_x()/interval) * interval; } else { x = Math.ceil(actor.get_x()/interval) * interval; } } else { if (x_next) { x = Math.ceil(actor.get_x()/interval) * interval; } else { x = Math.floor(actor.get_x()/interval) * interval; } } if (actor.get_y() < this.last_y) { if (y_next) { y = Math.floor(actor.get_y()/interval) * interval; } else { y = Math.ceil(actor.get_y()/interval) * interval; } } else { if (y_next) { y = Math.ceil(actor.get_y()/interval) * interval; } else { y = Math.floor(actor.get_y()/interval) * interval; } } this._dragPlaceholder.set_position(x,y); this._dragPlaceholder.set_size(actor.get_width(), actor.get_height()); this.last_x = x; this.last_y = y; return DND.DragMotionResult.MOVE_DROP; }, acceptDrop: function(source, actor, x, y, time) { if (!(source instanceof Desklet.Desklet)) return false; Main.uiGroup.remove_actor(actor); this.actor.add_actor(actor); mouseTrackEnabled = -1; // forces an update of all desklet mouse tracks checkMouseTracking(); // Update GSettings let enabledDesklets = global.settings.get_strv(ENABLED_DESKLETS_KEY); for (let i = 0; i < enabledDesklets.length; i++){ let definition = enabledDesklets[i]; if (definition.indexOf(source._uuid + ":" + source.instance_id) == 0){ let elements = definition.split(":"); elements[2] = actor.get_x(); elements[3] = actor.get_y(); if (global.settings.get_boolean(DESKLET_SNAP_KEY)){ elements[2] = this._dragPlaceholder.x elements[3] = this._dragPlaceholder.y; } definition = elements.join(":"); enabledDesklets[i] = definition; } } global.settings.set_strv(ENABLED_DESKLETS_KEY, enabledDesklets); this._dragPlaceholder.hide(); this.last_x = -1; this.last_y = -1; return true; }, cancelDrag: function(source, actor) { if (!(source instanceof Desklet.Desklet)) return false; Main.uiGroup.remove_actor(actor); this.actor.add_actor(actor); mouseTrackEnabled = -1; checkMouseTracking(); this._dragPlaceholder.hide(); this.last_x = -1; this.last_y = -1; return true; }, hideDragPlaceholder: function() { this._dragPlaceholder.hide(); } }; Cinnamon-2.8.6/js/ui/cinnamonMountOperation.js0000664000175000017500000003546312625666430020337 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Signals = imports.signals; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Pango = imports.gi.Pango; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const ModalDialog = imports.ui.modalDialog; const Params = imports.misc.params; const LIST_ITEM_ICON_SIZE = 48; /* ------ Common Utils ------- */ function _setLabelText(label, text) { if (text) { label.set_text(text); label.show(); } else { label.set_text(''); label.hide(); } } function _setButtonsForChoices(dialog, choices) { let buttons = []; for (let idx = 0; idx < choices.length; idx++) { let button = idx; buttons.unshift({ label: choices[idx], action: Lang.bind(dialog, function() { dialog.emit('response', button); })}); } dialog.setButtons(buttons); } function _setLabelsForMessage(dialog, message) { let labels = message.split('\n'); _setLabelText(dialog.subjectLabel, labels[0]); if (labels.length > 1) _setLabelText(dialog.descriptionLabel, labels[1]); } /* -------------------------------------------------------- */ function ListItem(app) { this._init(app); } ListItem.prototype = { _init: function(app) { this._app = app; let layout = new St.BoxLayout({ vertical: false}); this.actor = new St.Button({ style_class: 'show-processes-dialog-app-list-item', can_focus: true, child: layout, reactive: true, x_align: St.Align.START, x_fill: true }); this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE); let iconBin = new St.Bin({ style_class: 'show-processes-dialog-app-list-item-icon', child: this._icon }); layout.add(iconBin); this._nameLabel = new St.Label({ text: this._app.get_name(), style_class: 'show-processes-dialog-app-list-item-name' }); let labelBin = new St.Bin({ y_align: St.Align.MIDDLE, child: this._nameLabel }); layout.add(labelBin); this.actor.connect('clicked', Lang.bind(this, this._onClicked)); }, _onClicked: function() { this.emit('activate'); this._app.activate(); } }; Signals.addSignalMethods(ListItem.prototype); function CinnamonMountOperation(source, params) { this._init(source, params); } CinnamonMountOperation.prototype = { _init: function(source, params) { params = Params.parse(params, { reaskPassword: false }); this._reaskPassword = params.reaskPassword; this._dialog = null; this._processesDialog = null; this.mountOp = new Cinnamon.MountOperation(); this.mountOp.connect('ask-question', Lang.bind(this, this._onAskQuestion)); this.mountOp.connect('ask-password', Lang.bind(this, this._onAskPassword)); this.mountOp.connect('show-processes-2', Lang.bind(this, this._onShowProcesses2)); this.mountOp.connect('aborted', Lang.bind(this, this._onAborted)); this._icon = new St.Icon({ gicon: source.get_icon(), style_class: 'cinnamon-mount-operation-icon' }); }, _onAskQuestion: function(op, message, choices) { this._dialog = new CinnamonMountQuestionDialog(this._icon); this._dialog.connect('response', Lang.bind(this, function(object, choice) { this.mountOp.set_choice(choice); this.mountOp.reply(Gio.MountOperationResult.HANDLED); this._dialog.close(global.get_current_time()); this._dialog = null; })); this._dialog.update(message, choices); this._dialog.open(global.get_current_time()); }, _onAskPassword: function(op, message) { this._notificationShowing = true; this._source = new CinnamonMountPasswordSource(message, this._icon, this._reaskPassword); this._source.connect('password-ready', Lang.bind(this, function(source, password) { this.mountOp.set_password(password); this.mountOp.reply(Gio.MountOperationResult.HANDLED); this._notificationShowing = false; this._source.destroy(); })); this._source.connect('destroy', Lang.bind(this, function() { if (!this._notificationShowing) return; this._notificationShowing = false; this.mountOp.reply(Gio.MountOperationResult.ABORTED); })); }, _onAborted: function(op) { if (!this._dialog) return; this._dialog.close(global.get_current_time()); this._dialog = null; }, _onShowProcesses2: function(op) { let processes = op.get_show_processes_pids(); let choices = op.get_show_processes_choices(); let message = op.get_show_processes_message(); if (!this._processesDialog) { this._processesDialog = new CinnamonProcessesDialog(this._icon); this._dialog = this._processesDialog; this._processesDialog.connect('response', Lang.bind(this, function(object, choice) { if (choice == -1) { this.mountOp.reply(Gio.MountOperationResult.ABORTED); } else { this.mountOp.set_choice(choice); this.mountOp.reply(Gio.MountOperationResult.HANDLED); } this._processesDialog.close(global.get_current_time()); this._dialog = null; })); this._processesDialog.open(global.get_current_time()); } this._processesDialog.update(message, processes, choices); }, } function CinnamonMountQuestionDialog(icon) { this._init(icon); } CinnamonMountQuestionDialog.prototype = { __proto__: ModalDialog.ModalDialog.prototype, _init: function(icon) { ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' }); let mainContentLayout = new St.BoxLayout(); this.contentLayout.add(mainContentLayout, { x_fill: true, y_fill: false }); this._iconBin = new St.Bin({ child: icon }); mainContentLayout.add(this._iconBin, { x_fill: true, y_fill: false, x_align: St.Align.END, y_align: St.Align.MIDDLE }); let messageLayout = new St.BoxLayout({ vertical: true }); mainContentLayout.add(messageLayout, { y_align: St.Align.START }); this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' }); messageLayout.add(this.subjectLabel, { y_fill: false, y_align: St.Align.START }); this.descriptionLabel = new St.Label({ style_class: 'mount-question-dialog-description' }); this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.descriptionLabel.clutter_text.line_wrap = true; messageLayout.add(this.descriptionLabel, { y_fill: true, y_align: St.Align.START }); }, update: function(message, choices) { _setLabelsForMessage(this, message); _setButtonsForChoices(this, choices); } } Signals.addSignalMethods(CinnamonMountQuestionDialog.prototype); function CinnamonMountPasswordSource(message, icon, reaskPassword) { this._init(message, icon, reaskPassword); } CinnamonMountPasswordSource.prototype = { __proto__: MessageTray.Source.prototype, _init: function(message, icon, reaskPassword) { let strings = message.split('\n'); MessageTray.Source.prototype._init.call(this, strings[0]); this._notification = new CinnamonMountPasswordNotification(this, strings, icon, reaskPassword); // add ourselves as a source, and popup the notification if (Main.messageTray) Main.messageTray.add(this); this.notify(this._notification); }, } Signals.addSignalMethods(CinnamonMountPasswordSource.prototype); function CinnamonMountPasswordNotification(source, strings, icon, reaskPassword) { this._init(source, strings, icon, reaskPassword); } CinnamonMountPasswordNotification.prototype = { __proto__: MessageTray.Notification.prototype, _init: function(source, strings, icon, reaskPassword) { MessageTray.Notification.prototype._init.call(this, source, strings[0], null, { customContent: true, icon: icon }); // set the notification to transient and urgent, so that it // expands out this.setTransient(true); this.setUrgency(MessageTray.Urgency.CRITICAL); if (strings[1]) this.addBody(strings[1]); if (reaskPassword) { let label = new St.Label({ style_class: 'mount-password-reask', text: _("Wrong password, please try again") }); this.addActor(label); } this._responseEntry = new St.Entry({ style_class: 'mount-password-entry', can_focus: true }); this.setActionArea(this._responseEntry); this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); this._responseEntry.clutter_text.set_password_char('\u25cf'); // U+25CF is the unicode BLACK CIRCLE this._responseEntry.grab_key_focus(); }, _onEntryActivated: function() { let text = this._responseEntry.get_text(); if (text == '') return; this.source.emit('password-ready', text); } } function CinnamonProcessesDialog(icon) { this._init(icon); } CinnamonProcessesDialog.prototype = { __proto__: ModalDialog.ModalDialog.prototype, _init: function(icon) { ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' }); let mainContentLayout = new St.BoxLayout(); this.contentLayout.add(mainContentLayout, { x_fill: true, y_fill: false }); this._iconBin = new St.Bin({ child: icon }); mainContentLayout.add(this._iconBin, { x_fill: true, y_fill: false, x_align: St.Align.END, y_align: St.Align.MIDDLE }); let messageLayout = new St.BoxLayout({ vertical: true }); mainContentLayout.add(messageLayout, { y_align: St.Align.START }); this.subjectLabel = new St.Label({ style_class: 'show-processes-dialog-subject' }); messageLayout.add(this.subjectLabel, { y_fill: false, y_align: St.Align.START }); this.descriptionLabel = new St.Label({ style_class: 'show-processes-dialog-description' }); this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.descriptionLabel.clutter_text.line_wrap = true; messageLayout.add(this.descriptionLabel, { y_fill: true, y_align: St.Align.START }); let scrollView = new St.ScrollView({ style_class: 'show-processes-dialog-app-list'}); scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); this.contentLayout.add(scrollView, { x_fill: true, y_fill: true }); scrollView.hide(); this._applicationList = new St.BoxLayout({ vertical: true }); scrollView.add_actor(this._applicationList, { x_fill: true, y_fill: true, x_align: St.Align.START, y_align: St.Align.MIDDLE }); this._applicationList.connect('actor-added', Lang.bind(this, function() { if (this._applicationList.get_children().length == 1) scrollView.show(); })); this._applicationList.connect('actor-removed', Lang.bind(this, function() { if (this._applicationList.get_children().length == 0) scrollView.hide(); })); }, _setAppsForPids: function(pids) { // remove all the items this._applicationList.destroy_children(); pids.forEach(Lang.bind(this, function(pid) { let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_app_from_pid(pid); if (!app) return; let item = new ListItem(app); this._applicationList.add(item.actor, { x_fill: true }); item.connect('activate', Lang.bind(this, function() { // use -1 to indicate Cancel this.emit('response', -1); })); })); }, update: function(message, processes, choices) { this._setAppsForPids(processes); _setLabelsForMessage(this, message); _setButtonsForChoices(this, choices); } } Signals.addSignalMethods(CinnamonProcessesDialog.prototype); Cinnamon-2.8.6/js/ui/indicatorManager.js0000664000175000017500000012647212625666430017101 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // Copyright (C) 2011 Giovanni Campagna // Copyright (C) 2013-2014 Jonas Kümmerlin // // 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 2 // 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, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Clutter = imports.gi.Clutter; const Cogl = imports.gi.Cogl; const GdkPixbuf = imports.gi.GdkPixbuf; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Lang = imports.lang; const Mainloop = imports.mainloop; const Signals = imports.signals; const Main = imports.ui.main; const DBusMenu = imports.ui.dbusMenu; const SignalManager = imports.misc.signalManager; const CinnamonConfig = imports.misc.config; const SNICategory = { APPLICATION: 'ApplicationStatus', COMMUNICATIONS: 'Communications', SYSTEM: 'SystemServices', HARDWARE: 'Hardware' }; const SNIStatus = { PASSIVE: 'Passive', ACTIVE: 'Active', NEEDS_ATTENTION: 'NeedsAttention' }; const LIFETIME_TIMESPAN = 5000; //5s const GC_INTERVAL = 10000; //10s // TODO: replace with org.freedesktop and /org/freedesktop when approved const KDE_PREFIX = 'org.kde'; const AYATANA_PREFIX = 'org.ayatana'; const AYATANA_PATH_PREFIX = '/org/ayatana'; const WATCHER_BUS_NAME = KDE_PREFIX + '.StatusNotifierWatcher'; const WATCHER_INTERFACE = WATCHER_BUS_NAME; const WATCHER_OBJECT = '/StatusNotifierWatcher'; const ITEM_OBJECT = '/StatusNotifierItem'; const StatusNotifierWatcherIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; /* * #IndicatorManager: * @short_description: Will get all newly added or changed indicators. * * The IndicatorManager class will get all newly added or changed indicators. * They will be shared in Cinnamon Main, so will be accessible for all clients. */ function IndicatorManager() { this._init(); } IndicatorManager.prototype = { _init: function() { this._indicators = {}; if (global.settings.get_boolean("enable-indicators")) { this.statusNotifierWatcher = new StatusNotifierWatcher(); this.statusNotifierWatcher.connect('indicator-dispatch', Lang.bind(this, this._onIndicatorDispatch)); } }, // handlers = { "signal": handler } _connectAndSaveId: function(target, handlers , idArray) { idArray = typeof idArray != 'undefined' ? idArray : []; for (let signal in handlers) { idArray.push(target.connect(signal, handlers[signal])); } return idArray; }, _disconnectSignals: function(obj, signalsHandlers) { if ((obj)&&(signalsHandlers)) { for (let pos in signalsHandlers) obj.disconnect(signalsHandlers[pos]); } }, getIndicatorIds: function() { let list = []; for (let id in this._indicators) { if (this._indicators[id].indicator.isReady) list.push(id); } return list; }, getIndicatorById: function(id) { if ((id in this._indicators) && (this._indicators[id].indicator.isReady)) return this._indicators[id].indicator; return null; }, _onIndicatorDispatch: function(notifierWatcher, id) { if (this.statusNotifierWatcher != null) { let appIndicator = notifierWatcher.getItemById(id); let signalsIndicator = this._connectAndSaveId(appIndicator, { 'ready' : Lang.bind(this, this._onIndicatorReady), 'destroy' : Lang.bind(this, this._onIndicatorDestroy), 'status' : Lang.bind(this, this._onIndicatorChanged), 'label' : Lang.bind(this, this._onIndicatorChanged), 'reset' : Lang.bind(this, this._onIndicatorChanged), 'overlay-icon' : Lang.bind(this, this._onIndicatorChanged), 'icon' : Lang.bind(this, this._onIndicatorChanged), }); /*let signalManager = new SignalManager.SignalManager(this); signalManager.connect(appIndicator, 'ready', this._onIndicatorReady); signalManager.connect(appIndicator, 'destroy', this._onIndicatorDestroy); signalManager.connect(appIndicator, 'status', this._onIndicatorChanged); signalManager.connect(appIndicator, 'label', this._onIndicatorChanged); signalManager.connect(appIndicator, 'reset', this._onIndicatorChanged); signalManager.connect(appIndicator, 'overlay-icon', this._onIndicatorChanged); signalManager.connect(appIndicator, 'icon', this._onIndicatorChanged);*/ this._indicators[id] = { indicator: appIndicator, signals: signalsIndicator }; if (appIndicator.isReady) { this._onIndicatorReady(appIndicator); } } }, _onIndicatorReady: function(appIndicator) { let id = appIndicator.getNotifierId(); if ((id != appIndicator.id) && !(appIndicator.id in this._indicators)) { this._indicators[appIndicator.id] = this._indicators[id]; delete this._indicators[id]; } this.emit('indicator-added', appIndicator); }, _onIndicatorDestroy: function(appIndicator) { if (appIndicator.id in this._indicators) { let properties = this._indicators[appIndicator.id]; this._disconnectSignals(appIndicator, properties.signals); delete this._indicators[appIndicator.id]; //properties.signals.disconnectAllSignals(); this.emit('indicator-removed', appIndicator); } }, _onIndicatorChanged: function(appIndicator, signal) { this.emit('indicator-changed', appIndicator); }, destroy: function() { if (this.statusNotifierWatcher) this.statusNotifierWatcher.destroy(); this.statusNotifierWatcher = null; } }; Signals.addSignalMethods(IndicatorManager.prototype); /** * #AppIndicator: * @short_description: This is a generic container for indicator information and common functions. * * The AppIndicator class serves as a generic container for indicator information and functions common * for every displaying implementation (IndicatorMessageSource and IndicatorStatusIcon) */ function AppIndicator() { this._init.apply(this, arguments); } AppIndicator.prototype = { _init: function(busName, object) { this._busName = busName; this._object = object; this._proxy = new XmlLessDBusProxy({ connection: Gio.DBus.session, name: busName, path: object, interface: 'org.kde.StatusNotifierItem', propertyWhitelist: [ //keep sorted alphabetically, please 'AttentionIconName', 'AttentionIconPixmap', 'Category', 'IconName', 'IconPixmap', 'IconThemePath', 'Id', 'Menu', 'OverlayIconName', 'OverlayIconPixmap', 'Status', 'Title', 'ToolTip', 'XAyatanaLabel' ], onReady: Lang.bind(this, function() { this.isReady = true; this.emit('ready'); }) }); this._proxy.connect('-property-changed', Lang.bind(this, this._onPropertyChanged)); this._proxy.connect('-signal', Lang.bind(this, this._translateNewSignals)); this._isInBlacklist = false; }, // The Author of the spec didn't like the PropertiesChanged signal, so he invented his own _translateNewSignals: function(proxy, signal, params) { if (signal.substr(0, 3) == 'New') { let prop = signal.substr(3); if (this._proxy.propertyWhitelist.indexOf(prop) > -1) this._proxy.invalidateProperty(prop); if (this._proxy.propertyWhitelist.indexOf(prop + 'Pixmap') > -1) this._proxy.invalidateProperty(prop + 'Pixmap'); if (this._proxy.propertyWhitelist.indexOf(prop + 'Name') > -1) this._proxy.invalidateProperty(prop + 'Name'); } else if (signal == 'XAyatanaNewLabel') { // and the ayatana guys made sure to invent yet another way of composing these signals... this._proxy.invalidateProperty('XAyatanaLabel'); } }, //public property getters get title() { return this._proxy.cachedProperties.Title; }, get id() { return this._proxy.cachedProperties.Id; }, get status() { return this._proxy.cachedProperties.Status; }, get label() { return this._proxy.cachedProperties.XAyatanaLabel; }, get attentionIcon() { return [ this._proxy.cachedProperties.AttentionIconName, this._proxy.cachedProperties.AttentionIconPixmap, this._proxy.cachedProperties.IconThemePath ]; }, get icon() { return [ this._proxy.cachedProperties.IconName, this._proxy.cachedProperties.IconPixmap, this._proxy.cachedProperties.IconThemePath ]; }, get overlayIcon() { return [ this._proxy.cachedProperties.OverlayIconName, this._proxy.cachedProperties.OverlayIconPixmap, this._proxy.cachedProperties.IconThemePath ]; }, getNotifierId: function() { return this._busName + this._object; }, isInBlacklist: function() { return this._isInBlacklist; }, setInBlacklist: function() { this._isInBlacklist = true; this.emit('blacklist'); }, getIconActor: function(size) { return new IconActor(this, size); }, //async because we may need to check the presence of a menubar object as well as the creation is async. createMenuClientAsync: function(clb) { let path = this._proxy.cachedProperties.Menu || "/MenuBar"; this._validateMenu(this._busName, path, function(correctly, name, path) { if (correctly) { // global.log("creating menu on "+[name, path]); clb(new DBusMenu.DBusClient(name, path)); } else { clb(null); } }); }, _validateMenu: function(bus, path, callback) { Gio.DBus.session.call( bus, path, "org.freedesktop.DBus.Properties", "Get", GLib.Variant.new("(ss)", ["com.canonical.dbusmenu", "Version"]), GLib.VariantType.new("(v)"), Gio.DBusCallFlags.NONE, -1, null, function(conn, result) { try { var val = conn.call_finish(result); } catch (e) { global.logWarning("Invalid menu: "+e); return callback(false); } var version = val.deep_unpack()[0].deep_unpack(); //FIXME: what do we implement? if (version >= 2) { return callback(true, bus, path); } else { global.logWarning("Incompatible dbusmenu version: "+version); return callback(false); } }, null ); }, _onPropertyChanged: function(proxy, property, newValue) { // some property changes require updates on our part, // a few need to be passed down to the displaying code // all these can mean that the icon has to be changed if (property == 'Status' || property.substr(0, 4) == 'Icon' || property.substr(0, 13) == 'AttentionIcon') this.emit('icon'); // same for overlays if (property.substr(0, 11) == 'OverlayIcon') this.emit('overlay-icon'); // this may make all of our icons invalid if (property == 'IconThemePath') { this.emit('icon'); this.emit('overlay-icon'); } // the label will be handled elsewhere if (property == 'XAyatanaLabel') this.emit('label'); // status updates are important for the StatusNotifierDispatcher if (property == 'Status') this.emit('status'); }, // triggers a reload of all properties reset: function(triggerReady) { this._proxy.invalidateAllProperties(Lang.bind(this, function() { this.emit('reset'); })); }, destroy: function() { this.emit('destroy'); this.disconnectAll(); this._proxy.destroy(); }, open: function() { // FIXME: The original implementation don't use WindowID, because is not able to get // the x11 window id from a MetaWindow nor can we call any X11 functions, but is // possible on cinnamon. Luckily for Gnome Shell, the Activate method usually works fine. // parameters are "an hint to the item where to show eventual windows" [sic] // ... and don't seem to have any effect. this._proxy.call({ name: 'Activate', paramTypes: 'ii', paramValues: [0, 0] // we don't care about the result }); }, scroll: function(dx, dy) { if (dx != 0) this._proxy.call({ name: 'Scroll', paramTypes: 'is', paramValues: [ Math.floor(dx), 'horizontal' ] }); if (dy != 0) this._proxy.call({ name: 'Scroll', paramTypes: 'is', paramValues: [ Math.floor(dy), 'vertical' ] }); } }; Signals.addSignalMethods(AppIndicator.prototype); /* * #StatusNotifierWatcher: * @short_description: The class implements the StatusNotifierWatcher dbus object. */ function StatusNotifierWatcher() { this._init(); } StatusNotifierWatcher.prototype = { _init: function() { this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(StatusNotifierWatcherIface, this); this._dbusImpl.export(Gio.DBus.session, WATCHER_OBJECT); this._everAcquiredName = false; this._ownName = Gio.DBus.session.own_name(WATCHER_BUS_NAME, Gio.BusNameOwnerFlags.NONE, Lang.bind(this, this._acquiredName), Lang.bind(this, this._lostName)); this._items = { }; this._nameWatcher = { }; }, _acquiredName: function() { this._everAcquiredName = true; }, _lostName: function() { if (this._everAcquiredName) global.log('Lost name' + WATCHER_BUS_NAME); else global.logWarning('Failed to acquire ' + WATCHER_BUS_NAME); }, // create a unique index for the _items dictionary _getItemId: function(busName, objPath) { return busName + objPath; }, getItemById: function(id) { if (id in this._items) return this._items[id]; return null; }, getIdForItem: function(item) { if (id in this._items) return this._items[id]; return null; }, RegisterStatusNotifierItemAsync: function(params, invocation) { // it would be too easy if all application behaved the same // instead, ayatana patched gnome apps to send a path // while kde apps send a bus name let service = params[0]; let busName, objPath; if (service.charAt(0) == '/') { // looks like a path busName = invocation.get_sender(); objPath = service; } else { // we hope it is a bus name busName = service; objPath = ITEM_OBJECT; } let id = this._getItemId(busName, objPath); if (this._items[id]) { //delete the old one and add the new indicator // global.logWarning("Attempting to re-register "+id+"; resetting instead"); this._items[id].reset(); } else { this._items[id] = new AppIndicator(busName, objPath); this._dbusImpl.emit_signal('ServiceRegistered', GLib.Variant.new('(s)', service)); this._nameWatcher[id] = Gio.DBus.session.watch_name(busName, Gio.BusNameWatcherFlags.NONE, null, Lang.bind(this, this._itemVanished)); this.emit('indicator-dispatch', id); this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems', GLib.Variant.new('as', this.RegisteredStatusNotifierItems)); } invocation.return_value(null); }, _itemVanished: function(proxy, busName) { // FIXME: this is useless if the path name disappears while the bus stays alive (not unheard of) for (let id in this._items) { if (id.indexOf(busName) == 0) { this._remove(id); } } }, _remove: function(id) { this._items[id].destroy(); delete this._items[id]; Gio.DBus.session.unwatch_name(this._nameWatcher[id]); delete this._nameWatcher[id]; this._dbusImpl.emit_signal('ServiceUnregistered', GLib.Variant.new('(s)', id)); this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems', GLib.Variant.new('as', this.RegisteredStatusNotifierItems)); }, RegisterNotificationHost: function(service) { throw new Gio.DBusError('org.gnome.Shell.UnsupportedMethod', 'Registering additional notification hosts is not supported'); }, IsNotificationHostRegistered: function() { return true; }, ProtocolVersion: function() { // "The version of the protocol the StatusNotifierWatcher instance implements." [sic] // in what syntax? return "%s/%s (KDE; compatible; mostly) Cinnamon/%s".format("systray-indicator@cinnamon.org", "0.1", CinnamonConfig.PACKAGE_VERSION); }, get RegisteredStatusNotifierItems() { return Object.keys(this._items); }, get IsStatusNotifierHostRegistered() { return true; }, destroy: function() { if (!this._isDestroyed) { // this doesn't do any sync operation and doesn't allow us to hook up the event of being finished // which results in our unholy debounce hack (see extension.js) Gio.DBus.session.unown_name(this._ownName); this._dbusImpl.unexport(); for (let i in this._nameWatcher) { Gio.DBus.session.unwatch_name(this._nameWatcher[i]); } delete this._nameWatcher; for (let i in this._items) { this._items[i].destroy(); } delete this._items; this._isDestroyed = true; } } }; Signals.addSignalMethods(StatusNotifierWatcher.prototype); /** * #XmlLessDBusProxy: * @short_description: This is a proxy for org.freedesktop.DBus.Properties and org.kde.StatusNotifierItem. * * This proxy works completely without an interface xml, making it both flexible * and mistake-prone. It will cache properties and emit events, and provides * shortcuts for calling methods. */ function XmlLessDBusProxy(params) { this._init(params); } XmlLessDBusProxy.prototype = { _init: function(params) { if (!params.connection || !params.name || !params.path || !params.interface) throw new Error("XmlLessDBusProxy: please provide connection, name, path and interface"); this.connection = params.connection; this.name = params.name; this.path = params.path; this.interface = params.interface; this.propertyWhitelist = params.propertyWhitelist || []; this.cachedProperties = {}; this.invalidateAllProperties(params.onReady); this._signalId = this.connection.signal_subscribe(this.name, this.interface, null, this.path, null, Gio.DBusSignalFlags.NONE, this._onSignal.bind(this)); this._propChangedId = this.connection.signal_subscribe(this.name, 'org.freedesktop.DBus.Properties', 'PropertiesChanged', this.path, null, Gio.DBusSignalFlags.NONE, this._onPropertyChanged.bind(this)) }, setProperty: function(propertyName, valueVariant) { //TODO: implement }, // Initiates recaching the given property. // // This is useful if the interface notifies the consumer of changed properties // in unorthodox ways or if you changed the whitelist invalidateProperty: function(propertyName, callback) { this.connection.call(this.name, this.path, 'org.freedesktop.DBus.Properties', 'Get', GLib.Variant.new('(ss)', [ this.interface, propertyName ]), GLib.VariantType.new('(v)'), Gio.DBusCallFlags.NONE, -1, null, Lang.bind(this, this._getPropertyCallback, propertyName, callback)); }, _getPropertyCallback: function(conn, result, propertyName, callback) { try { let newValue = conn.call_finish(result).deep_unpack()[0].deep_unpack(); if (this.propertyWhitelist.indexOf(propertyName) > -1) { this.cachedProperties[propertyName] = newValue; this.emit("-property-changed", propertyName, newValue); this.emit("-property-changed::"+propertyName, newValue); } } catch (e) { // this can mean two things: // - the interface is gone (or doesn't conform or whatever) // - the property doesn't exist // we do not care and we don't even log it. //global.logWarning("XmlLessDBusProxy: while getting property: "+e) } if (callback) callback(); }, invalidateAllProperties: function(callback) { let waitFor = 0; this.propertyWhitelist.forEach(function(prop) { waitFor += 1; this.invalidateProperty(prop, maybeFinished); }, this); function maybeFinished() { waitFor -= 1; if (waitFor == 0 && callback) callback(); } }, _onPropertyChanged: function(conn, sender, path, iface, signal, params) { let [ , changed, invalidated ] = params.deep_unpack(); for (let i in changed) { if (this.propertyWhitelist.indexOf(i) > -1) { this.cachedProperties[i] = changed[i].deep_unpack(); this.emit("-property-changed", i, this.cachedProperties[i]); this.emit("-property-changed::"+i, this.cachedProperties[i]); } } for (let i = 0; i < invalidated.length; ++i) { if (this.propertyWhitelist.indexOf(invalidated[i]) > -1) this.invalidateProperty(invalidated[i]); } }, _onSignal: function(conn, sender, path, iface, signal, params) { this.emit("-signal", signal, params); this.emit(signal, params.deep_unpack()); }, call: function(params) { if (!params) throw new Error("XmlLessDBusProxy::call: need params argument"); if (!params.name) throw new Error("XmlLessDBusProxy::call: missing name"); if (params.params instanceof GLib.Variant) { // good! } else if (params.paramTypes && params.paramValues) { params.params = GLib.Variant.new('(' + params.paramTypes + ')', params.paramValues); } else { throw new Error("XmlLessDBusProxy::call: provide either paramType (string) and \ paramValues (array) or params (GLib.Variant)"); } if (!params.returnTypes) params.returnTypes = ''; if (!params.onSuccess) params.onSuccess = function() {}; if (!params.onError) params.onError = function(error) { global.logWarning("XmlLessDBusProxy::call: DBus error: "+error); }; this.connection.call(this.name, this.path, this.interface, params.name, params.params, GLib.VariantType.new('(' + params.returnTypes + ')'), Gio.DBusCallFlags.NONE, -1, null, function(conn, result) { try { let returnVariant = conn.call_finish(result) params.onSuccess(returnVariant.deep_unpack()) } catch (e) { params.onError(e) } }); }, destroy: function() { this.emit('-destroy'); this.disconnectAll(); this.connection.signal_unsubscribe(this._signalId); this.connection.signal_unsubscribe(this._propChangedId); } }; Signals.addSignalMethods(XmlLessDBusProxy.prototype); /** * #IconActor: * @short_description: This is a container for the indicator icon with some advaced features. */ function IconActor() { this._init.apply(this, arguments); } IconActor.prototype = { _init: function(indicator, size) { this.actor = new St.BoxLayout({ style_class: 'applet-box', reactive: true, track_hover: true }); this.actor._delegate = this; this._menu = null; this.menuSignal = 0; this._indicator = indicator; this._iconSize = size; this._iconCache = new IconCache(); this._mainIcon = new St.Bin(); this._overlayIcon = new St.Bin({ 'x-align': St.Align.END, 'y-align': St.Align.END }); this.actor.add_actor(this._mainIcon); this.actor.add_actor(this._overlayIcon); this._signalManager = new SignalManager.SignalManager(this); this._signalManager.connect(this.actor, 'scroll-event', this._handleScrollEvent); this._signalManager.connect(Gtk.IconTheme.get_default(), 'changed', this._invalidateIcon); //this._signalManager.connect(this._indicator, 'icon', this._updateIcon); //this._signalManager.connect(this._indicator, 'overlay-icon', this._updateOverlayIcon); //this._signalManager.connect(this._indicator, 'ready', this._invalidateIcon); //this._signalManager.connect(this._indicator, 'status', this._updatedStatus); //this._signalManager.connect(this._indicator, 'blacklist', this._updatedBlacklist); //this._signalManager.connect(this._indicator, 'blacklist', this.destroy); this._signalsIndicator = this._connectAndSaveId(this._indicator, { 'icon' : Lang.bind(this, this._updateIcon), 'overlay-icon': Lang.bind(this, this._updateOverlayIcon), 'ready' : Lang.bind(this, this._invalidateIcon), 'status' : Lang.bind(this, this._updatedStatus), 'blacklist' : Lang.bind(this, this._updatedBlacklist), 'destroy' : Lang.bind(this, this.destroy), }); if (indicator.isReady) this._invalidateIcon(); }, // handlers = { "signal": handler } _connectAndSaveId: function(target, handlers , idArray) { idArray = typeof idArray != 'undefined' ? idArray : []; for (let signal in handlers) { idArray.push(target.connect(signal, handlers[signal])); } return idArray; }, _disconnectSignals: function(obj, signalsHandlers) { if ((obj)&&(signalsHandlers)) { for (let pos in signalsHandlers) obj.disconnect(signalsHandlers[pos]); } }, setMenu: function(menu) { if (this._menuSignal > 0) { this.actor.disconnect(this._menuSignal); this._menuSignal = 0; } if (menu) { this._menuSignal = this.actor.connect('button-press-event', Lang.bind(this, this._onIconButtonPressEvent)); this._menu = menu; } }, setSize: function(size) { if (this._iconSize != size) { this._iconSize = size; this._invalidateIcon(); } }, _updatedBlacklist: function() { if (this._indicator.isInBlacklist()) this.actor.visible = false; }, _updatedStatus: function() { if (this._indicator.status == SNIStatus.PASSIVE) this.actor.visible = false; else if ((this._indicator.status == SNIStatus.ACTIVE || this._indicator.status == SNIStatus.NEEDS_ATTENTION) && !this._indicator.isInBlacklist()) this.actor.visible = true; if ((this._menu)&&(!this.actor.visible)) this._menu.close(false); }, // Will look the icon up in the cache, if it's found // it will return it. Otherwise, it will create it and cache it. // The .inUse flag will be set to true. So when you don't need // the returned icon anymore, make sure to check the .inUse property // and set it to false if needed so that it can be picked up by the garbage // collector. _cacheOrCreateIconByName: function(iconSize, iconName, themePath) { let id = iconName + '@' + iconSize + (themePath ? '##' + themePath : ''); let icon = this._iconCache.get(id) || this._createIconByName(iconSize, iconName, themePath); if (icon) { icon.inUse = true; this._iconCache.add(id, icon); } return icon; }, _onIconButtonPressEvent: function(actor, event) { let draggableParent = this._getDragable(); if (((draggableParent)&&(!draggableParent.inhibit)) || (!this._menu)) return false; if (event.get_button() == 1) this._menu.toggle(); return false; }, // FIXME: We can move this outsite the applet, or otherwise, the user of the api // need to provide an actor._delegate Object for the dragable parent actor. _getDragable: function() { let actorDragable = this.actor.get_parent(); while (actorDragable) { if ((actorDragable._delegate)&&(actorDragable._delegate._draggable)) return actorDragable._delegate._draggable; actorDragable = this.actor.get_parent(); } return null; }, _createIconByName: function(size, name, themePath) { // realSize will contain the actual icon size in contrast to the requested icon size let realSize = size; let gicon = null; if (name && name[0] == "/") { //HACK: icon is a path name. This is not specified by the api but at least inidcator-sensors uses it. let [ format, width, height ] = GdkPixbuf.Pixbuf.get_file_info(name); if (!format) { global.logError("invalid image format: "+name); } else { // if the actual icon size is smaller, save that for later. // scaled icons look ugly. if (Math.max(width, height) < size) realSize = Math.max(width, height); gicon = Gio.icon_new_for_string(name); } } else if (name) { // we manually look up the icon instead of letting st.icon do it for us // this allows us to sneak in an indicator provided search path and to avoid ugly upscaled icons // indicator-application looks up a special "panel" variant, we just replicate that here name = name + "-panel"; // icon info as returned by the lookup let iconInfo = null; // we try to avoid messing with the default icon theme, so we'll create a new one if needed if (themePath) { var icon_theme = new Gtk.IconTheme(); Gtk.IconTheme.get_default().get_search_path().forEach(function(path) { icon_theme.append_search_path(path); }); icon_theme.append_search_path(themePath); icon_theme.set_screen(imports.gi.Gdk.Screen.get_default()); } else { var icon_theme = Gtk.IconTheme.get_default(); } // try to look up the icon in the icon theme iconInfo = icon_theme.lookup_icon(name, size, Gtk.IconLookupFlags.GENERIC_FALLBACK); // no icon? that's bad! if (iconInfo === null) { global.logError("unable to lookup icon for "+name); } else { // we have an icon // the icon size may not match the requested size, especially with custom themes if (iconInfo.get_base_size() < size) { // stretched icons look very ugly, we avoid that and just show the smaller icon realSize = iconInfo.get_base_size(); } // create a gicon for the icon gicon = Gio.icon_new_for_string(iconInfo.get_filename()); } } if (gicon) return new St.Icon({ gicon: gicon, icon_size: realSize }); else return null; }, _createIconFromPixmap: function(iconSize, iconPixmapArray) { // the pixmap actually is an array of pixmaps with different sizes // we use the one that is smaller or equal the iconSize // maybe it's empty? that's bad. if (!iconPixmapArray || iconPixmapArray.length < 1) return null; let sortedIconPixmapArray = iconPixmapArray.sort(function(pixmapA, pixmapB) { // we sort biggest to smallest let areaA = pixmapA[0] * pixmapA[1]; let areaB = pixmapB[0] * pixmapB[1]; return areaB - areaA; }) let qualifiedIconPixmapArray = sortedIconPixmapArray.filter(function(pixmap) { // we disqualify any pixmap that is bigger than our requested size return pixmap[0] <= iconSize && pixmap[1] <= iconSize; }) // if no one got qualified, we use the smallest one available let iconPixmap = qualifiedIconPixmapArray.length > 0 ? qualifiedIconPixmapArray[0] : sortedIconPixmapArray.pop(); let [ width, height, bytes ] = iconPixmap; let rowstride = width * 4; // hopefully this is correct try { let image = new Clutter.Image(); image.set_bytes(bytes, Cogl.PixelFormat.ABGR_8888, width, height, rowstride); return new Clutter.Actor({ width: Math.min(width, iconSize), height: Math.min(height, iconSize), content: image }); } catch (e) { // the image data was probably bogus. We don't really know why, but it _does_ happen. // we could log it here, but that doesn't really help in tracking it down. return null; } }, // updates the base icon _updateIcon: function() { // remove old icon if (this._mainIcon.get_child()) { let child = this._mainIcon.get_child(); if (child.inUse) child.inUse = false; else if (child.destroy) child.destroy(); this._mainIcon.set_child(null); } // place to save the new icon let newIcon = null; // we might need to use the AttentionIcon*, which have precedence over the normal icons if (this._indicator.status == SNIStatus.NEEDS_ATTENTION) { let [ name, pixmap, theme ] = this._indicator.attentionIcon; if (name && name.length) newIcon = this._cacheOrCreateIconByName(this._iconSize, name, theme); if (!newIcon && pixmap) newIcon = this._createIconFromPixmap(this._iconSize, pixmap); } if (!newIcon) { let [ name, pixmap, theme ] = this._indicator.icon; if (name && name.length) newIcon = this._cacheOrCreateIconByName(this._iconSize, name, theme); if (!newIcon && pixmap) newIcon = this._createIconFromPixmap(this._iconSize, pixmap); } this._mainIcon.set_child(newIcon); }, _updateOverlayIcon: function() { // remove old icon if (this._overlayIcon.get_child()) { let child = this._overlayIcon.get_child(); if (child.inUse) child.inUse = false; else if (child.destroy) child.destroy(); this._overlayIcon.set_child(null); } // KDE hardcodes the overlay icon size to 10px (normal icon size 16px) // we approximate that ratio for other sizes, too. // our algorithms will always pick a smaller one instead of stretching it. let iconSize = Math.floor(this._iconSize / 1.6); let newIcon = null; // create new let [ name, pixmap, theme ] = this._indicator.overlayIcon; if (name && name.length) newIcon = this._cacheOrCreateIconByName(iconSize, name, theme); if (!newIcon && pixmap) newIcon = this._createIconFromPixmap(iconSize, pixmap); this._overlayIcon.set_child(newIcon); }, _handleScrollEvent: function(actor, event) { if (actor != this) return Clutter.EVENT_PROPAGATE; if (event.get_source() != this) return Clutter.EVENT_PROPAGATE; if (event.type() != Clutter.EventType.SCROLL) return Clutter.EVENT_PROPAGATE; // Since Clutter 1.10, clutter will always send a smooth scrolling event // with explicit deltas, no matter what input device is used // In fact, for every scroll there will be a smooth and non-smooth scroll // event, and we can choose which one we interpret. if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH) { let [ dx, dy ] = event.get_scroll_delta(); this._indicator.scroll(dx, dy); } return Clutter.EVENT_STOP; }, // called when the icon theme changes _invalidateIcon: function() { if (this._iconCache) this._iconCache.clear(); this._updateIcon(); this._updateOverlayIcon(); }, destroy: function() { this._signalManager.disconnectAllSignals(); this._disconnectSignals(this._indicator, this._signalsIndicator); if (this._menuSignal > 0) this.actor.disconnect(this._menuSignal); if (this._menu) { this._menu.close(false); this._menu.destroy(); } this._menu = null; this._iconCache.destroy(); this.actor.destroy(); } }; /** * #IconCache: * @short_description: The class caches icon objects in case they're reused shortly aftwerwards. * * The icon cache caches icon objects in case they're reused shortly aftwerwards. * This is necessary for some indicators like skype which rapidly switch between serveral icons. * Without caching, the garbage collection would never be able to handle the amount of new icon data. * If the lifetime of an icon is over, the cache will destroy the icon. (!) * The presence of an inUse property set to true on the icon will extend the lifetime. * * how to use: see IconCache.add, IconCache.get */ function IconCache() { this._init(); } IconCache.prototype = { _init: function() { this._cache = {}; this._lifetime = {}; //we don't want to attach lifetime to the object this._gc(); }, add: function(id, o) { // global.log("IconCache: adding "+id); if (!(o && id)) return null; if (id in this._cache && this._cache[id] !== o) this._remove(id); this._cache[id] = o; this._lifetime[id] = new Date().getTime() + LIFETIME_TIMESPAN; return o; }, _remove: function(id) { // global.log('IconCache: removing '+id); if ('destroy' in this._cache[id]) this._cache[id].destroy(); delete this._cache[id]; delete this._lifetime[id]; }, forceDestroy: function(id) { this._remove(id); }, // removes everything from the cache clear: function() { for (let id in this._cache) this._remove(id); }, // returns an object from the cache, or null if it can't be found. get: function(id) { if (id in this._cache) { // global.log('IconCache: retrieving '+id); this._lifetime[id] = new Date().getTime() + LIFETIME_TIMESPAN; //renew lifetime return this._cache[id]; } else return null; }, _gc: function() { let time = new Date().getTime(); for (let id in this._cache) { if (this._cache[id].inUse) { continue; } else if (this._lifetime[id] < time) { this._remove(id); } else { //global.log("IconCache: " + id + " survived this round."); } } if (!this._stopGc) Mainloop.timeout_add(GC_INTERVAL, Lang.bind(this, this._gc)); return false; //we just added our timeout again. }, destroy: function() { this._stopGc = true; this.clear(); } }; Cinnamon-2.8.6/js/ui/searchProviderManager.js0000664000175000017500000000756412625666430020105 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Extension = imports.ui.extension; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Lang = imports.lang; // Maps uuid -> importer object (extension directory tree) var extensions; // Maps uuid -> metadata object var extensionMeta; // Maps uuid -> extension state object (returned from init()) const searchProviderObj = {}; // Arrays of uuids var enabledSearchProviders; const ENABLED_SEARCH_PROVIDERS_KEY = 'enabled-search-providers'; // Callback for extension.js function prepareExtensionUnload(extension) { delete searchProviderObj[extension.uuid]; } // Callback for extension.js function finishExtensionLoad(extension) { searchProviderObj[extension.uuid] = extension.module; return true; } function onEnabledSearchProvidersChanged() { enabledSearchProviders = global.settings.get_strv(ENABLED_SEARCH_PROVIDERS_KEY); for(let uuid in Extension.Type.SEARCH_PROVIDER.maps.objects) { if(enabledSearchProviders.indexOf(uuid) == -1) Extension.unloadExtension(uuid, Extension.Type.SEARCH_PROVIDER); } for(let i=0; i 0) { this._grabbed = false; this._eventCaptureId = 0; this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged)); this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM, { x_fill: true, y_fill: true, x_align: St.Align.START }); // Adds style to existing keyboard style to avoid repetition this._boxPointer.actor.add_style_class_name('keyboard-subkeys'); this._getExtendedKeys(); this.actor._extended_keys = this._extended_keyboard; this._boxPointer.actor.hide(); Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true }); } }, _makeKey: function () { let label = GLib.markup_escape_text(this._key.label, -1); let button = new St.Button ({ label: label, style_class: 'keyboard-key' }); button.key_width = this._key.width; button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); })); button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); })); return button; }, _getUnichar: function(key) { let keyval = key.keyval; let unichar = Gdk.keyval_to_unicode(keyval); if (unichar) { return String.fromCharCode(unichar); } else { return key.name; } }, _getExtendedKeys: function () { this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout', vertical: false }); for (let i = 0; i < this._extended_keys.length; ++i) { let extended_key = this._extended_keys[i]; let label = this._getUnichar(extended_key); let key = new St.Button({ label: label, style_class: 'keyboard-key' }); key.extended_key = extended_key; key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); })); key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); })); this._extended_keyboard.add(key); } this._boxPointer.bin.add_actor(this._extended_keyboard); }, _onEventCapture: function (actor, event) { let source = event.get_source(); let type = event.type(); if ((type == Clutter.EventType.BUTTON_PRESS || type == Clutter.EventType.BUTTON_RELEASE) && this._extended_keyboard.contains(source)) { source.extended_key.press(); source.extended_key.release(); return false; } if (type == Clutter.EventType.BUTTON_PRESS) { this._boxPointer.actor.hide(); this._ungrab(); return true; } return false; }, _ungrab: function () { global.stage.disconnect(this._eventCaptureId); this._eventCaptureId = 0; this._grabbed = false; Main.popModal(this.actor); }, _onShowSubkeysChanged: function () { if (this._key.show_subkeys) { this.actor.fake_release(); this._boxPointer.actor.raise_top(); this._boxPointer.setPosition(this.actor, 0.5); this._boxPointer.show(true); this.actor.set_hover(false); if (!this._grabbed) { Main.pushModal(this.actor); this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture)); this._grabbed = true; } this._key.release(); } else { if (this._grabbed) this._ungrab(); this._boxPointer.hide(true); } } }; function Keyboard() { this._init.apply(this, arguments); } Keyboard.prototype = { _init: function () { this._impl = Gio.DBusExportedObject.wrapJSObject(CaribouKeyboardIface, this); this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard'); this.actor = null; this._focusInExtendedKeys = false; this._timestamp = global.display.get_current_time_roundtrip(); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw)); this._keyboardSettings = new Gio.Settings({ schema_id: KEYBOARD_SCHEMA }); this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA }); this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._settingsChanged(); }, init: function () { this._redraw(); }, // _compareTimestamp: // // Compare two timestamps taking into account // CURRENT_TIME (0) _compareTimestamp: function(one, two) { if (one == two) return 0; if (one == Clutter.CURRENT_TIME) return 1; if (two == Clutter.CURRENT_TIME) return -1; return one - two; }, _settingsChanged: function (settings, key) { this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD); if (!this._enableKeyboard && !this._keyboard) return; if (this._enableKeyboard && this._keyboard && this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE)) return; if (this._keyboard) this._destroyKeyboard(); if (this._enableKeyboard) { // If we've been called because the setting actually just // changed to true (as opposed to being called from // this._init()), then we want to pop up the keyboard. let showKeyboard = (settings != null); // However, caribou-gtk-module or this._onKeyFocusChanged // will probably immediately tell us to hide it, so we // have to fake things out so we'll ignore that request. if (showKeyboard) this._timestamp = global.display.get_current_time_roundtrip() + 1; this._setupKeyboard(showKeyboard); } else Main.layoutManager.hideKeyboard(true); }, _destroyKeyboard: function() { if (this._keyboardNotifyId) this._keyboard.disconnect(this._keyboardNotifyId); if (this._focusNotifyId) global.stage.disconnect(this._focusNotifyId); this._keyboard = null; this.actor.destroy(); this.actor = null; }, _setupKeyboard: function(show) { this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true }); Main.layoutManager.keyboardBox.add_actor(this.actor); Main.layoutManager.trackChrome(this.actor); this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) }); this._groups = {}; this._current_page = null; // Initialize keyboard key measurements this._numOfHorizKeys = 0; this._numOfVertKeys = 0; this._addKeys(); // Keys should be layout according to the group, not the // locale; as Caribou already provides the expected layout, // this means enforcing LTR for all locales. this.actor.text_direction = Clutter.TextDirection.LTR; //this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged)); //this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged)); if (show) this.show(); }, _onKeyFocusChanged: function () { let focus = global.stage.key_focus; // Showing an extended key popup and clicking a key from the extended keys // will grab focus, but ignore that let extendedKeysWereFocused = this._focusInExtendedKeys; this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key); if (this._focusInExtendedKeys || extendedKeysWereFocused) return; let time = global.get_current_time(); if (focus instanceof Clutter.Text) this.Show(time); else this.Hide(time); }, _addKeys: function () { let groups = this._keyboard.get_groups(); for (let i = 0; i < groups.length; ++i) { let gname = groups[i]; let group = this._keyboard.get_group(gname); group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged)); let layers = {}; let levels = group.get_levels(); for (let j = 0; j < levels.length; ++j) { let lname = levels[j]; let level = group.get_level(lname); let layout = new St.BoxLayout({ style_class: 'keyboard-layout', vertical: true }); this._loadRows(level, layout); layers[lname] = layout; this.actor.add(layout, { x_fill: false }); layout.hide(); } this._groups[gname] = layers; } this._setActiveLayer(); }, _addRows : function (keys, layout) { let keyboard_row = new St.BoxLayout(); for (let i = 0; i < keys.length; ++i) { let children = keys[i].get_children(); let right_box = new St.BoxLayout({ style_class: 'keyboard-row' }); let left_box = new St.BoxLayout({ style_class: 'keyboard-row' }); for (let j = 0; j < children.length; ++j) { if (this._numOfHorizKeys == 0) this._numOfHorizKeys = children.length; let key = children[j]; let button = new Key(key); if (key.align == 'right') right_box.add(button.actor); else left_box.add(button.actor); if (key.name == 'Caribou_Prefs') { key.connect('key-released', Lang.bind(this, this.hide)); } } keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START }); keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END }); } layout.add(keyboard_row); }, _loadRows : function (level, layout) { let rows = level.get_rows(); for (let i = 0; i < rows.length; ++i) { let row = rows[i]; if (this._numOfVertKeys == 0) this._numOfVertKeys = rows.length; this._addRows(row.get_columns(), layout); } }, _redraw: function () { if (!this._enableKeyboard) return; let monitor = Main.layoutManager.bottomMonitor; let maxHeight = monitor.height / 3; this.actor.width = monitor.width; let layout = this._current_page; let verticalSpacing = layout.get_theme_node().get_length('spacing'); let padding = layout.get_theme_node().get_length('padding'); let box = layout.get_children()[0].get_children()[0]; let horizontalSpacing = box.get_theme_node().get_length('spacing'); let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing; let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys); let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing; let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys); let keySize = Math.min(keyWidth, keyHeight); this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding; let rows = this._current_page.get_children(); for (let i = 0; i < rows.length; ++i) { let keyboard_row = rows[i]; let boxes = keyboard_row.get_children(); for (let j = 0; j < boxes.length; ++j) { let keys = boxes[j].get_children(); for (let k = 0; k < keys.length; ++k) { let child = keys[k]; child.width = keySize * child.key_width; child.height = keySize; if (child._extended_keys) { let extended_keys = child._extended_keys.get_children(); for (let n = 0; n < extended_keys.length; ++n) { let extended_key = extended_keys[n]; extended_key.width = keySize; extended_key.height = keySize; } } } } } }, _onLevelChanged: function () { this._setActiveLayer(); this._redraw(); }, _onGroupChanged: function () { this._setActiveLayer(); this._redraw(); }, _setActiveLayer: function () { let active_group_name = this._keyboard.active_group; let active_group = this._keyboard.get_group(active_group_name); let active_level = active_group.active_level; let layers = this._groups[active_group_name]; if (this._current_page != null) { this._current_page.hide(); } this._current_page = layers[active_level]; this._current_page.show(); }, show: function () { this._redraw(); Main.layoutManager.showKeyboard(); }, hide: function () { Main.layoutManager.hideKeyboard(); }, _moveTemporarily: function () { let currentWindow = global.screen.get_display().focus_window; let rect = currentWindow.get_outer_rect(); let newX = rect.x; let newY = 3 * this.actor.height / 2; currentWindow.move_frame(true, newX, newY); }, _setLocation: function (x, y) { if (y >= 2 * this.actor.height) this._moveTemporarily(); }, // D-Bus methods Show: function(timestamp) { if (!this._enableKeyboard) return; if (this._compareTimestamp(timestamp, this._timestamp) < 0) return; if (timestamp != Clutter.CURRENT_TIME) this._timestamp = timestamp; this.show(); }, Hide: function(timestamp) { if (!this._enableKeyboard) return; if (this._compareTimestamp(timestamp, this._timestamp) < 0) return; if (timestamp != Clutter.CURRENT_TIME) this._timestamp = timestamp; this.hide(); }, SetCursorLocation: function(x, y, w, h) { if (!this._enableKeyboard) return; // this._setLocation(x, y); }, SetEntryLocation: function(x, y, w, h) { if (!this._enableKeyboard) return; // this._setLocation(x, y); }, get Name() { return 'cinnamon'; } }; Cinnamon-2.8.6/js/ui/windowManager.js0000664000175000017500000011620412625666430016424 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Meta = imports.gi.Meta; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Mainloop = imports.mainloop; const Gio = imports.gi.Gio; const AppSwitcher = imports.ui.appSwitcher.appSwitcher; const CoverflowSwitcher = imports.ui.appSwitcher.coverflowSwitcher; const TimelineSwitcher = imports.ui.appSwitcher.timelineSwitcher; const ClassicSwitcher = imports.ui.appSwitcher.classicSwitcher; const WindowEffects = imports.ui.windowEffects; const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const Tweener = imports.ui.tweener; const WINDOW_ANIMATION_TIME = 0.25; const TILE_HUD_ANIMATION_TIME = 0.15; const DIM_TIME = 0.500; const DIM_DESATURATION = 0.6; const DIM_BRIGHTNESS = -0.2; const UNDIM_TIME = 0.250; /* edge zones for tiling/snapping identification copied from muffin/src/core/window-private.h ___________________________ | 4 0 5 | | | | | | | | | | 2 3 | | | | | | | | | | 7 1 6 | |_________________________| */ const ZONE_TOP = 0; const ZONE_BOTTOM = 1; const ZONE_LEFT = 2; const ZONE_RIGHT = 3; const ZONE_TL = 4; const ZONE_TR = 5; const ZONE_BR = 6; const ZONE_BL = 7; function getTopInvisibleBorder(metaWindow) { let outerRect = metaWindow.get_outer_rect(); let inputRect = metaWindow.get_input_rect(); return outerRect.y - inputRect.y; } function WindowDimmer(actor) { this._init(actor); } WindowDimmer.prototype = { _init: function(actor) { this._desaturateEffect = new Clutter.DesaturateEffect(); this._brightnessEffect = new Clutter.BrightnessContrastEffect(); actor.add_effect(this._desaturateEffect); actor.add_effect(this._brightnessEffect); this.actor = actor; this._dimFactor = 0.0; }, setEnabled: function(enabled) { this._desaturateEffect.enabled = enabled; this._brightnessEffect.enabled = enabled; }, set dimFactor(factor) { this._dimFactor = factor; this._desaturateEffect.set_factor(factor * DIM_DESATURATION); this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS); }, get dimFactor() { return this._dimFactor; } }; function getWindowDimmer(actor) { if (!actor._windowDimmer) actor._windowDimmer = new WindowDimmer(actor); return actor._windowDimmer; } function TilePreview() { this._init(); } TilePreview.prototype = { _init: function() { this.actor = new St.Bin({ style_class: 'tile-preview', important: true }); global.window_group.add_actor(this.actor); this._snapQueued = 0; this._reset(); this._showing = false; }, show: function(window, tileRect, monitorIndex, snapQueued) { let windowActor = window.get_compositor_private(); if (!windowActor) return; if (this._snapQueued != snapQueued) { this._updateStyle(); this._snapQueued = snapQueued; } if (this._rect && this._rect.equal(tileRect)) return; let changeMonitor = (this._monitorIndex == -1 || this._monitorIndex != monitorIndex); this._monitorIndex = monitorIndex; this._rect = tileRect; let monitor = Main.layoutManager.monitors[monitorIndex]; if (!this._showing || changeMonitor) { let monitorRect = new Meta.Rectangle({ x: monitor.x, y: monitor.y, width: monitor.width, height: monitor.height }); let [, rect] = window.get_outer_rect().intersect(monitorRect); this.actor.set_size(rect.width, rect.height); this.actor.set_position(rect.x, rect.y); this.actor.opacity = 0; } this._showing = true; this.actor.show(); windowActor.raise_top(); Tweener.addTween(this.actor, { x: tileRect.x, y: tileRect.y, width: tileRect.width, height: tileRect.height, opacity: 255, time: WINDOW_ANIMATION_TIME, transition: 'easeOutQuad' }); }, hide: function() { if (!this._showing) return; this._showing = false; Tweener.addTween(this.actor, { opacity: 0, time: WINDOW_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, this._reset) }); }, _reset: function() { this.actor.hide(); this._rect = null; this._monitorIndex = -1; }, _updateStyle: function() { if (this.actor.has_style_class_name('snap')) this.actor.remove_style_class_name('snap'); else this.actor.add_style_class_name('snap'); }, destroy: function() { this.actor.destroy(); } }; function HudPreview() { this._init(); } HudPreview.prototype = { _init: function() { this.actor = new St.Bin({ style_class: 'tile-hud', important: true }); global.window_group.add_actor(this.actor); this._tileHudSettings = new Gio.Settings({ schema_id: "org.cinnamon.muffin" }); this._tileHudSettings.connect("changed::tile-hud-threshold", Lang.bind(this, this._onTileHudSettingsChanged)); this._onTileHudSettingsChanged(); this._snapQueued = 0; this._reset(); this._showing = false; }, show: function(currentProximityZone, workArea, snapQueued) { let changeZone = (this._zone != currentProximityZone); if (this._snapQueued != snapQueued) { this._updateSnapStyle(); this._snapQueued = snapQueued; } let pseudoClass = null; if (!this._showing || changeZone) { this._zone = currentProximityZone; let monitorRect = workArea; let tileGap = this._hudSize + 10; switch(this._zone) { case ZONE_TOP: this._x = monitorRect.x + tileGap; this._y = monitorRect.y; this._w = monitorRect.width - (tileGap * 2); this._h = 0; this._animatedX = this._x; this._animatedY = this._y; this._animatedW = this._w; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'top'; break; case ZONE_BOTTOM: this._x = monitorRect.x + tileGap; this._y = monitorRect.y + monitorRect.height; this._w = monitorRect.width - (tileGap * 2); this._h = 0; this._animatedX = this._x; this._animatedY = this._y - this._hudSize; this._animatedW = this._w; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'bottom'; break; case ZONE_LEFT: this._x = monitorRect.x; this._y = monitorRect.y + tileGap; this._w = 0; this._h = monitorRect.height - (tileGap * 2); this._animatedX = this._x; this._animatedY = this._y; this._animatedW = this._w + this._hudSize; this._animatedH = this._h; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'left'; break; case ZONE_RIGHT: this._x = monitorRect.x + monitorRect.width; this._y = monitorRect.y + tileGap; this._w = 0; this._h = monitorRect.height - (tileGap * 2); this._animatedX = this._x - this._hudSize; this._animatedY = this._y; this._animatedW = this._w + this._hudSize; this._animatedH = this._h; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'right'; break; case ZONE_TL: this._x = monitorRect.x; this._y = monitorRect.y; this._w = 0; this._h = 0; this._animatedX = this._x; this._animatedY = this._y; this._animatedW = this._w + this._hudSize; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'top-left'; break; case ZONE_TR: this._x = monitorRect.x + monitorRect.width; this._y = monitorRect.y; this._w = 0; this._h = 0; this._animatedX = this._x - this._hudSize; this._animatedY = this._y; this._animatedW = this._w + this._hudSize; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'top-right'; break; case ZONE_BL: this._x = monitorRect.x; this._y = monitorRect.y + monitorRect.height; this._w = 0; this._h = 0; this._animatedX = this._x; this._animatedY = this._y - this._hudSize; this._animatedW = this._w + this._hudSize; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'bottom-left'; break; case ZONE_BR: this._x = monitorRect.x + monitorRect.width; this._y = monitorRect.y + monitorRect.height; this._w = 0; this._h = 0; this._animatedX = this._x - this._hudSize; this._animatedY = this._y - this._hudSize; this._animatedW = this._w + this._hudSize; this._animatedH = this._h + this._hudSize; this.actor.set_size(this._w, this._h); this.actor.set_position(this._x, this._y); pseudoClass = 'bottom-right'; break; default: this.hide(); return; } this._updateStyle(pseudoClass); this._showing = true; this.actor.show(); this.actor.raise_top(); this.actor.opacity = 0; Tweener.addTween(this.actor, { x: this._animatedX, y: this._animatedY, width: this._animatedW, height: this._animatedH, opacity: 255, time: TILE_HUD_ANIMATION_TIME, transition: 'easeOutQuad' }); } }, hide: function() { if (!this._showing) return; this._showing = false; Tweener.addTween(this.actor, { x: this._x, y: this._y, width: this._w, height: this._h, opacity: 0, time: TILE_HUD_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, this._reset) }); }, _reset: function () { this.actor.hide(); this._zone = -1; }, _onTileHudSettingsChanged: function() { let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this._hudSize = this._tileHudSettings.get_int("tile-hud-threshold") * scaleFactor; }, _updateStyle: function(pseudoClass) { let currentStyle = this.actor.get_style_pseudo_class(); if (currentStyle) this.actor.remove_style_pseudo_class(currentStyle); if (pseudoClass) { this.actor.set_style_pseudo_class(pseudoClass); } }, _updateSnapStyle: function() { if (this.actor.has_style_class_name('snap')) this.actor.remove_style_class_name('snap'); else this.actor.add_style_class_name('snap'); }, destroy: function() { this.actor.destroy(); } } function WindowManager() { this._init(); } WindowManager.prototype = { _init : function() { this._cinnamonwm = global.window_manager; this._minimizing = []; this._maximizing = []; this._unmaximizing = []; this._tiling = []; this._mapping = []; this._destroying = []; this.effects = { map: new WindowEffects.Map(this), close: new WindowEffects.Close(this), minimize: new WindowEffects.Minimize(this), unminimize: new WindowEffects.Unminimize(this), tile: new WindowEffects.Tile(this), maximize: new WindowEffects.Maximize(this), unmaximize: new WindowEffects.Unmaximize(this) }; this._snapOsd = null; this._workspace_osd_array = []; this._tilePreview = null; this._hudPreview = null; this._dimmedWindows = []; this._animationBlockCount = 0; this._switchData = null; this._cinnamonwm.connect('kill-window-effects', Lang.bind(this, this._killWindowEffects)); this._cinnamonwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace)); this._cinnamonwm.connect('minimize', Lang.bind(this, this._minimizeWindow)); this._cinnamonwm.connect('maximize', Lang.bind(this, this._maximizeWindow)); this._cinnamonwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow)); this._cinnamonwm.connect('tile', Lang.bind(this, this._tileWindow)); this._cinnamonwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview)); this._cinnamonwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview)); this._cinnamonwm.connect('show-hud-preview', Lang.bind(this, this._showHudPreview)); this._cinnamonwm.connect('hide-hud-preview', Lang.bind(this, this._hideHudPreview)); this._cinnamonwm.connect('map', Lang.bind(this, this._mapWindow)); this._cinnamonwm.connect('destroy', Lang.bind(this, this._destroyWindow)); Meta.keybindings_set_custom_handler('move-to-workspace-left', Lang.bind(this, this._moveWindowToWorkspaceLeft)); Meta.keybindings_set_custom_handler('move-to-workspace-right', Lang.bind(this, this._moveWindowToWorkspaceRight)); Meta.keybindings_set_custom_handler('switch-to-workspace-left', Lang.bind(this, this._showWorkspaceSwitcher)); Meta.keybindings_set_custom_handler('switch-to-workspace-right', Lang.bind(this, this._showWorkspaceSwitcher)); Meta.keybindings_set_custom_handler('switch-to-workspace-up', Lang.bind(this, this._showWorkspaceSwitcher)); Meta.keybindings_set_custom_handler('switch-to-workspace-down', Lang.bind(this, this._showWorkspaceSwitcher)); Meta.keybindings_set_custom_handler('switch-windows', Lang.bind(this, this._startAppSwitcher)); Meta.keybindings_set_custom_handler('switch-group', Lang.bind(this, this._startAppSwitcher)); Meta.keybindings_set_custom_handler('switch-windows-backward', Lang.bind(this, this._startAppSwitcher)); Meta.keybindings_set_custom_handler('switch-group-backward', Lang.bind(this, this._startAppSwitcher)); Meta.keybindings_set_custom_handler('switch-panels', Lang.bind(this, this._startAppSwitcher)); Meta.keybindings_set_custom_handler('switch-panels-backward', Lang.bind(this, this._startAppSwitcher)); Main.overview.connect('showing', Lang.bind(this, function() { for (let i = 0; i < this._dimmedWindows.length; i++) this._undimWindow(this._dimmedWindows[i], true); })); Main.overview.connect('hiding', Lang.bind(this, function() { for (let i = 0; i < this._dimmedWindows.length; i++) this._dimWindow(this._dimmedWindows[i], true); })); global.screen.connect ("show-snap-osd", Lang.bind (this, this._showSnapOSD)); global.screen.connect ("hide-snap-osd", Lang.bind (this, this._hideSnapOSD)); global.screen.connect ("show-workspace-osd", Lang.bind (this, this.showWorkspaceOSD)); this.settings = new Gio.Settings({schema_id: "org.cinnamon.muffin"}); }, blockAnimations: function() { this._animationBlockCount++; }, unblockAnimations: function() { this._animationBlockCount = Math.max(0, this._animationBlockCount - 1); }, _shouldAnimate : function(actor) { if (Main.modalCount) { // system is in modal state return false; } if (Main.software_rendering) return false; if (!actor) return global.settings.get_boolean("desktop-effects"); let type = actor.meta_window.get_window_type(); if (type == Meta.WindowType.NORMAL) { return global.settings.get_boolean("desktop-effects"); } if (type == Meta.WindowType.DIALOG || type == Meta.WindowType.MODAL_DIALOG) { return global.settings.get_boolean("desktop-effects-on-dialogs"); } return false; }, _startWindowEffect: function(cinnamonwm, name, actor, args, overwriteKey){ let effect = this.effects[name]; if(!this._shouldAnimate(actor)){ cinnamonwm[effect.wmCompleteName](actor); return; } let key = "desktop-effects-" + (overwriteKey || effect.name); let type = global.settings.get_string(key + "-effect"); //make sure to end a running effect if(actor.current_effect_name){ this._endWindowEffect(cinnamonwm, actor.current_effect_name, actor); } this[effect.arrayName].push(actor); actor.current_effect_name = name; actor.orig_opacity = actor.opacity; actor.show(); if(effect[type]){ let time = global.settings.get_int(key + "-time") / 1000; let transition = global.settings.get_string(key + "-transition"); effect[type](cinnamonwm, actor, time, transition, args); } else if(!overwriteKey) //when not unminimizing, but the effect was not found, end it this._endWindowEffect(cinnamonwm, name, actor); }, _endWindowEffect: function(cinnamonwm, name, actor){ let effect = this.effects[name]; //effect will be an instance of WindowEffects.Effect let idx = this[effect.arrayName].indexOf(actor); if(idx !== -1){ this[effect.arrayName].splice(idx, 1); Tweener.removeTweens(actor); delete actor.current_effect_name; effect._end(actor); cinnamonwm[effect.wmCompleteName](actor); Main.panelManager.updatePanelsVisibility(); } }, _killWindowEffects: function (cinnamonwm, actor) { for(let i in this._effects){ this._endWindowEffect(cinnamonwm, i, actor); } }, _minimizeWindow : function(cinnamonwm, actor) { Main.soundManager.play('minimize'); // reset all cached values in case "traditional" is no longer in effect actor.get_meta_window()._cinnamonwm_has_origin = false; this._startWindowEffect(cinnamonwm, "minimize", actor); }, _tileWindow : function (cinnamonwm, actor, targetX, targetY, targetWidth, targetHeight) { Main.soundManager.play('tile'); this._startWindowEffect(cinnamonwm, "tile", actor, [targetX, targetY, targetWidth, targetHeight]); }, _maximizeWindow : function(cinnamonwm, actor, targetX, targetY, targetWidth, targetHeight) { Main.soundManager.play('maximize'); this._startWindowEffect(cinnamonwm, "maximize", actor, [targetX, targetY, targetWidth, targetHeight]); }, _unmaximizeWindow : function(cinnamonwm, actor, targetX, targetY, targetWidth, targetHeight) { Main.soundManager.play('unmaximize'); this._startWindowEffect(cinnamonwm, "unmaximize", actor, [targetX, targetY, targetWidth, targetHeight]); }, _hasAttachedDialogs: function(window, ignoreWindow) { var count = 0; window.foreach_transient(function(win) { if (win != ignoreWindow && win.is_attached_dialog()) count++; return false; }); return count != 0; }, _checkDimming: function(window, ignoreWindow) { let shouldDim = this._hasAttachedDialogs(window, ignoreWindow); if (shouldDim && !window._dimmed) { window._dimmed = true; this._dimmedWindows.push(window); if (!Main.overview.visible) this._dimWindow(window, true); } else if (!shouldDim && window._dimmed) { window._dimmed = false; this._dimmedWindows = this._dimmedWindows.filter(function(win) { return win != window; }); if (!Main.overview.visible) this._undimWindow(window, true); } }, _dimWindow: function(window, animate) { let actor = window.get_compositor_private(); if (!actor) return; let dimmer = getWindowDimmer(actor); let enabled = Meta.prefs_get_attach_modal_dialogs(); dimmer.setEnabled(enabled); if (!enabled) return; if (animate) { Tweener.addTween(dimmer, { dimFactor: 1.0, time: DIM_TIME, transition: 'linear' }); } else { getWindowDimmer(actor).dimFactor = 1.0; } }, _undimWindow: function(window, animate) { let actor = window.get_compositor_private(); if (!actor) return; let dimmer = getWindowDimmer(actor); let enabled = Meta.prefs_get_attach_modal_dialogs(); dimmer.setEnabled(enabled); if (!enabled) return; if (animate) { Tweener.addTween(dimmer, { dimFactor: 0.0, time: UNDIM_TIME, transition: 'linear' }); } else { getWindowDimmer(actor).dimFactor = 0.0; } }, _mapWindow : function(cinnamonwm, actor) { actor._windowType = actor.meta_window.get_window_type(); actor._notifyWindowTypeSignalId = actor.meta_window.connect('notify::window-type', Lang.bind(this, function () { let type = actor.meta_window.get_window_type(); actor._windowType = type; })); if (actor.meta_window.is_attached_dialog()) { this._checkDimming(actor.get_meta_window().get_transient_for()); } if (actor.get_meta_window()._cinnamonwm_has_origin === true) { Main.soundManager.play('minimize'); try { this._startWindowEffect(cinnamonwm, "unminimize", actor, null, "minimize") return; } catch(e) { //catch "no origin found" } } else if (actor.meta_window.get_window_type() == Meta.WindowType.NORMAL) { Main.soundManager.play('map'); } this._startWindowEffect(cinnamonwm, "map", actor); }, _destroyWindow : function(cinnamonwm, actor) { if (actor.meta_window.get_window_type() == Meta.WindowType.NORMAL) { Main.soundManager.play('close'); } actor.orig_opacity = actor.opacity; let window = actor.meta_window; if (window.is_attached_dialog()) { let parent = window.get_transient_for(); this._checkDimming(parent, window); } if (actor._notifyWindowTypeSignalId) { window.disconnect(actor._notifyWindowTypeSignalId); actor._notifyWindowTypeSignalId = 0; } if (window._dimmed) { this._dimmedWindows = this._dimmedWindows.filter(function(win) { return win != window; }); } if (window.minimized) { cinnamonwm.completed_destroy(actor); return; } this._startWindowEffect(cinnamonwm, "close", actor); }, _switchWorkspace : function(cinnamonwm, from, to, direction) { if (!this._shouldAnimate()) { cinnamonwm.completed_switch_workspace(); return; } let windows = global.get_window_actors(); /* @direction is the direction that the "camera" moves, so the * screen contents have to move one screen's worth in the * opposite direction. */ let xDest = 0, yDest = 0; if (direction == Meta.MotionDirection.UP || direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_RIGHT) yDest = global.screen_height; else if (direction == Meta.MotionDirection.DOWN || direction == Meta.MotionDirection.DOWN_LEFT || direction == Meta.MotionDirection.DOWN_RIGHT) yDest = -global.screen_height; if (direction == Meta.MotionDirection.LEFT || direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.DOWN_LEFT) xDest = global.screen_width; else if (direction == Meta.MotionDirection.RIGHT || direction == Meta.MotionDirection.UP_RIGHT || direction == Meta.MotionDirection.DOWN_RIGHT) xDest = -global.screen_width; for (let i = 0; i < windows.length; i++) { let window = windows[i]; if (!window.meta_window.showing_on_its_workspace()) continue; if ((window.meta_window == this._movingWindow) || ((global.display.get_grab_op() == Meta.GrabOp.MOVING || global.display.get_grab_op() == Meta.GrabOp.KEYBOARD_MOVING) && window.meta_window == global.display.get_focus_window())) { /* We are moving this window to the other workspace. In fact, * it is already on the other workspace, so it is hidden. We * force it to show and then don't animate it, so it stays * there while other windows move. */ window.show_all(); this._movingWindow = undefined; } else if (window.get_workspace() == from) { if (window.origX == undefined) { window.origX = window.x; window.origY = window.y; } Tweener.addTween(window, { x: window.origX + xDest, y: window.origY + yDest, time: WINDOW_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: function() { window.hide(); window.set_position(window.origX, window.origY); window.origX = undefined; window.origY = undefined; } }); } else if (window.get_workspace() == to) { if (window.origX == undefined) { window.origX = window.x; window.origY = window.y; window.set_position(window.origX - xDest, window.origY - yDest); } Tweener.addTween(window, { x: window.origX, y: window.origY, time: WINDOW_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(window, function() { window.origX = undefined; window.origY = undefined; }) }); window.show_all(); } } Tweener.addTween(this, {time: WINDOW_ANIMATION_TIME, onComplete: function() { cinnamonwm.completed_switch_workspace(); }}); }, _showTilePreview: function(cinnamonwm, window, tileRect, monitorIndex, snapQueued) { if (!this._tilePreview) this._tilePreview = new TilePreview(); this._tilePreview.show(window, tileRect, monitorIndex, snapQueued); }, _hideTilePreview: function(cinnamonwm) { if (!this._tilePreview) return; this._tilePreview.hide(); this._tilePreview.destroy(); this._tilePreview = null; }, _showHudPreview: function(cinnamonwm, currentProximityZone, workArea, snapQueued) { if (global.settings.get_boolean("show-tile-hud")) { if (!this._hudPreview) this._hudPreview = new HudPreview(); this._hudPreview.show(currentProximityZone, workArea, snapQueued); } }, _hideHudPreview: function(cinnamonwm) { if (!this._hudPreview) return; this._hudPreview.hide(); this._hudPreview.destroy(); this._hudPreview = null; }, showWorkspaceOSD : function() { this._hideSnapOSD(); this._hideWorkspaceOSD(); if (global.settings.get_boolean("workspace-osd-visible")) { let osd_x = global.settings.get_int("workspace-osd-x"); let osd_y = global.settings.get_int("workspace-osd-y"); let duration = global.settings.get_int("workspace-osd-duration") / 1000; let current_workspace_index = global.screen.get_active_workspace_index(); if (this.settings.get_boolean("workspaces-only-on-primary")) { this._showWorkspaceOSDOnMonitor(Main.layoutManager.primaryMonitor, osd_x, osd_y, duration, current_workspace_index); } else { for (let i = 0; i < Main.layoutManager.monitors.length; i++) { let monitor = Main.layoutManager.monitors[i]; this._showWorkspaceOSDOnMonitor(monitor, osd_x, osd_y, duration, current_workspace_index); } } } }, _showWorkspaceOSDOnMonitor : function(monitor, osd_x, osd_y, duration, current_workspace_index) { let osd = new St.Label({style_class:'workspace-osd', important: true}); this._workspace_osd_array.push(osd); osd.set_text(Main.getWorkspaceName(current_workspace_index)); osd.set_opacity = 0; Main.layoutManager.addChrome(osd, { visibleInFullscreen: false, affectsInputRegion: false }); /* * This aligns the osd edges to the minimum/maximum values from gsettings, * if those are selected to be used. For values in between minimum/maximum, * it shifts the osd by half of the percentage used of the overall space available * for display (100% - (left and right 'padding')). * The horizontal minimum/maximum values are 5% and 95%, resulting in 90% available for positioning * If the user choses 50% as osd position, these calculations result the osd being centered onscreen */ let [minX, maxX, minY, maxY] = [5, 95, 5, 95]; let delta = (osd_x - minX) / (maxX - minX); let x = monitor.x + Math.round((monitor.width * osd_x / 100) - (osd.width * delta)); delta = (osd_y - minY) / (maxY - minY); let y = monitor.y + Math.round((monitor.height * osd_y / 100) - (osd.height * delta)); osd.set_position(x, y); Tweener.addTween(osd, { opacity: 255, time: duration, transition: 'linear', onComplete: this._hideWorkspaceOSD, onCompleteScope: this }); }, _hideWorkspaceOSD : function() { for (let i = 0; i < this._workspace_osd_array.length; i++) { let osd = this._workspace_osd_array[i]; if (osd != null) { osd.hide(); Main.layoutManager.removeChrome(osd); osd.destroy(); } } this._workspace_osd_array = [] }, _showSnapOSD : function(metaScreen, monitorIndex) { if (global.settings.get_boolean("show-snap-osd")) { if (this._snapOsd == null) { this._snapOsd = new ModalDialog.InfoOSD(); let mod = this.settings.get_string("snap-modifier"); if (mod == "Super") this._snapOsd.addText(_("Hold to enter snap mode")); else if (mod == "Alt") this._snapOsd.addText(_("Hold to enter snap mode")); else if (mod == "Control") this._snapOsd.addText(_("Hold to enter snap mode")); else if (mod == "Shift") this._snapOsd.addText(_("Hold to enter snap mode")); this._snapOsd.addText(_("Use the arrow keys to shift workspaces")); } this._snapOsd.show(monitorIndex); } }, _hideSnapOSD : function() { if (this._snapOsd != null) { this._snapOsd.hide(); } }, _createAppSwitcher : function(binding) { if (AppSwitcher.getWindowsForBinding(binding).length == 0) return; let style = global.settings.get_string("alttab-switcher-style"); if(style == 'coverflow') new CoverflowSwitcher.CoverflowSwitcher(binding); else if(style == 'timeline') new TimelineSwitcher.TimelineSwitcher(binding); else new ClassicSwitcher.ClassicSwitcher(binding); }, _startAppSwitcher : function(display, screen, window, binding) { this._createAppSwitcher(binding); }, _shiftWindowToWorkspace : function(window, direction) { if (window.get_window_type() === Meta.WindowType.DESKTOP) { return; } this._movingWindow = window; let workspace = global.screen.get_active_workspace().get_neighbor(direction); if (workspace != global.screen.get_active_workspace()) { window.change_workspace(workspace); workspace.activate_with_focus(window, global.get_current_time()); this.showWorkspaceOSD(); } }, _moveWindowToWorkspaceLeft : function(display, screen, window, binding) { this._shiftWindowToWorkspace(window, Meta.MotionDirection.LEFT); }, _moveWindowToWorkspaceRight : function(display, screen, window, binding) { this._shiftWindowToWorkspace(window, Meta.MotionDirection.RIGHT); }, _showWorkspaceSwitcher : function(display, screen, window, binding) { if (binding.get_name() == 'switch-to-workspace-up') { Main.expo.toggle(); return; } if (binding.get_name() == 'switch-to-workspace-down') { Main.overview.toggle(); return; } if (screen.n_workspaces == 1) return; let current_workspace_index = global.screen.get_active_workspace_index(); if (binding.get_name() == 'switch-to-workspace-left') { this.actionMoveWorkspaceLeft(); if (current_workspace_index !== global.screen.get_active_workspace_index()) { this.showWorkspaceOSD(); } } else if (binding.get_name() == 'switch-to-workspace-right') { this.actionMoveWorkspaceRight(); if (current_workspace_index !== global.screen.get_active_workspace_index()) { this.showWorkspaceOSD(); } } }, actionMoveWorkspaceLeft: function() { var active = global.screen.get_active_workspace(); var neighbour = active.get_neighbor(Meta.MotionDirection.LEFT) if (active != neighbour) { Main.soundManager.play('switch'); neighbour.activate(global.get_current_time()); } }, actionMoveWorkspaceRight: function() { var active = global.screen.get_active_workspace(); var neighbour = active.get_neighbor(Meta.MotionDirection.RIGHT) if (active != neighbour) { Main.soundManager.play('switch'); neighbour.activate(global.get_current_time()); } }, actionMoveWorkspaceUp: function() { global.screen.get_active_workspace().get_neighbor(Meta.MotionDirection.UP).activate(global.get_current_time()); }, actionMoveWorkspaceDown: function() { global.screen.get_active_workspace().get_neighbor(Meta.MotionDirection.DOWN).activate(global.get_current_time()); }, actionFlipWorkspaceLeft: function() { var active = global.screen.get_active_workspace(); var neighbor = active.get_neighbor(Meta.MotionDirection.LEFT); if (active != neighbor) { neighbor.activate(global.get_current_time()); let [x, y, mods] = global.get_pointer(); global.set_pointer(global.screen_width - 10, y); } }, actionFlipWorkspaceRight: function() { var active = global.screen.get_active_workspace(); var neighbor = active.get_neighbor(Meta.MotionDirection.RIGHT); if (active != neighbor) { neighbor.activate(global.get_current_time()); let [x, y, mods] = global.get_pointer(); global.set_pointer(10, y); } } }; Cinnamon-2.8.6/js/ui/modalDialog.js0000664000175000017500000006210212625666430016033 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const St = imports.gi.St; const Atk = imports.gi.Atk; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Pango = imports.gi.Pango; const Params = imports.misc.params; const Util = imports.misc.util; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const Gettext = imports.gettext; const OPEN_AND_CLOSE_TIME = 0.1; const FADE_IN_BUTTONS_TIME = 0.33; const FADE_OUT_DIALOG_TIME = 1.0; const State = { OPENED: 0, CLOSED: 1, OPENING: 2, CLOSING: 3, FADED_OUT: 4 }; /** * #ModalDialog: * @short_description: A generic object that displays a modal dialog * @state (ModalDialog.State): The state of the modal dialog, which may be * `ModalDialog.State.OPENED`, `CLOSED`, `OPENING`, `CLOSING` or `FADED_OUT`. * @contentLayout (St.BoxLayout): The box containing the contents of the modal * dialog (excluding the buttons) * * The #ModalDialog object is a generic popup dialog in Cinnamon. It can either * be created directly and then manipulated afterwards, or used as a base class * for more sophisticated modal dialog. * * For simple usage such as displaying a message, or asking for confirmation, * the #ConfirmDialog and #NotifyDialog classes may be used instead. */ function ModalDialog() { this._init(); } ModalDialog.prototype = { /** * _init: * @params (JSON): parameters for the modal dialog. Options include * @cinnamonReactive, which determines whether the modal dialog should * block Cinnamon input, and @styleClass, which is the style class the * modal dialog should use. */ _init: function(params) { params = Params.parse(params, { cinnamonReactive: false, styleClass: null }); this.state = State.CLOSED; this._hasModal = false; this._cinnamonReactive = params.cinnamonReactive; this._group = new St.Group({ visible: false, x: 0, y: 0, accessible_role: Atk.Role.DIALOG }); Main.uiGroup.add_actor(this._group); let constraint = new Clutter.BindConstraint({ source: global.stage, coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE }); this._group.add_constraint(constraint); this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy)); this._actionKeys = {}; this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this._backgroundBin = new St.Bin(); this._group.add_actor(this._backgroundBin); this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', vertical: true }); if (params.styleClass != null) { this._dialogLayout.add_style_class_name(params.styleClass); } if (!this._cinnamonReactive) { this._lightbox = new Lightbox.Lightbox(this._group, { inhibitEvents: true }); this._lightbox.highlight(this._backgroundBin); let stack = new Cinnamon.Stack(); this._backgroundBin.child = stack; this._eventBlocker = new Clutter.Group({ reactive: true }); stack.add_actor(this._eventBlocker); stack.add_actor(this._dialogLayout); } else { this._backgroundBin.child = this._dialogLayout; } this.contentLayout = new St.BoxLayout({ vertical: true }); this._dialogLayout.add(this.contentLayout, { x_fill: true, y_fill: true, x_align: St.Align.MIDDLE, y_align: St.Align.START }); this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', vertical: false }); this._dialogLayout.add(this._buttonLayout, { expand: true, x_align: St.Align.MIDDLE, y_align: St.Align.END }); global.focus_manager.add_group(this._dialogLayout); this._initialKeyFocus = this._dialogLayout; this._savedKeyFocus = null; }, /** * destroy: * * Destroys the modal dialog */ destroy: function() { this._group.destroy(); }, /** * setButtons: * @buttons (array): the buttons to display in the modal dialog * * This sets the buttons in the modal dialog. The buttons is an array of * JSON objects, each of which corresponds to one button. * * Each JSON object *must* contain @label and @action, which are the text * displayed on the button and the callback function to use when the button * is clicked respectively. * * Optional arguments include @focused, which determines whether the button * is initially focused, and @key, which is a keybinding associated with * the button press such that pressing the keybinding will have the same * effect as clicking the button. * * An example usage is * ``` * dialog.setButtons([ * { * label: _("Cancel"), * action: Lang.bind(this, this.callback), * key: Clutter.Escape * }, * { * label: _("OK"), * action: Lang.bind(this, this.destroy), * key: Clutter.Return * } * ]); * ``` */ setButtons: function(buttons) { let hadChildren = this._buttonLayout.get_children() > 0; this._buttonLayout.destroy_children(); this._actionKeys = {}; let focusSetExplicitly = false; for (let i = 0; i < buttons.length; i ++) { let buttonInfo = buttons[i]; let label = buttonInfo['label']; let action = buttonInfo['action']; let key = buttonInfo['key']; let wantsfocus = buttonInfo['focused'] === true; let nofocus = buttonInfo['focused'] === false; buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button', reactive: true, can_focus: true, label: label }); let x_alignment; if (buttons.length == 1) x_alignment = St.Align.END; else if (i == 0) x_alignment = St.Align.START; else if (i == buttons.length - 1) x_alignment = St.Align.END; else x_alignment = St.Align.MIDDLE; if (wantsfocus) { this._initialKeyFocus = buttonInfo.button; focusSetExplicitly = true; } if (!focusSetExplicitly && !nofocus && (this._initialKeyFocus == this._dialogLayout || this._buttonLayout.contains(this._initialKeyFocus))) { this._initialKeyFocus = buttonInfo.button; } this._buttonLayout.add(buttonInfo.button, { expand: true, x_fill: false, y_fill: false, x_align: x_alignment, y_align: St.Align.MIDDLE }); buttonInfo.button.connect('clicked', action); if (key) this._actionKeys[key] = action; } // Fade in buttons if there weren't any before if (!hadChildren && buttons.length > 0) { this._buttonLayout.opacity = 0; Tweener.addTween(this._buttonLayout, { opacity: 255, time: FADE_IN_BUTTONS_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.emit('buttons-set'); }) }); } else { this.emit('buttons-set'); } }, _onKeyPressEvent: function(object, keyPressEvent) { let modifiers = Cinnamon.get_event_state(keyPressEvent); let ctrlAltMask = Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.MOD1_MASK; let symbol = keyPressEvent.get_key_symbol(); if (symbol === Clutter.Escape && !(modifiers & ctrlAltMask)) { this.close(); return; } let action = this._actionKeys[symbol]; if (action) action(); }, _onGroupDestroy: function() { this.emit('destroy'); }, _fadeOpen: function() { let monitor = Main.layoutManager.currentMonitor; this._backgroundBin.set_position(monitor.x, monitor.y); this._backgroundBin.set_size(monitor.width, monitor.height); this.state = State.OPENING; this._dialogLayout.opacity = 255; if (this._lightbox) this._lightbox.show(); this._group.opacity = 0; this._group.show(); Tweener.addTween(this._group, { opacity: 255, time: OPEN_AND_CLOSE_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.state = State.OPENED; this.emit('opened'); }) }); }, setInitialKeyFocus: function(actor) { this._initialKeyFocus = actor; }, /** * open: * @timestamp (int): (optional) timestamp optionally used to associate the * call with a specific user initiated event * * Opens and displays the modal dialog. */ open: function(timestamp) { if (this.state == State.OPENED || this.state == State.OPENING) return true; if (!this.pushModal(timestamp)) return false; this._fadeOpen(); return true; }, /** * close: * @timestamp (int): (optional) timestamp optionally used to associate the * call with a specific user initiated event * * Closes the modal dialog. */ close: function(timestamp) { if (this.state == State.CLOSED || this.state == State.CLOSING) return; this.state = State.CLOSING; this.popModal(timestamp); this._savedKeyFocus = null; Tweener.addTween(this._group, { opacity: 0, time: OPEN_AND_CLOSE_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.state = State.CLOSED; this._group.hide(); }) }); }, /** * popModal: * @timestamp (int): (optional) timestamp optionally used to associate the * call with a specific user initiated event * * Drop modal status without closing the dialog; this makes the * dialog insensitive as well, so it needs to be followed shortly * by either a %close() or a %pushModal() */ popModal: function(timestamp) { if (!this._hasModal) return; let focus = global.stage.key_focus; if (focus && this._group.contains(focus)) this._savedKeyFocus = focus; else this._savedKeyFocus = null; Main.popModal(this._group, timestamp); global.gdk_screen.get_display().sync(); this._hasModal = false; if (!this._cinnamonReactive) this._eventBlocker.raise_top(); }, /** * pushModal: * @timestamp (int): (optional) timestamp optionally used to associate the * call with a specific user initiated event * * Pushes the modal to the modal stack so that it grabs the required * inputs. */ pushModal: function (timestamp) { if (this._hasModal) return true; if (!Main.pushModal(this._group, timestamp)) return false; this._hasModal = true; if (this._savedKeyFocus) { this._savedKeyFocus.grab_key_focus(); this._savedKeyFocus = null; } else this._initialKeyFocus.grab_key_focus(); if (!this._cinnamonReactive) this._eventBlocker.lower_bottom(); return true; }, /** * _fadeOutDialog: * @timestamp (int): (optional) timestamp optionally used to associate the * call with a specific user initiated event * * This method is like %close(), but fades the dialog out much slower, * and leaves the lightbox in place. Once in the faded out state, * the dialog can be brought back by an open call, or the lightbox * can be dismissed by a close call. * * The main point of this method is to give some indication to the user * that the dialog reponse has been acknowledged but will take a few * moments before being processed. * * e.g., if a user clicked "Log Out" then the dialog should go away * imediately, but the lightbox should remain until the logout is * complete. */ _fadeOutDialog: function(timestamp) { if (this.state == State.CLOSED || this.state == State.CLOSING) return; if (this.state == State.FADED_OUT) return; this.popModal(timestamp); Tweener.addTween(this._dialogLayout, { opacity: 0, time: FADE_OUT_DIALOG_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.state = State.FADED_OUT; }) }); } }; Signals.addSignalMethods(ModalDialog.prototype); /** * #SpicesAboutDialog: * @short_description: A dialog for a spice "about" window * * This is a window that displays an about dialog for Cinnamon "spices". * * This is usually used by Cinnamon itself via an "About" right click menu, but * individual spices can also use this to open an about dialog if they wish. */ function SpicesAboutDialog(metadata, type) { this._init(metadata, type); } SpicesAboutDialog.prototype = { __proto__: ModalDialog.prototype, /** * _init: * metadata (JSON): the metadata object of the spice * type (string): the type of the spice, which should be "applet", * "desklet" or "extension" */ _init: function(metadata, type) { ModalDialog.prototype._init.call(this); //prepare translation this.uuid = metadata.uuid; Gettext.bindtextdomain(metadata.uuid, GLib.get_home_dir() + "/.local/share/locale"); let contentBox = new St.BoxLayout({vertical: true, style_class: "about-content" }); this.contentLayout.add_actor(contentBox); let topBox = new St.BoxLayout(); contentBox.add_actor(topBox); //icon let icon; if (metadata.icon) { icon = new St.Icon({icon_name: metadata.icon, icon_size: 48, icon_type: St.IconType.FULLCOLOR, style_class: "about-icon"}); } else { let file = Gio.file_new_for_path(metadata.path + "/icon.png"); if (file.query_exists(null)) { let gicon = new Gio.FileIcon({file: file}); icon = new St.Icon({gicon: gicon, icon_size: 48, icon_type: St.IconType.FULLCOLOR, style_class: "about-icon"}); } else { icon = new St.Icon({icon_name: "cs-"+type, icon_size: 48, icon_type: St.IconType.FULLCOLOR, style_class: "about-icon"}); } } topBox.add_actor(icon); let topTextBox = new St.BoxLayout({vertical: true}); topBox.add_actor(topTextBox); /*title*/ let titleBox = new St.BoxLayout(); topTextBox.add_actor(titleBox); let title = new St.Label({text: this._(metadata.name), style_class: "about-title"}); titleBox.add_actor(title); if (metadata.version) { let versionBin = new St.Bin({x_align: St.Align.START, y_align: St.Align.END}); titleBox.add_actor(versionBin); let version = new St.Label({text: " v%s".format(metadata.version), style_class: "about-version"}); versionBin.add_actor(version); } //uuid let uuid = new St.Label({text: metadata.uuid, style_class: "about-uuid"}); topTextBox.add_actor(uuid); //description let desc = new St.Label({text: this._(metadata.description), style_class: "about-description"}); let dText = desc.clutter_text; topTextBox.add_actor(desc); // optional content if(metadata.comments || metadata.website || metadata.contributors){ let scrollBox = new St.ScrollView({style_class: "about-scrollBox"}); contentBox.add(scrollBox, {expand: true}); let infoBox = new St.BoxLayout({vertical: true, style_class: "about-scrollBox-innerBox"}); scrollBox.add_actor(infoBox); // comments if (metadata.comments) { let comments = new St.Label({text: _("Comments:") + "\n\t" + this._(metadata.comments)}); let cText = comments.clutter_text; cText.ellipsize = Pango.EllipsizeMode.NONE; cText.line_wrap = true; cText.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR); infoBox.add_actor(comments); } // website if (metadata.website) { let wsBox = new St.BoxLayout({vertical: true}); infoBox.add_actor(wsBox); let wLabel = new St.Label({text: _("Website:")}); wsBox.add_actor(wLabel); let wsButton = new St.Button({x_align: St.Align.START, style_class: "cinnamon-link", name: "about-website"}); wsBox.add_actor(wsButton); let website = new St.Label({text: metadata.website}); let wtext = website.clutter_text; wtext.ellipsize = Pango.EllipsizeMode.NONE; wtext.line_wrap = true; wtext.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR); wsButton.add_actor(website); wsButton.connect("clicked", Lang.bind(this, this._launchSite, metadata.website)); } // contributors if (metadata.contributors) { let list = metadata.contributors; // enforce that the list is an array if(typeof list === "string") list = list.split(","); // trim whitespaces, try to translate each item and glue all together list = list.map(String.trim).map(this._, this).join("\n\t"); let contributors = new St.Label({text: _("Contributors:") + "\n\t" + list}); infoBox.add_actor(contributors); } } //dialog close button this.setButtons([ {label: _("Close"), key: "", focus: true, action: Lang.bind(this, this._onOk)} ]); this.open(global.get_current_time()); }, // translation _: function(str) { // look into the text domain first let translated = Gettext.dgettext(this.uuid, str); // if it looks translated, return the translation of the domain if(translated !== str) return translated; // else, use the default cinnamon domain return _(str); }, _onOk: function() { this.close(global.get_current_time()); }, _launchSite: function(a, b, site) { Util.spawnCommandLine("xdg-open " + site); this.close(global.get_current_time()); } } /** * #ConfirmDialog * @short_description: A simple dialog with a "Yes" and "No" button. * @callback (function): Callback when "Yes" is clicked * * A confirmation dialog that calls @callback and then destroys itself if user * clicks "Yes". If the user clicks "No", the dialog simply destroys itself. * * Inherits: ModalDialog.ModalDialog */ function ConfirmDialog(label, callback){ this._init(label, callback); } ConfirmDialog.prototype = { __proto__: ModalDialog.prototype, /** * _init: * @label (string): label to display on the confirm dialog * @callback (function): function to call when user clicks "yes" * * Constructor function. */ _init: function(label, callback){ ModalDialog.prototype._init.call(this); this.contentLayout.add(new St.Label({text: label})); this.callback = callback; this.setButtons([ { label: _("Yes"), action: Lang.bind(this, function(){ this.destroy(); this.callback(); }) }, { label: _("No"), action: Lang.bind(this, this.destroy) } ]); }, }; /** * #NotifyDialog * @short_description: A simple dialog that presents a message with an "OK" * button. * * A notification dialog that displays a message to user. Destroys itself after * user clicks "OK" * * Inherits: ModalDialog.ModalDialog */ function NotifyDialog(label){ this._init(label); } NotifyDialog.prototype = { __proto__: ModalDialog.prototype, /** * _init: * @label (string): label to display on the notify dialog * * Constructor function. */ _init: function(label){ ModalDialog.prototype._init.call(this); this.contentLayout.add(new St.Label({text: label})); this.setButtons([ { label: _("OK"), action: Lang.bind(this, this.destroy) } ]); }, }; /** * #InfoOSD * @short_description: An OSD that displays information to users * @actor (St.BoxLayout): actor of the OSD * * Creates an OSD to show information to user at the center of the screen. Can * display texts or a general #St.Widget. This is useful as "hints" to the * user, eg. the popup shown when the user clicks the "Add panel" button to * guide them how to add a panel. * * This does not destroy itself, and the caller of this is responsible for * destroying it after usage (via the %destroy function), or hiding it with * %hide for later reuse. */ function InfoOSD(text) { this._init(text); } InfoOSD.prototype = { /** * _init: * @text (string): (optional) Text to display on the OSD * * Constructor function. Creates an OSD and adds it to the chrome. Adds a * label with text @text if specified. */ _init: function(text) { this.actor = new St.BoxLayout({vertical: true, style_class: "info-osd", important: true}); if (text) { let label = new St.Label({text: text}); this.actor.add(label); } Main.layoutManager.addChrome(this.actor, {visibleInFullscreen: false, affectsInputRegion: false}); }, /** * show: * @monitorIndex (int): (optional) Monitor to display OSD on. Default is * primary monitor * * Shows the OSD at the center of monitor @monitorIndex. Shows at the * primary monitor if not specified. */ show: function(monitorIndex) { if (!monitorIndex) monitorIndex = 0; let monitor = Main.layoutManager.monitors[monitorIndex]; // The actor has to be shown first so that the width and height can be calculated properly this.actor.opacity = 0; this.actor.show(); let x = monitor.x + Math.round((monitor.width - this.actor.width)/2); let y = monitor.y + Math.round((monitor.height - this.actor.height)/2); this.actor.set_position(x, y); this.actor.opacity = 255; }, /** * hide: * * Hides the OSD. */ hide: function() { this.actor.hide(); }, /** * destroy: * * Destroys the OSD */ destroy: function() { Main.layoutManager.removeChrome(this.actor); this.actor.destroy(); }, /** * addText: * @text (string): text to display * @params (JSON): parameters to be used when adding text * * Adds a text label displaying @text to the OSD */ addText: function(text, params) { let label = new St.Label({text: text}); this.actor.add(label, params); }, /** * addActor: * @actor (St.Widget): actor to add * @params (JSON): parameters to be used when adding actor * * Adds the actor @actor to the OSD */ addActor: function(actor, params) { this.actor.add(actor, params); } } Cinnamon-2.8.6/js/ui/extension.js0000664000175000017500000004661412625666430015645 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cinnamon = imports.gi.Cinnamon; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Signals = imports.signals; const St = imports.gi.St; const AppletManager = imports.ui.appletManager; const Config = imports.misc.config; const DeskletManager = imports.ui.deskletManager; const ExtensionSystem = imports.ui.extensionSystem; const SearchProviderManager = imports.ui.searchProviderManager; const Main = imports.ui.main; const State = { INITIALIZING: 0, LOADED: 1, ERROR: 2, OUT_OF_DATE: 3 }; // macro for creating extension types function _createExtensionType(name, folder, manager, overrides){ let type = { name: name, folder: folder, requiredFunctions: ["main"], requiredProperties: ["uuid", "name", "description"], niceToHaveProperties: [], roles: {}, callbacks: { finishExtensionLoad: manager.finishExtensionLoad, prepareExtensionUnload: manager.prepareExtensionUnload }, maps: { importObjects: {}, objects: {}, meta: {}, dirs: {} } }; for(let prop in overrides) type[prop] = overrides[prop]; // Add signal methods Signals.addSignalMethods(type); let path = GLib.build_filenamev([global.userdatadir, folder]); type.userDir = path; // create user directories if they don't exist. let dir = Gio.file_new_for_path(type.userDir) try { if(!dir.query_exists(null)) dir.make_directory_with_parents(null); } catch(e){ global.logError(e); } return type; } /** * const Type: * @EXTENSION: Cinnamon extensions * @APPLET: Cinnamon panel applets * * @name: Upper case first character name for printing messages * Also converted to lowercase to find the correct javascript file * @folder: The folder name within the system and user cinnamon folders * @requiredFunctions: Functions that must exist in the main javascript file * @requiredProperties: Properties that must be set in the metadata.json file * @niceToHaveProperties: Properties that are encouraged to be set in the metadata.json file * @roles: Roles an extension can assume. Values will be set internally, set to null. * key => name of the role, value => reference to the extension object * @callbacks: Callbacks used to do some manual actions on load / unload * * Extension types with some attributes helping to load these extension types. * Properties are nested, with lowerCamelCase properties (e.g. requiredFunctions) as sub-properties of CAPITAL one (EXTENSION). Thus they are refered to as, e.g., Type.EXTENSION.requiredFunctions */ const Type = { EXTENSION: _createExtensionType("Extension", "extensions", ExtensionSystem, { requiredFunctions: ["init", "disable", "enable"], requiredProperties: ["uuid", "name", "description", "cinnamon-version"], niceToHaveProperties: ["url"], }), APPLET: _createExtensionType("Applet", "applets", AppletManager, { roles: { notifications: null, windowlist: null, panellauncher: null } }), DESKLET: _createExtensionType("Desklet", "desklets", DeskletManager, { roles: { notifications: null, windowlist: null } }), SEARCH_PROVIDER: _createExtensionType("Search provider", "search_providers", SearchProviderManager, { requiredFunctions: ["perform_search", "on_result_selected"] }) }; // Create a dummy metadata object when metadata parsing failed or was not done yet. function createMetaDummy(uuid, path, state, type) { return { name: uuid, description: 'Metadata load failed', state: state, path: path, error: '', type: type }; } // The Extension object itself function Extension(dir, type, force, uuid) { this._init(dir, type, force, uuid); } Extension.prototype = { _init: function(dir, type, force, uuid) { this.uuid = uuid; this.dir = dir; this.type = type; this.lowerType = type.name.toLowerCase().replace(" ", "_"); this.theme = null; this.stylesheet = null; this.iconDirectory = null; this.meta = createMetaDummy(this.uuid, dir.get_path(), State.INITIALIZING, type); this.startTime = new Date().getTime(); this.loadMetaData(dir.get_child('metadata.json')); if (!force) this.validateMetaData(); if (this.meta.multiversion) { this.dir = findExtensionSubdirectory(this.dir); this.meta.path = this.dir.get_path(); type.maps.dirs[this.uuid] = this.dir; } this.ensureFileExists(this.dir.get_child(this.lowerType + '.js')); this.loadStylesheet(this.dir.get_child('stylesheet.css')); if (this.stylesheet) { Main.themeManager.connect('theme-set', Lang.bind(this, function() { this.loadStylesheet(this.dir.get_child('stylesheet.css')); })); } this.loadIconDirectory(this.dir); imports.addSubImporter(this.lowerType, this.meta.path); type.maps.importObjects[this.uuid] = imports[this.lowerType]; try { this.module = type.maps.importObjects[this.uuid][this.lowerType]; // get [extension/applet/desklet].js } catch (e) { throw this.logError('Error importing ' + this.lowerType + '.js from ' + this.uuid, e); } for (let i = 0; i < this.type.requiredFunctions.length; i++) { let func = this.type.requiredFunctions[i]; if (!this.module[func]) { throw this.logError('Function "' + func + '" is missing'); } } type.maps.objects[this.uuid] = this; }, finalize : function() { this.meta.state = State.LOADED; this.type.emit('extension-loaded', this.uuid); let endTime = new Date().getTime(); global.log('Loaded %s %s in %d ms'.format(this.lowerType, this.uuid, (endTime - this.startTime))); }, formatError:function(message) { return '[%s "%s"]: %s'.format(this.type.name, this.uuid, message); }, logError: function (message, error, state) { this.meta.state = state || State.ERROR; this.meta.error += message; let errorMessage = this.formatError(message); if(error) global.logError(error); else error = new Error(errorMessage); global.logError(errorMessage); // An error during initialization leads to unloading the extension again. if(this.meta.state == State.INITIALIZING) { this.unlockRole(); this.unloadStylesheet(); this.unloadIconDirectory(); forgetExtension(this.uuid); } error._alreadyLogged = true; return error; }, logWarning: function (message) { global.logWarning(this.formatError(message)); }, loadMetaData: function(metadataFile) { let oldState = this.meta.state; let oldPath = this.meta.path; try { this.ensureFileExists(metadataFile); let metadataContents = Cinnamon.get_file_contents_utf8_sync(metadataFile.get_path()); this.meta = JSON.parse(metadataContents); // Store some additional crap here this.meta.state = oldState; this.meta.path = oldPath; this.meta.type = this.type; this.meta.error = ''; this.type.maps.meta[this.uuid] = this.meta; } catch (e) { this.meta = createMetaDummy(this.uuid, oldPath, State.ERROR, this.type); this.type.maps.meta[this.uuid] = this.meta; throw this.logError('Failed to load/parse metadata.json', e); } }, validateMetaData: function() { // Some properties are required to run this.checkProperties(this.type.requiredProperties, true); // Others are nice to have this.checkProperties(this.type.niceToHaveProperties, false); if (this.meta.uuid != this.uuid) { throw this.logError('uuid "' + this.meta.uuid + '" from metadata.json does not match directory name.'); } // If cinnamon or js version are set, check them if('cinnamon-version' in this.meta && !versionCheck(this.meta['cinnamon-version'], Config.PACKAGE_VERSION)) { throw this.logError('Extension is not compatible with current Cinnamon version', null, State.OUT_OF_DATE); } if('js-version' in this.meta && !versionCheck(this.meta['js-version'], Config.GJS_VERSION)) { throw this.logError('Extension is not compatible with current GJS version', null, State.OUT_OF_DATE); } // If a role is set, make sure it's a valid one let role = this.meta['role']; if(role) { if (!(role in this.type.roles)) { throw this.logError('Unknown role definition: ' + role + ' in metadata.json'); } } }, checkProperties: function(properties, fatal) { for (let i = 0; i < properties.length; i++) { let prop = properties[i]; if (!this.meta[prop]) { let msg = 'Missing property "' + prop + '" in metadata.json'; if(fatal) throw this.logError(msg); else this.logWarning(msg); } } }, loadStylesheet: function (file) { if (file.query_exists(null)) { try { let themeContext = St.ThemeContext.get_for_stage(global.stage); this.theme = themeContext.get_theme(); } catch (e) { throw this.logError('Error trying to get theme', e); } try { this.theme.load_stylesheet(file.get_path()); this.stylesheet = file.get_path(); } catch (e) { throw this.logError('Stylesheet parse error', e); } } }, unloadStylesheet: function () { if (this.theme != null && this.stylesheet != null) { try { this.theme.unload_stylesheet(this.stylesheet); } catch (e) { global.logError('Error unloading stylesheet', e); } } }, loadIconDirectory: function(dir) { let iconDir = dir.get_child("icons"); if (iconDir.query_exists(null)) { let path = iconDir.get_path(); this.iconDirectory = path; Gtk.IconTheme.get_default().append_search_path(path); } }, unloadIconDirectory: function() { if (this.iconDirectory) { let iconTheme = Gtk.IconTheme.get_default(); let searchPath = iconTheme.get_search_path(); for (let i = 0; i < searchPath.length; i++) { if (searchPath[i] == this.iconDirectory) { searchPath.splice(i,1); iconTheme.set_search_path(searchPath); break; } } } }, ensureFileExists: function(file) { if (!file.query_exists(null)) { throw this.logError('File not found: ' + file.get_path()); } }, lockRole: function(roleProvider) { let role = this.meta['role']; if(role && this.type.roles[role] != this) { if(this.type.roles[role] != null) { return false; } if(roleProvider != null) { this.type.roles[role] = this; this.roleProvider = roleProvider; global.log("Role locked: " + role); return true; } } return true; }, unlockRole: function() { let role = this.meta['role']; if(role && this.type.roles[role] == this) { this.type.roles[role] = null; this.roleProvider = null; global.log("Role unlocked: " + role); } } } /** * versionCheck: * @required: an array of versions we're compatible with * @current: the version we have * * Check if a component is compatible for an extension. * @required is an array, and at least one version must match. * @current must be in the format ... * is always ignored * is ignored if not specified (so you can target the whole release) * and must match * Each target version must be at least and */ function versionCheck(required, current) { let currentArray = current.split('.'); let major = currentArray[0]; let minor = currentArray[1]; let point = currentArray[2]; for (let i = 0; i < required.length; i++) { let requiredArray = required[i].split('.'); if (requiredArray[0] == major && requiredArray[1] == minor && (requiredArray[2] == point || requiredArray[2] === undefined)) return true; } return false; } /** * versionLeq: * @a (string): the first version * @b (string): the second version * * Returns: whether a <= b */ function versionLeq(a, b) { a = a.split('.'); b = b.split('.'); if (a.length == 2) a.push(0); if (b.length == 2) b.push(0); for (let i = 0; i < 3; i++) { if (a[i] == b[i]) continue; else if (a[i] > b[i]) return false; else return true; } return true; } // Returns a string version of a State value function getMetaStateString(state) { switch (state) { case State.INITIALIZING: return _("Initializing"); case State.LOADED: return _("Loaded"); case State.ERROR: return _("Error"); case State.OUT_OF_DATE: return _("Out of date"); } return 'Unknown'; // Not translated, shouldn't appear } /** * loadExtension: * * @uuid (string): uuid of xlet * @type (Extension.Type): type of xlet */ function loadExtension(uuid, type) { let force = uuid.indexOf("!") == 0; uuid = uuid.replace(/^!/,''); let extension = type.maps.objects[uuid]; if(!extension) { try { type.maps.dirs[uuid] = findExtensionDirectory(uuid, type); if (type.maps.dirs[uuid] == null) throw ("not-found"); extension = new Extension(type.maps.dirs[uuid], type, force, uuid); if(!type.callbacks.finishExtensionLoad(extension)) throw (type.name + ' ' + uuid + ': Could not create applet object.'); extension.finalize(); Main.cinnamonDBusService.EmitXletAddedComplete(true, uuid); } catch (e) { /* Silently fail to load xlets that aren't actually installed - but no error, since the user can't do anything about it anyhow (short of editing gsettings). Silent failure is consistent with other reactions in Cinnamon to missing items (e.g. panel launchers just don't show up if their program isn't installed, but we don't remove them or anything) */ if (e == "not-found") { forgetExtension(uuid, type, true); return null; } Main.cinnamonDBusService.EmitXletAddedComplete(false, uuid); Main.xlet_startup_error = true; forgetExtension(uuid, type); if(e._alreadyLogged) e = undefined; global.logError('Could not load ' + type.name.toLowerCase() + ' ' + uuid, e); type.maps.meta[uuid].state = State.ERROR; if (!type.maps.meta[uuid].name) type.maps.meta[uuid].name = uuid return null; } } return extension; } /** * unloadExtension: * * @uuid (string): uuid of xlet * @type (Extension.Type): type of xlet * @deleteConfig (bool): delete also config files, defaults to true */ function unloadExtension(uuid, type, deleteConfig = true) { let extension = type.maps.objects[uuid]; if (extension) { extension.unlockRole(); // Try to disable it -- if it's ERROR'd, we can't guarantee that, // but it will be removed on next reboot, and hopefully nothing // broke too much. try { extension.type.callbacks.prepareExtensionUnload(extension, deleteConfig); } catch(e) { global.logError('Error disabling ' + extension.lowerType + ' ' + extension.uuid, e); } extension.unloadStylesheet(); extension.unloadIconDirectory(); extension.type.emit('extension-unloaded', extension.uuid); forgetExtension(extension.uuid, type, true); } } function forgetExtension(uuid, type, forgetMeta) { delete type.maps.importObjects[uuid]; delete type.maps.objects[uuid]; if(forgetMeta) delete type.maps.meta[uuid]; } /** * reloadExtension: * * @uuid (string): uuid of xlet * @type (Extension.Type): type of xlet * * Reloads an xlet. Useful when the source has changed. */ function reloadExtension(uuid, type) { let extension = type.maps.objects[uuid]; if(extension) unloadExtension(uuid, type, false); loadExtension(uuid, type); } function findExtensionDirectory(uuid, type) { let dirPath = type.userDir + "/" + uuid; let dir = Gio.file_new_for_path(dirPath); if (dir.query_file_type(Gio.FileQueryInfoFlags.NONE, null) == Gio.FileType.DIRECTORY) return dir; let systemDataDirs = GLib.get_system_data_dirs(); for (let datadir of systemDataDirs) { dirPath = datadir + '/cinnamon/' + type.folder + '/' + uuid; dir = Gio.file_new_for_path(dirPath); if (dir.query_file_type(Gio.FileQueryInfoFlags.NONE, null) == Gio.FileType.DIRECTORY) return dir; } return null; } /** * findExtensionSubdirectory: * @dir (Gio.File): directory to search in * * For extensions that are shipped with multiple versions in different * directories, look for the largest available version that is less than or * equal to the current running version. If no such version is found, the * original directory is returned. * * Returns (Gio.File): directory object of the desired directory. */ function findExtensionSubdirectory(dir) { try { let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); let info; let largest = null; while ((info = fileEnum.next_file(null)) != null) { let fileType = info.get_file_type(); if (fileType != Gio.FileType.DIRECTORY) continue; let name = info.get_name(); if (!name.match(/^[1-9][0-9]*\.[0-9]+(\.[0-9]+)?$/)) continue; if (versionLeq(name, Config.PACKAGE_VERSION) && (!largest || versionLeq(largest[0], name))) { largest = [name, fileEnum.get_child(info)]; } } fileEnum.close(null); if (largest) return largest[1]; else return dir; } catch (e) { global.logError('Error looking for extension version for ' + dir.get_basename() + ' in directory ' + dir, e); return dir; } } function get_max_instances (uuid, type) { if (uuid in type.maps.meta) { if ("max-instances" in type.maps.meta[uuid]) { let i = type.maps.meta[uuid]["max-instances"]; return parseInt(i); } } return 1; } Cinnamon-2.8.6/js/ui/panel.js0000664000175000017500000022723612625666430014731 0ustar fabiofabio/** * FILE:panel.js * @short_description: The file responsible for managing panels * * This file is where everything about panels happens. #Main will create a * #PanelManager object, which is responsible for creating and moving panels. * There is also a %checkPanelUpgrade function used as a transition between the * old panel settings and the new panel settings. */ const Cairo = imports.cairo; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Applet = imports.ui.applet; const AppletManager = imports.ui.appletManager; const DND = imports.ui.dnd; const Gtk = imports.gi.Gtk; const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const PopupMenu = imports.ui.popupMenu; const SignalManager = imports.misc.signalManager; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const BUTTON_DND_ACTIVATION_TIMEOUT = 250; const ANIMATED_ICON_UPDATE_TIMEOUT = 100; const SPINNER_ANIMATION_TIME = 0.2; const AUTOHIDE_ANIMATION_TIME = 0.2; const TIME_DELTA = 1500; const APPLETS_DROP_ANIMATION_TIME = 0.2; const PANEL_AUTOHIDE_KEY = "panels-autohide"; const PANEL_SHOW_DELAY_KEY = "panels-show-delay"; const PANEL_HIDE_DELAY_KEY = "panels-hide-delay"; const PANEL_HEIGHT_KEY = "panels-height"; const PANEL_RESIZABLE_KEY = "panels-resizable"; const PANEL_SCALE_TEXT_ICONS_KEY = "panels-scale-text-icons"; const DEFAULT_VALUES = {"panels-autohide": "false", "panels-show-delay": "0", "panels-hide-delay": "0", "panels-height": "25", "panels-resizable": "false", "panels-scale-text-icons": "true"}; const Direction = { LEFT : 0, RIGHT : 1 } // To make sure the panel corners blend nicely with the panel, // we draw background and borders the same way, e.g. drawing // them as filled shapes from the outside inwards instead of // using cairo stroke(). So in order to give the border the // appearance of being drawn on top of the background, we need // to blend border and background color together. // For that purpose we use the following helper methods, taken // from st-theme-node-drawing.c function _norm(x) { return Math.round(x / 255); } function _over(srcColor, dstColor) { let src = _premultiply(srcColor); let dst = _premultiply(dstColor); let result = new Clutter.Color(); result.alpha = src.alpha + _norm((255 - src.alpha) * dst.alpha); result.red = src.red + _norm((255 - src.alpha) * dst.red); result.green = src.green + _norm((255 - src.alpha) * dst.green); result.blue = src.blue + _norm((255 - src.alpha) * dst.blue); return _unpremultiply(result); } function _premultiply(color) { return new Clutter.Color({ red: _norm(color.red * color.alpha), green: _norm(color.green * color.alpha), blue: _norm(color.blue * color.alpha), alpha: color.alpha }); }; function _unpremultiply(color) { if (color.alpha == 0) return new Clutter.Color(); let red = Math.min((color.red * 255 + 127) / color.alpha, 255); let green = Math.min((color.green * 255 + 127) / color.alpha, 255); let blue = Math.min((color.blue * 255 + 127) / color.alpha, 255); return new Clutter.Color({ red: red, green: green, blue: blue, alpha: color.alpha }); }; /** * calculateAllocation: * @leftMin (real): minimum width of left box * @leftNatural (real): natural width of left box * @centerMin (real): minimum width of center box * @centerNatural (real): natural width of center box * @rightMin (real): minimum width of right box * @rightNatural (real): natural width of right box * @alloc (real): total available width to allocate * @centerOccupied (boolean): whether the center box is occupied. * * Given the minimum and natural width requested by each box, this function * calculates how much width should actually allocated to each box. The * function returns two variables [@left, @right], which is the expected width * of each side. * * The expected outcome of the code is as follows: * * Assuming that the centerBox is filled, the primary objective is to center * the centerBox whenever possible. This will be done all the time unless doing * so requires some box's width to go under its minimum width. * * If we are centering the centerBox, there are two possible scenarios. * Firstly, if the centerBox can be perfectly centered while everything takes * their natural size, then everything will be allocated at least their natural * size such that the centerBox is centered, leftBox is left aligned, rightBox * is right aligned. * * Otherwise, we first allocate the minWidth to every box, and then distribute * the remaining space proportional to how much more space each box wants. * This is done in a way that ensures the leftWidth and rightWidth are equal. * * If it is not possible to center the centerBox, but there is enough space to * just allocate the boxes, the centerBox will be made as centered as possible * without making things go under their minWidth. This is achieved by making * the shorter box go to their min width, and distributing the remaining space * among the two other boxes. * * Finally, if there isn't even enough space to just put the things, the width * allocated is just proportional to the minimum width. * * In the cases where the centerBox is not occupied, a similar mechanism is * employed. If there is enough space for everything to get their natural * width, this will happen. Otherwise, we first allocate the minimum width and * then distribute the remaining space proportional to how much more space each * box wants. In the scenario where the isn't enough space to just allocate the * minimum width, we just allocate proportional to the minimum width. * * Returns (array): The left and right widths to be allocated. */ function calculateAllocation(leftMin, leftNatural, centerMin, centerNatural, rightMin, rightNatural, alloc, centerOccupied) { let totalMin = leftMin + centerMin + rightMin; let totalNatural = leftNatural + centerNatural + rightNatural; let sideMin = Math.max(leftMin, rightMin); let sideNatural = Math.max(leftNatural, rightNatural); let totalCentMin = centerMin + 2 * sideMin; let totalCentNatural = centerNatural + 2 * sideNatural; let left, right; if (centerOccupied) { if (totalCentNatural < alloc) { /* We can give everything their natural width and center will * still be centered. */ left = (alloc - centerNatural) / 2; right = left; } else if (totalCentMin < alloc) { /* Center can be centered as without shrinking things too much. * First give everything the min they want, and they * distribute the remaining space proportional to how much the * regions want. */ let totalRemaining = alloc - totalCentMin; let totalWant = totalCentNatural - totalCentMin; // totalWant != 0 or else totalCentNatural == totalCentMin left = sideMin + (sideNatural - sideMin) / totalWant * totalRemaining; right = left; } else if (totalMin < alloc) { /* There is enough space for min if we don't care about * centering. Make center things as center as possible */ if (leftMin > rightMin) { left = leftMin; if (leftMin + centerNatural + rightNatural < alloc) { right = alloc - leftMin - centerNatural; } else { let totalRemaining = alloc - totalMin; let totalWant = centerNatural + rightNatural - (centerMin + rightMin); right = rightMin; if (totalWant > 0) right += (rightNatural - rightMin) / totalWant * totalRemaining; } } else { right = rightMin; if (rightMin + centerNatural + leftNatural < alloc) { left = alloc - rightMin - centerNatural; } else { let totalRemaining = alloc - totalMin; let totalWant = centerNatural + leftNatural - (centerMin + leftMin); left = leftMin; if (totalWant > 0) left += (leftNatural - rightMin) / totalWant * totalRemaining; } } } else { /* Scale everything down according to their min. */ left = leftMin / totalMin * alloc; right = rightMin / totalMin * alloc; } } else { if (totalNatural < alloc) { /* Everything's fine. Allocate as usual. */ left = leftNatural; right = rightNatural; } else if (totalMin < alloc) { /* There is enough space for min but not for natural. * Allocate the min and then divide the remaining space * according to how much more they want. */ let totalRemaining = alloc - totalMin; let totalWant = totalNatural - totalMin; // totalWant != 0 or else totalMin == totalNatural left = leftMin + (leftNatural - leftMin) / totalWant * totalRemaining; right = rightMin + (rightNatural - rightMin) / totalWant * totalRemaining; } else { /* Scale everything down according to their min. */ // totalMin != 0 or else totalMin < alloc left = leftMin / totalMin * alloc; right = rightMin / totalMin * alloc; } } return [Math.round(left), Math.round(right)]; } /** * checkPanelUpgrade: * * Run from main, prior to PanelManager being initialized * this handles the one-time transition between panel implementations * to make this transition invisible to the user. We will evaluate the * desktop-layout key, and pre-set applets-enabled and panels-enabled * appropriately. */ function checkPanelUpgrade() { let oldLayout = global.settings.get_string("desktop-layout"); let doIt = false; /* GLib >= 2.4 has get_user_value, use that if possible - this being null * indicates either the user never changed from the default "traditional" * panel layout, or else this upgrade has already been performed (since * with this set of patches, its default value goes from traditional to nothing.) * Either way, we don't need to do anything in this case. With glib < 2.4, * we instead check if the value is set to "" - either by result of the new * default, or by this upgrade already having been run. */ try { doIt = (global.settings.get_user_value("desktop-layout") != null) } catch (e) { doIt = (global.settings.get_string("desktop-layout") != ""); } if (!doIt) return; switch (oldLayout) { case "flipped": global.settings.set_strv("panels-enabled", ["1:0:top"]); break; case "classic": global.settings.set_strv("panels-enabled", ["1:0:top", "2:0:bottom"]); break; case "traditional": /* Default (explicitly set) - no processing needed */ default: break; } global.settings.reset("desktop-layout"); } /** * #PanelManager * * @short_description: Manager of Cinnamon panels * * #PanelManager creates panels and startup and * provides methods for easier access of panels */ function PanelManager() { this._init(); } PanelManager.prototype = { _init: function() { this.panels = []; this.panelsMeta = []; // Properties of panels in format [, ] this.canAdd = true; // Whether there is space for more panels to be added let panelProperties = global.settings.get_strv("panels-enabled"); for (let i in panelProperties) { let elements = panelProperties[i].split(":"); if (elements.length != 3) { global.log("Invalid panel definition: " + panelProperties[i]); continue; } this._loadPanel(parseInt(elements[0]), parseInt(elements[1]), elements[2]=="bottom"); } this._setMainPanel(); this.addPanelMode = false; this._panelsEnabledId = global.settings.connect("changed::panels-enabled", Lang.bind(this, this._onPanelsEnabledChanged)); this._panelEditModeId = global.settings.connect("changed::panel-edit-mode", Lang.bind(this, this._onPanelEditModeChanged)); this._monitorsChangedId = global.screen.connect("monitors-changed", Lang.bind(this, this._onMonitorsChanged)); this._addOsd = new ModalDialog.InfoOSD(_("Select position of new panel. Esc to cancel.")); this._moveOsd = new ModalDialog.InfoOSD(_("Select new position of panel. Esc to cancel.")); this._addOsd.hide(); this._moveOsd.hide(); this._checkCanAdd(); }, /** * disablePanels: * * Disables (hide and lock) all panels */ disablePanels: function() { for (let i in this.panels) { if (this.panels[i]) this.panels[i].disable(); } }, /** * enablePanels: * * Enables all panels */ enablePanels: function() { for (let i in this.panels) { if (this.panels[i]) this.panels[i].enable(); } }, /** * setPanelsOpacity: * @opacity (int): opacity of panels * * Sets the opacity of all hideable panels to @opacity */ setPanelsOpacity: function(opacity) { for (let i in this.panels) { if (this.panels[i] && this.panels[i].isHideable()) this.panels[i].opacity = opacity; } }, /** * removePanel: * @panelId (int): Panel id of the panel to be removed * * Remove the panel from the list panels-enabled */ removePanel: function(panelId) { let list = global.settings.get_strv("panels-enabled"); for (let i in list) { if (list[i].split(":")[0] == panelId) { list.splice(i, 1); break; } } global.settings.set_strv("panels-enabled", list); }, /** * addPanel: * @monitorIndex (integer): monitor to be added to * @bottomPosition (boolean): whether the panel is added to the bottom * * Adds a new panel to the specified position */ addPanel: function(monitorIndex, bottomPosition) { let list = global.settings.get_strv("panels-enabled"); let i = 0; // Start counting at 1 for compatibility // Magic: Keep recursing until there is a free panel id while (true) if (!this.panelsMeta[++i]) break; // Add default values outerLoop: for (let key in DEFAULT_VALUES) { let settings = global.settings.get_strv(key); for (let j = 0; j < settings.length; j++){ if (settings[j].split(":")[0] == i){ continue outerLoop; } } settings.push(i + ":" + DEFAULT_VALUES[key]); global.settings.set_strv(key, settings); } list.push(i + ":" + monitorIndex + ":" + (bottomPosition ? "bottom" : "top")); global.settings.set_strv("panels-enabled", list); // Delete all panel dummies if (this.addPanelMode) this._destroyDummyPanels(); }, /** * movePanel: * @monitorIndex (integer): monitor to be added to * @bottomPosition (boolean): whether the panel is added to the bottom * * Moves the panel of id this.moveId to the specified position */ movePanel: function(monitorIndex, bottomPosition) { let list = global.settings.get_strv("panels-enabled"); let i = -1; for (let i in list) { if (list[i].split(":")[0] == this.moveId) { list[i] = this.moveId + ":" + monitorIndex + ":" + (bottomPosition ? "bottom" : "top"); break; } } global.settings.set_strv("panels-enabled", list); // Delete all panel dummies if (this.addPanelMode) this._destroyDummyPanels(); }, /** * _destroyDummyPanels: * * Destroys all panel dummies */ _destroyDummyPanels: function() { for (let i in this.dummyPanels) { if (this.dummyPanels[i][0]) this.dummyPanels[i][0].destroy(); if (this.dummyPanels[i][1]) this.dummyPanels[i][1].destroy(); delete this.dummyPanels[i][0]; delete this.dummyPanels[i][1]; } this.addPanelMode = false; this._addOsd.hide(); this._moveOsd.hide(); if (Main.keybindingManager.bindings['close-add-panel']) Main.keybindingManager.removeHotKey('close-add-panel'); }, /** * getPanelInMonitor: * @monitorIndex (integer): index of monitor * * Retrieves all the panels in the monitor of index @monitorIndex * * Returns: an array of panels */ getPanelsInMonitor: function(monitorIndex) { let returnValue = []; for (let i in this.panels) { if (this.panels[i].monitorIndex == monitorIndex) returnValue.push(this.panels[i]); } return returnValue; }, /** * getPanels: * * Retrieves all panels * * Returns: an array of panels */ getPanels: function() { return this.panels; }, /** * getPanel: * @monitorIndex (integer): index of monitor * @bottomPosition (boolean): whether the bottom panel is wanted * * Gets a specific panel in monitor @monitorIndex (bottom panel if @bottomPosition is true) * * Returns: the panel required (null if panel not found) */ getPanel: function(monitorIndex, bottomPosition) { for (let i in this.panels) { if (!this.panels[i]) continue; if (this.panels[i].monitorIndex == monitorIndex && this.panels[i].bottomPosition == bottomPosition) return this.panels[i]; } return null; }, /** * updatePanelsVisibility: * * Prompts every panel to update its visibility (show/hide). This is used * by WindowManager after window map/tile/etc animations, and after popup * menus close. */ updatePanelsVisibility: function() { for (let i in this.panels) { if (!this.panels[i]) continue; this.panels[i]._updatePanelVisibility(); } }, /** * _loadPanel: * @ID (integer): panel id * @monitorIndex (integer): index of monitor of panel * @bottomPosition (boolean): whether the panel should be at the bottom or not * @panelList (array): (optional) the list in which the new panel should be appended to (not necessarily this.panels, c.f. _onPanelsEnabledChanged) Default: this.panels * @metaList(array): (optional) the list in which the new panel metadata should be appended to (not necessarily this.panelsMeta, c.f. _onPanelsEnabledChanged) Default: this.panelsMeta * * Loads a panel with the given properties and appends it to @panelList. @panelList is usually this.panels but is a different array when used by _onPanelsEnabledChanged. * * Returns (Panel.Panel): Panel created */ _loadPanel: function(ID, monitorIndex, bottomPosition, panelList, metaList) { if (!panelList) panelList = this.panels; if (!metaList) metaList = this.panelsMeta; if (panelList[ID]) { global.log("Multiple panels with same ID (" + ID + ") are found"); return null; } panelList.length = Math.max(panelList.length, ID+1); metaList.length = panelList.length; let repeat = false; for (let i in metaList) { if ((metaList[i][0] == monitorIndex) && (metaList[i][1] == bottomPosition) && i != ID) { global.log("Conflicting panel definitions: " + ID + ":" + monitorIndex + ":" + (bottomPosition ? "bottom" : "top" )); repeat = true; break; } } if (repeat) return null; metaList[ID] = [monitorIndex, bottomPosition]; if (monitorIndex < 0 || monitorIndex >= global.screen.get_n_monitors()) { global.log("Monitor " + monitorIndex + " not found. Not creating panel"); return null; } panelList[ID] = new Panel(ID, monitorIndex, bottomPosition); return panelList[ID]; }, _checkCanAdd: function() { let monitorCount = global.screen.get_n_monitors(); let panelCount = monitorCount * 2; for (let i in this.panelsMeta) { if (this.panelsMeta[i][0] >= monitorCount) // Monitor does not exist continue; panelCount --; } if (this.canAdd != (panelCount != 0)) { this.canAdd = (panelCount != 0); } }, _updateAllPointerBarriers: function() { this.panels.forEach(function(panel) { panel._updatePanelBarriers(); }); }, _onPanelsEnabledChanged: function() { let newPanels = new Array(this.panels.length); let newMeta = new Array(this.panels.length); let panelProperties = global.settings.get_strv("panels-enabled"); for (let i = 0; i < panelProperties.length; i ++) { let elements = panelProperties[i].split(":"); if (elements.length != 3) { global.log("Invalid panel definition: " + panelProperties[i]); continue; } let ID = parseInt(elements[0]); // If panel is moved if (this.panels[ID]) { // Move panel object to newPanels newPanels[ID] = this.panels[ID]; newMeta[ID] = [parseInt(elements[1]), elements[2]=="bottom"]; this.panels[ID] = null; if (newMeta[ID][0] != this.panelsMeta[ID][0] || newMeta[ID][1] != this.panelsMeta[ID][1]) { newPanels[ID].updatePosition(newMeta[ID][0], newMeta[ID][1]); AppletManager.updateAppletsOnPanel(newPanels[ID]); } } else { let panel = this._loadPanel(ID, parseInt(elements[1]), elements[2]=="bottom", newPanels, newMeta); if (panel) AppletManager.loadAppletsOnPanel(panel); } } // Destroy removed panels for (let i in this.panels) if (this.panels[i]) this.panels[i].destroy(); this.panels = newPanels; this.panelsMeta = newMeta; this._setMainPanel(); this._checkCanAdd(); this._updateAllPointerBarriers(); }, _onMonitorsChanged: function() { let monitorCount = global.screen.get_n_monitors(); for (let i in this.panelsMeta) { if (this.panelsMeta[i] && !this.panels[i]) { // If there is a meta but not a panel, i.e. panel could not create due to non-existent monitor, try again. let panel = this._loadPanel(i, this.panelsMeta[i][0], this.panelsMeta[i][1]); if (panel) AppletManager.loadAppletsOnPanel(panel); } else if (this.panelsMeta[i][0] >= monitorCount) { // Monitor of the panel went missing this.panels[i].destroy(); delete this.panels[i]; } else { // Nothing happens. Re-allocate panel this.panels[i]._moveResizePanel(); } } if (this.addPanelMode) { this._destroyDummyPanels(); this._showDummyPanels(this.dummyCallback); } this._setMainPanel(); this._checkCanAdd(); }, _onPanelEditModeChanged: function() { if (!global.settings.get_boolean("panel-edit-mode")) { if (this.addPanelMode) this._destroyDummyPanels(); } }, /** * addPanelQuery: * * Prompts user where to add the panel */ addPanelQuery: function() { if (this.addPanelMode || !this.canAdd) return; this._showDummyPanels(Lang.bind(this, this.addPanel)); this._addOsd.show(); }, /** * movePanelQuery: * @id (integer): the id of the panel to be moved * * Prompts user where to move the panel */ movePanelQuery: function(id) { if (this.addPanelMode || !this.canAdd) return; this.moveId = id; this._showDummyPanels(Lang.bind(this, this.movePanel)); this._moveOsd.show(); }, _showDummyPanels: function(callback) { let monitorCount = global.screen.get_n_monitors(); this.dummyCallback = callback; this.dummyPanels = []; while (this.dummyPanels.push([]) < monitorCount); // Generate a 2D array of length monitorCount; Push returns new length of array for (let i in this.panelsMeta) { if (this.panelsMeta[i][0] >= monitorCount) // Monitor does not exist continue; this.dummyPanels[this.panelsMeta[i][0]][this.panelsMeta[i][1] ? 0 : 1] = false; } for (let i = 0; i < monitorCount; i++) { for (let j = 0; j < 2; j++) { if (this.dummyPanels[i][j] != false) { this.dummyPanels[i][j] = new PanelDummy(i, j == 0, callback); } } } this.addPanelMode = true; Main.keybindingManager.addHotKey('close-add-panel', 'Escape', Lang.bind(this, function() { if (this.addPanelMode) this._destroyDummyPanels(); })); return true; }, // Set Main.panel so that applets that look for it don't break _setMainPanel: function() { for (let i = 0; i < this.panels.length; i++) { if (this.panels[i]) { Main.panel = this.panels[i]; break; } } } } /** * #PanelDummy * @short_description: Dummy panels for users to select new position of panel * * #PanelDummy creates some boxes at possible panel locations for users to * select where to place their new panels */ function PanelDummy(monitorIndex, bottomPosition, callback) { this._init(monitorIndex, bottomPosition, callback); } PanelDummy.prototype = { _init: function(monitorIndex, bottomPosition, callback) { this.monitorIndex = monitorIndex; this.bottomPosition = bottomPosition; this.callback = callback; this.monitor = global.screen.get_monitor_geometry(monitorIndex); this.actor = new Cinnamon.GenericContainer({style_class: "panel-dummy", reactive: true, track_hover: true, important: true}); Main.layoutManager.addChrome(this.actor, { addToWindowgroup: false }); this.actor.set_size(this.monitor.width, 25 * global.ui_scale); this.actor.set_position(this.monitor.x, bottomPosition ? this.monitor.y + this.monitor.height - (25 * global.ui_scale) : this.monitor.y); this.actor.connect('button-press-event', Lang.bind(this, this._onClicked)); this.actor.connect('enter-event', Lang.bind(this, this._onEnter)); this.actor.connect('leave-event', Lang.bind(this, this._onLeave)); }, _onClicked: function() { this.callback(this.monitorIndex, this.bottomPosition); }, _onEnter: function() { this.actor.add_style_pseudo_class('entered'); if (this.noStyle) this.actor.opacity = 160; }, _onLeave: function() { this.actor.remove_style_pseudo_class('entered'); if (this.noStyle) this.actor.opacity = 100; }, /** * destroy: * * Destroys panel dummy actor */ destroy: function() { this.actor.destroy(); } } function AnimatedIcon(name, size) { this._init(name, size); } AnimatedIcon.prototype = { _init: function(name, size) { this.actor = new St.Bin({ visible: false }); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('notify::visible', Lang.bind(this, function() { if (this.actor.visible) { this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update)); } else { if (this._timeoutId) Mainloop.source_remove(this._timeoutId); this._timeoutId = 0; } })); this._timeoutId = 0; this._i = 0; this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size, null); this.actor.set_child(this._animations); }, _update: function() { this._animations.hide_all(); this._animations.show(); if (this._i && this._i < this._animations.get_n_children()) this._animations.get_child_at_index(this._i++).show(); else { this._i = 1; if (this._animations.get_n_children()) this._animations.get_child_at_index(0).show(); } return true; }, _onDestroy: function() { if (this._timeoutId) Mainloop.source_remove(this._timeoutId); } }; function TextShadower() { this._init(); } TextShadower.prototype = { _init: function() { this.actor = new Cinnamon.GenericContainer(); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this._label = new St.Label(); this.actor.add_actor(this._label); for (let i = 0; i < 4; i++) { let actor = new St.Label({ style_class: 'label-shadow' }); actor.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.actor.add_actor(actor); } this._label.raise_top(); }, _getPreferredWidth: function(actor, forHeight, alloc) { let [minWidth, natWidth] = this._label.get_preferred_width(forHeight); alloc.min_size = minWidth + 2; alloc.natural_size = natWidth + 2; }, _getPreferredHeight: function(actor, forWidth, alloc) { let [minHeight, natHeight] = this._label.get_preferred_height(forWidth); alloc.min_size = minHeight + 2; alloc.natural_size = natHeight + 2; }, _allocate: function(actor, box, flags) { let children = this.actor.get_children(); let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = this._label.get_preferred_size(); let childWidth = Math.min(natChildWidth, availWidth - 2); let childHeight = Math.min(natChildHeight, availHeight - 2); for (let i = 0; i < children.length; i++) { let child = children[i]; let childBox = new Clutter.ActorBox(); // The order of the labels here is arbitrary, except // we know the "real" label is at the end because Clutter.Group // sorts by Z order switch (i) { case 0: // top childBox.x1 = 1; childBox.y1 = 0; break; case 1: // right childBox.x1 = 2; childBox.y1 = 1; break; case 2: // bottom childBox.x1 = 1; childBox.y1 = 2; break; case 3: // left childBox.x1 = 0; childBox.y1 = 1; break; case 4: // center childBox.x1 = 1; childBox.y1 = 1; break; } childBox.x2 = childBox.x1 + childWidth; childBox.y2 = childBox.y1 + childHeight; child.allocate(childBox, flags); } } }; function PanelCorner(panel, side) { this._init(panel, side); } PanelCorner.prototype = { _init: function(box, side) { this._side = side; this._box = box; this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged)); this.actor = new St.DrawingArea({ style_class: 'panel-corner' }); this.actor.connect('style-changed', Lang.bind(this, this._styleChanged)); this.actor.connect('repaint', Lang.bind(this, this._repaint)); }, _findRightmostButton: function(container) { if (!container.get_children) return null; let children = container.get_children(); if (!children || children.length == 0) return null; // Start at the back and work backward let index = children.length - 1; while (index >= 0 && !children[index].visible) index--; if (index < 0) return null; return children[index]; }, _findLeftmostButton: function(container) { if (!container.get_children) return null; let children = container.get_children(); if (!children || children.length == 0) return null; // Start at the front and work forward let index = 0; while (index < children.length && !children[index].visible) index++; if (index == children.length) return null; return children[index]; }, _boxStyleChanged: function() { let side = this._side; let rtlAwareContainer = this._box instanceof St.BoxLayout; if (rtlAwareContainer && this._box.get_direction() == St.TextDirection.RTL) { if (this._side == St.Side.LEFT) side = St.Side.RIGHT; else if (this._side == St.Side.RIGHT) side = St.Side.LEFT; } let button; if (side == St.Side.LEFT) button = this._findLeftmostButton(this._box); else if (side == St.Side.RIGHT) button = this._findRightmostButton(this._box); if (button) { if (this._button && this._buttonStyleChangedSignalId) { this._button.disconnect(this._buttonStyleChangedSignalId); this._button.style = null; } this._button = button; button.connect('destroy', Lang.bind(this, function() { if (this._button == button) { this._button = null; this._buttonStyleChangedSignalId = 0; } })); // Synchronize the locate button's pseudo classes with this corner this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this, function(actor) { let pseudoClass = button.get_style_pseudo_class(); this.actor.set_style_pseudo_class(pseudoClass); })); // The corner doesn't support theme transitions, so override // the .panel-button default button.style = 'transition-duration: 0'; } }, _repaint: function() { let node = this.actor.get_theme_node(); let cornerRadius = node.get_length("-panel-corner-radius"); let innerBorderWidth = node.get_length('-panel-corner-inner-border-width'); let outerBorderWidth = node.get_length('-panel-corner-outer-border-width'); let backgroundColor = node.get_color('-panel-corner-background-color'); let innerBorderColor = node.get_color('-panel-corner-inner-border-color'); let outerBorderColor = node.get_color('-panel-corner-outer-border-color'); let cr = this.actor.get_context(); cr.setOperator(Cairo.Operator.SOURCE); cr.moveTo(0, 0); if (this._side == St.Side.LEFT) cr.arc(cornerRadius, innerBorderWidth + cornerRadius, cornerRadius, Math.PI, 3 * Math.PI / 2); else cr.arc(0, innerBorderWidth + cornerRadius, cornerRadius, 3 * Math.PI / 2, 2 * Math.PI); cr.lineTo(cornerRadius, 0); cr.closePath(); let savedPath = cr.copyPath(); let over = _over(innerBorderColor, _over(outerBorderColor, backgroundColor)); Clutter.cairo_set_source_color(cr, over); cr.fill(); let xOffsetDirection = this._side == St.Side.LEFT ? -1 : 1; let offset = outerBorderWidth; over = _over(innerBorderColor, backgroundColor); Clutter.cairo_set_source_color(cr, over); cr.save(); cr.translate(xOffsetDirection * offset, - offset); cr.appendPath(savedPath); cr.fill(); cr.restore(); if (this._side == St.Side.LEFT) cr.rectangle(cornerRadius - offset, 0, offset, outerBorderWidth); else cr.rectangle(0, 0, offset, outerBorderWidth); cr.fill(); offset = innerBorderWidth; Clutter.cairo_set_source_color(cr, backgroundColor); cr.save(); cr.translate(xOffsetDirection * offset, - offset); cr.appendPath(savedPath); cr.fill(); cr.restore(); cr.$dispose(); }, _styleChanged: function() { let node = this.actor.get_theme_node(); let cornerRadius = node.get_length("-panel-corner-radius"); let innerBorderWidth = node.get_length('-panel-corner-inner-border-width'); this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius); this.actor.set_anchor_point(0, innerBorderWidth); } }; function SettingsLauncher(label, keyword, icon) { this._init(label, keyword, icon); } SettingsLauncher.prototype = { __proto__: PopupMenu.PopupIconMenuItem.prototype, _init: function (label, keyword, icon) { PopupMenu.PopupIconMenuItem.prototype._init.call(this, label, icon, St.IconType.SYMBOLIC); this._keyword = keyword; this.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings " + this._keyword); })); }, }; function populateSettingsMenu(menu, panelId) { menu.troubleshootItem = new PopupMenu.PopupSubMenuMenuItem(_("Troubleshoot")); menu.troubleshootItem.menu.addAction(_("Restart Cinnamon"), function(event) { global.reexec_self(); }); menu.troubleshootItem.menu.addAction(_("Looking Glass"), function(event) { Main.createLookingGlass().open(); }); menu.troubleshootItem.menu.addAction(_("Restore all settings to default"), function(event) { let confirm = new ModalDialog.ConfirmDialog("Are you sure you want to restore all settings to default?\n\n", function() { Util.spawnCommandLine("gsettings reset-recursively org.cinnamon"); global.reexec_self(); }); confirm.open(); }); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); menu.addMenuItem(menu.troubleshootItem); let panelSettingsSection = new PopupMenu.PopupSubMenuMenuItem(_("Modify panel")); let menuItem = new PopupMenu.PopupIconMenuItem(_("Remove panel"), "list-remove", St.IconType.SYMBOLIC); menuItem.activate = Lang.bind(menu, function() { Main.panelManager.removePanel(panelId); }); panelSettingsSection.menu.addMenuItem(menuItem); menu.addPanelItem = new PopupMenu.PopupIconMenuItem(_("Add panel"), "list-add", St.IconType.SYMBOLIC); menu.addPanelItem.activate = Lang.bind(menu, function() { Main.panelManager.addPanelQuery(); this.close(); }); panelSettingsSection.menu.addMenuItem(menu.addPanelItem); menu.movePanelItem = new PopupMenu.PopupIconMenuItem(_("Move panel"), "move", St.IconType.SYMBOLIC); menu.movePanelItem.activate = Lang.bind(menu, function() { Main.panelManager.movePanelQuery(this.panelId); this.close(); }); panelSettingsSection.menu.addMenuItem(menu.movePanelItem); menu.copyAppletItem = new PopupMenu.PopupIconMenuItem(_("Copy applet configuration"), "edit-copy", St.IconType.SYMBOLIC); menu.copyAppletItem.activate = Lang.bind(menu, function() { AppletManager.copyAppletConfiguration(this.panelId); this.close(); }); panelSettingsSection.menu.addMenuItem(menu.copyAppletItem); menu.pasteAppletItem = new PopupMenu.PopupIconMenuItem(_("Paste applet configuration"), "edit-paste", St.IconType.SYMBOLIC); menu.pasteAppletItem.activate = Lang.bind(menu, function() { let dialog = new ModalDialog.ConfirmDialog( _("Pasting applet configuration will remove all existing applets on this panel. Do you want to continue?") + "\n\n", Lang.bind(this, function() { AppletManager.pasteAppletConfiguration(this.panelId); })); dialog.open(); }); panelSettingsSection.menu.addMenuItem(menu.pasteAppletItem); menu.clearAppletItem = new PopupMenu.PopupIconMenuItem(_("Clear all applets"), "edit-clear-all", St.IconType.SYMBOLIC); menu.clearAppletItem.activate = Lang.bind(menu, function() { let dialog = new ModalDialog.ConfirmDialog( _("Are you sure you want to clear all applets on this panel?") + "\n\n", Lang.bind(this, function() { AppletManager.clearAppletConfiguration(this.panelId); })); dialog.open(); }); panelSettingsSection.menu.addMenuItem(menu.clearAppletItem); menu.addMenuItem(panelSettingsSection); menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Panel Edit mode let editMode = global.settings.get_boolean("panel-edit-mode"); let panelEditMode = new PopupMenu.PopupSwitchMenuItem(_("Panel edit mode"), editMode); panelEditMode.connect('toggled', function(item) { global.settings.set_boolean("panel-edit-mode", item.state); }); menu.addMenuItem(panelEditMode); global.settings.connect('changed::panel-edit-mode', function() { panelEditMode.setToggleState(global.settings.get_boolean("panel-edit-mode")); }); } function PanelContextMenu(launcher, orientation, panelId) { this._init(launcher, orientation, panelId); } PanelContextMenu.prototype = { __proto__: PopupMenu.PopupMenu.prototype, _init: function(launcher, orientation, panelId) { PopupMenu.PopupMenu.prototype._init.call(this, launcher.actor, 0.0, orientation, 0); Main.uiGroup.add_actor(this.actor); this.actor.hide(); this.panelId = panelId; let applet_settings_item = new SettingsLauncher(_("Add applets to the panel"), "applets panel" + panelId, "list-add"); this.addMenuItem(applet_settings_item); let menuItem = new SettingsLauncher(_("Panel settings"), "panel " + panelId, "emblem-system"); this.addMenuItem(menuItem); let menuItem = new SettingsLauncher(_("Themes"), "themes", "applications-graphics"); this.addMenuItem(menuItem); let menuSetting = new SettingsLauncher(_("All settings"), "", "preferences-system"); this.addMenuItem(menuSetting); populateSettingsMenu(this, panelId); }, open: function(animate) { PopupMenu.PopupMenu.prototype.open.call(this, animate); this.movePanelItem.setSensitive(Main.panelManager.canAdd); this.addPanelItem.setSensitive(Main.panelManager.canAdd); this.pasteAppletItem.setSensitive(AppletManager.clipboard.length != 0); let defs = AppletManager.enabledAppletDefinitions.idMap; let nonEmpty = false; for (let i in defs) { if (defs[i].panelId == this.panelId) { nonEmpty = true; break; } } this.copyAppletItem.setSensitive(nonEmpty); this.clearAppletItem.setSensitive(nonEmpty); } } function PanelZoneDNDHandler(panelZone){ this._init(panelZone); } PanelZoneDNDHandler.prototype = { _init : function(panelZone) { this._panelZone = panelZone; this._panelZone._delegate = this; this._dragPlaceholder = null; this._dragPlaceholderPos = -1; }, handleDragOver: function(source, actor, x, y, time) { if (!(source instanceof Applet.Applet)) return DND.DragMotionResult.NO_DROP; let children = this._panelZone.get_children(); let appletPos = children.indexOf(source.actor); let pos = 0; for (var i in children){ //if (children[i] == this._dragPlaceholder.actor) continue; if (x > children[i].get_allocation_box().x1 + children[i].width / 2) pos = i; } if (pos != this._dragPlaceholderPos) { this._dragPlaceholderPos = pos; // Don't allow positioning before or after self if (appletPos != -1 && pos == appletPos) { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); } this._dragPlaceholder = null; return DND.DragMotionResult.CONTINUE; } // If the placeholder already exists, we just move // it, but if we are adding it, expand its size in // an animation let fadeIn; if (this._dragPlaceholder) { this._dragPlaceholder.actor.destroy(); fadeIn = false; } else { fadeIn = true; } this._dragPlaceholder = new DND.GenericDragPlaceholderItem(); this._dragPlaceholder.child.set_width (20); this._dragPlaceholder.child.set_height (10); this._panelZone.insert_actor(this._dragPlaceholder.actor, this._dragPlaceholderPos); if (fadeIn) this._dragPlaceholder.animateIn(); } return DND.DragMotionResult.MOVE_DROP; }, acceptDrop: function(source, actor, x, y, time) { if (!(source instanceof Applet.Applet)) return false; let children = this._panelZone.get_children(); let curAppletPos = 0; let insertAppletPos; for (var i in children){ if (children[i]._delegate instanceof Applet.Applet){ children[i]._applet._newOrder = curAppletPos; curAppletPos++; }else if (children[i] == this._dragPlaceholder.actor){ insertAppletPos = curAppletPos; curAppletPos++; } } source.actor._applet._newOrder = insertAppletPos; source.actor._applet._newPanelLocation = this._panelZone; this._clearDragPlaceholder(); actor.destroy(); AppletManager.saveAppletsPositions(); return true; }, _clearDragPlaceholder: function() { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._dragPlaceholder = null; this._dragPlaceholderPos = -1; } } } /** * #Panel: * @short_description: A panel object on the monitor * * @panelId (int): the id of the panel * @monitorIndex (int): the index of the monitor containing the panel * @monitor (Meta.Rectangle): the geometry (bounding box) of the monitor * @bottomPosition (boolean): whether the panel is at the bottom of the screen * @actor (Cinnamon.GenericContainer): the actor of the panel * @scaleMode (boolean): whether the applets should scale with the panel * * @_leftBox (St.BoxLayout): the box containing all the applets in the left region * @_centerBox (St.BoxLayout): the box containing all the applets in the center region * @_rightBox (St.BoxLayout): the box containing all the applets in the right region * @_hidden (boolean): whether the panel is currently hidden * @_disabled (boolean): whether the panel is disabled * @_panelEditMode (boolean): whether the panel edit mode is on * @_context_menu (Panel.PanelContextMenu): the context menu of the panel * * This represents a panel on the screen. */ function Panel(id, monitorIndex, bottomPosition) { this._init(id, monitorIndex, bottomPosition); } Panel.prototype = { _init : function(id, monitorIndex, bottomPosition) { this.panelId = id; this.monitorIndex = monitorIndex; this.monitor = global.screen.get_monitor_geometry(monitorIndex); this.bottomPosition = bottomPosition; this._hidden = false; this._disabled = false; this._panelEditMode = false; this._autohideSettings = this._getProperty(PANEL_AUTOHIDE_KEY, "s"); this._themeFontSize = null; this._destroyed = false; this._signalManager = new SignalManager.SignalManager(this); this.scaleMode = false; this.actor = new Cinnamon.GenericContainer({ name: 'panel', reactive: true }); this.actor._delegate = this; if (this._getProperty(PANEL_RESIZABLE_KEY, "b")) { this.actor.set_height(this._getProperty(PANEL_HEIGHT_KEY, "i") * global.ui_scale); } this._menus = new PopupMenu.PopupMenuManager(this); this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); this.actor.add_actor(this._leftBox); this._leftBoxDNDHandler = new PanelZoneDNDHandler(this._leftBox); this._centerBox = new St.BoxLayout({ name: 'panelCenter' }); this.actor.add_actor(this._centerBox); this._centerBoxDNDHandler = new PanelZoneDNDHandler(this._centerBox); this._rightBox = new St.BoxLayout({ name: 'panelRight', align_end: true}); this.actor.add_actor(this._rightBox); this._rightBoxDNDHandler = new PanelZoneDNDHandler(this._rightBox); if (this.actor.get_direction() == St.TextDirection.RTL) { this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT); this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT); } else { this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT); this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT); } this.actor.add_actor(this._leftCorner.actor); this.actor.add_actor(this._rightCorner.actor); if (this.bottomPosition) this.actor.add_style_class_name('panel-bottom') else this.actor.add_style_class_name('panel-top') this._context_menu = new PanelContextMenu(this, bottomPosition ? St.Side.BOTTOM: St.Side.TOP, id); this._menus.addMenu(this._context_menu); this._context_menu._boxPointer._container.connect('allocate', Lang.bind(this._context_menu._boxPointer, function(actor, box, flags){ this._xPosition = this._xpos; this._shiftActor(); })); this._leftPanelBarrier = 0; this._rightPanelBarrier = 0; Main.layoutManager.addChrome(this.actor, { addToWindowgroup: false }); this._moveResizePanel(); this._onPanelEditModeChanged(); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent)); this.actor.connect('style-changed', Lang.bind(this, this._moveResizePanel)); this.actor.connect('leave-event', Lang.bind(this, this._leavePanel)); this.actor.connect('enter-event', Lang.bind(this, this._enterPanel)); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this._signalManager.connect(global.settings, "changed::" + PANEL_AUTOHIDE_KEY, this._processPanelAutoHide); this._signalManager.connect(global.settings, "changed::" + PANEL_HEIGHT_KEY, this._moveResizePanel); this._signalManager.connect(global.settings, "changed::" + PANEL_RESIZABLE_KEY, this._moveResizePanel); this._signalManager.connect(global.settings, "changed::" + PANEL_SCALE_TEXT_ICONS_KEY, this._onScaleTextIconsChanged); this._signalManager.connect(global.settings, "changed::panel-edit-mode", this._onPanelEditModeChanged); this._signalManager.connect(global.settings, "changed::no-adjacent-panel-barriers", this._updatePanelBarriers); }, /** * updatePosition: * @monitorIndex: integer, index of monitor * @bottomPosition, boolean, whether it should be placed at bottom * * Moves the panel to the monitor @monitorIndex and position @bottomPosition */ updatePosition: function(monitorIndex, bottomPosition) { this.monitorIndex = monitorIndex this.bottomPosition = bottomPosition; this.monitor = global.screen.get_monitor_geometry(monitorIndex); this._moveResizePanel(); this._context_menu = new PanelContextMenu(this, bottomPosition ? St.Side.BOTTOM: St.Side.TOP, this.panelId); this._menus.addMenu(this._context_menu); this._context_menu._boxPointer._container.connect('allocate', Lang.bind(this._context_menu._boxPointer, function(actor, box, flags){ this._xPosition = this._xpos; this._shiftActor(); })); if (this.bottomPosition) this.actor.set_style_class_name('panel-bottom') else this.actor.set_style_class_name('panel-top') }, /** * destroy: * * Destroys the panel */ destroy: function() { if (this._destroyed) return; AppletManager.unloadAppletsOnPanel(this); this._context_menu.close(); this._context_menu.destroy(); this._leftBox.destroy(); this._centerBox.destroy(); this._rightBox.destroy(); this._rightCorner.actor.destroy(); this._leftCorner.actor.destroy(); this.actor.destroy(); this._signalManager.disconnectAllSignals() this._menus = null; this.monitor = null; this._destroyed = true; return; }, /** * highlight: * @highlight (boolean): whether to turn on or off * * Turns on/off the highlight of the panel */ highlight: function(highlight) { this.actor.change_style_pseudo_class('highlight', highlight); }, /** * isHideable: * * Returns: whether the panel can be hidden (auto-hide or intellihide) */ isHideable: function() { return this._autohideSettings != "true"; }, /** * _getProperty * @key (string): name of gsettings key * @type (string): (optional) type of data requested. "b" for boolean, "i" for integer. Default value is string * * Gets the desired property of the panel from gsettings * * Returns: property required */ _getProperty: function(key, type){ let values = global.settings.get_strv(key); let property; for (let i = 0; i < values.length; i++){ if (values[i].split(":")[0]==this.panelId){ property=values[i].split(":")[1]; break; } } if (!property){ property = DEFAULT_VALUES[key]; values.push(this.panelId + ":" + property); global.settings.set_strv(key, values); } switch (type){ case "b": return property=="true"; case "i": return parseInt(property); default: return property; } }, handleDragOver: function(source, actor, x, y, time) { this._enterPanel(); if (this._dragShowId > 0) Mainloop.source_remove(this._dragShowId); let leaveIfOut = Lang.bind(this, function() { this._dragShowId = 0; let [x, y, whatever] = global.get_pointer(); this.actor.sync_hover(); if (this.actor.x < x && x < this.actor.x + this.actor.width && this.actor.y < y && y < this.actor.y + this.actor.height) { return true; } else { this._leavePanel(); return false; } }); this._dragShowId = Mainloop.timeout_add(500, leaveIfOut); return DND.DragMotionResult.NO_DROP; }, _updatePanelBarriers: function() { if (this._leftPanelBarrier) global.destroy_pointer_barrier(this._leftPanelBarrier); if (this._rightPanelBarrier) global.destroy_pointer_barrier(this._rightPanelBarrier); let noBarriers = global.settings.get_boolean("no-adjacent-panel-barriers"); if (this.actor.height) { let panelTop = (this.bottomPosition ? this.monitor.y + this.monitor.height - this.actor.height : this.monitor.y); let panelBottom = (this.bottomPosition ? this.monitor.y + this.monitor.height : this.monitor.y + this.actor.height); if (!noBarriers) { this._rightPanelBarrier = global.create_pointer_barrier( this.monitor.x + this.monitor.width - 1, panelTop, this.monitor.x + this.monitor.width - 1, panelBottom, 4 /* BarrierNegativeX */); this._leftPanelBarrier = global.create_pointer_barrier( this.monitor.x, panelTop, this.monitor.x, panelBottom, 1 /* BarrierPositiveX */); } else { this._rightPanelBarrier = 0; this._leftPanelBarrier = 0; } } else { this._leftPanelBarrier = 0; this._rightPanelBarrier = 0; } }, _onPanelEditModeChanged: function() { let old_mode = this._panelEditMode; this._panelEditMode = global.settings.get_boolean("panel-edit-mode"); this._leftBox.change_style_pseudo_class('dnd', this._panelEditMode); this._centerBox.change_style_pseudo_class('dnd', this._panelEditMode); this._rightBox.change_style_pseudo_class('dnd', this._panelEditMode); if (old_mode != this._panelEditMode) { this._processPanelAutoHide(); } }, _onButtonPressEvent: function (actor, event) { if (event.get_button()==1){ if (this._context_menu.isOpen) { this._context_menu.toggle(); } } if (event.get_button()==3){ try { let [x, y] = event.get_coords(); let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); if (this._context_menu._getMenuItems().length > 0 && target.get_parent() == this.actor) { this._context_menu.toggle(); if (!this._context_menu.isOpen) { return; } x -= this._context_menu._boxPointer._arrowOrigin; let monitor = Main.layoutManager.findMonitorForActor(this._context_menu._boxPointer.actor); let mywidth = this._context_menu._boxPointer.actor.get_allocation_box().x2-this._context_menu._boxPointer.actor.get_allocation_box().x1;//Width of menu if (x + mywidth - monitor.x > monitor.width) { x = monitor.width + monitor.x - mywidth; } if (x < monitor.x) { x = monitor.x; } this._context_menu._boxPointer._xpos = Math.round(x); this._context_menu._boxPointer._xPosition = this._context_menu._boxPointer._xpos; this._context_menu._boxPointer._shiftActor(); } } catch(e) { global.log(e); } } return; }, _onFocusChanged: function() { if (global.display.focus_window && this._focusWindow == global.display.focus_window.get_compositor_private()) return; this._signalManager.disconnect("position-changed"); this._signalManager.disconnect("size-changed"); if (!global.display.focus_window) return; this._focusWindow = global.display.focus_window.get_compositor_private(); this._signalManager.connect(this._focusWindow, "position-changed", this._updatePanelVisibility); this._signalManager.connect(this._focusWindow, "size-changed", this._updatePanelVisibility); this._updatePanelVisibility(); }, _processPanelAutoHide: function() { this._autohideSettings = this._getProperty(PANEL_AUTOHIDE_KEY, "s"); if (this._autohideSettings == "intel") { this._signalManager.connect(global.display, "notify::focus-window", this._onFocusChanged); /* focus-window signal is emitted when the workspace change * animation starts. When the animation ends, we do the position * check again because the windows have moved. We cannot use * _onFocusChanged because _onFocusChanged does nothing when there * is no actual focus change. */ this._signalManager.connect(global.window_manager, "switch-workspace-complete", this._updatePanelVisibility); this._onFocusChanged(); } else { this._signalManager.disconnect("notify::focus-window"); this._signalManager.disconnect("switch-workspace-complete"); this._signalManager.disconnect("position-changed"); this._signalManager.disconnect("size-changed"); } this._updatePanelVisibility(); Main.layoutManager._chrome.modifyActorParams(this.actor, { affectsStruts: this._autohideSettings == "false" }); }, /** * _moveResizePanel: * * Function to update the panel position and size according to settings * values. */ _moveResizePanel: function() { if (this._destroyed) return false; this.monitor = global.screen.get_monitor_geometry(this.monitorIndex); let panelHeight; let panelResizable = this._getProperty(PANEL_RESIZABLE_KEY, "b"); if (panelResizable) { panelHeight = this._getProperty(PANEL_HEIGHT_KEY, "i") * global.ui_scale; } else { let themeNode = this.actor.get_theme_node(); panelHeight = themeNode.get_length("height"); if (!panelHeight || panelHeight == 0) { panelHeight = 25 * global.ui_scale; } } if (!this._themeFontSize) { let themeNode = this.actor.get_theme_node(); this._themeFontSize = themeNode.get_length("font-size"); } this.scaleMode = this._getProperty(PANEL_RESIZABLE_KEY, "b") && this._getProperty(PANEL_SCALE_TEXT_ICONS_KEY, "b"); if (this.scaleMode) { let textheight = (panelHeight / (Applet.DEFAULT_PANEL_HEIGHT * global.ui_scale) * (Applet.PANEL_FONT_DEFAULT_HEIGHT * global.ui_scale)); this.actor.set_style('font-size: ' + textheight / global.ui_scale + 'px;'); } else { this.actor.set_style('font-size: ' + this._themeFontSize / global.ui_scale + 'px;'); } this.actor.set_height(panelHeight); this._processPanelAutoHide(); this.actor.set_size(this.monitor.width, panelHeight); this.actor.set_position(this.monitor.x, this.bottomPosition ? this.monitor.y + this.monitor.height - panelHeight : this.monitor.y); // AppletManager might not be initialized yet if (AppletManager.appletsLoaded) AppletManager.updateAppletPanelHeights(); return true; }, _onScaleTextIconsChanged: function() { let panelHeight = this._getProperty(PANEL_HEIGHT_KEY, "i"); this.scaleMode = this._getProperty(PANEL_RESIZABLE_KEY, "b") && this._getProperty(PANEL_SCALE_TEXT_ICONS_KEY, "b"); if (!this._themeFontSize) { let themeNode = this.actor.get_theme_node(); this._themeFontSize = themeNode.get_length("font-size"); } if (this.scaleMode) { let textheight = (panelHeight / Applet.DEFAULT_PANEL_HEIGHT) * Applet.PANEL_FONT_DEFAULT_HEIGHT; this.actor.set_style('font-size: ' + textheight + 'px;'); } else { this.actor.set_style('font-size: ' + this._themeFontSize ? this._themeFontSize + 'px;' : '8.5pt;'); } AppletManager.updateAppletPanelHeights(true); }, _getPreferredWidth: function(actor, forHeight, alloc) { alloc.min_size = -1; alloc.natural_size = Main.layoutManager.primaryMonitor.width; }, _getPreferredHeight: function(actor, forWidth, alloc) { // We don't need to implement this; it's forced by the CSS alloc.min_size = -1; alloc.natural_size = -1; }, _allocate: function(actor, box, flags) { let allocWidth = box.x2 - box.x1; let allocHeight = box.y2 - box.y1; let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1); let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1); let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1); let centerBoxOccupied = this._centerBox.get_children().length > 0; /* If panel edit mode, pretend central box is occupied and give it at * least width 25 so that things can be dropped into it */ if (this._panelEditMode) { centerBoxOccupied = true; centerMinWidth = Math.max(centerMinWidth, 25); centerNaturalWidth = Math.max(centerNaturalWidth, 25); } let [leftWidth, rightWidth] = calculateAllocation( leftMinWidth , leftNaturalWidth, centerMinWidth, centerNaturalWidth, rightMinWidth , rightNaturalWidth, allocWidth, centerBoxOccupied); let leftBoundary = leftWidth; let rightBoundary = allocWidth - rightWidth; if (this.actor.get_direction() == St.TextDirection.RTL) { leftBoundary = allocWidth - leftWidth; rightBoundary = rightWidth; } let childBox = new Clutter.ActorBox(); childBox.y1 = 0; childBox.y2 = allocHeight; if (this.actor.get_direction() == St.TextDirection.RTL) { childBox.x1 = leftBoundary; childBox.x2 = allocWidth; } else { childBox.x1 = 0; childBox.x2 = leftBoundary; } this._leftBox.allocate(childBox, flags); if (this.actor.get_direction() == St.TextDirection.RTL) { childBox.x1 = rightBoundary; childBox.x2 = leftBoundary; } else { childBox.x1 = leftBoundary; childBox.x2 = rightBoundary; } this._centerBox.allocate(childBox, flags); if (this.actor.get_direction() == St.TextDirection.RTL) { childBox.x1 = 0; childBox.x2 = rightBoundary; } else { childBox.x1 = rightBoundary; childBox.x2 = allocWidth; } this._rightBox.allocate(childBox, flags); let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1); let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1); childBox.x1 = 0; childBox.x2 = cornerWidth; childBox.y1 = allocHeight; childBox.y2 = allocHeight + cornerHeight; this._leftCorner.actor.allocate(childBox, flags); [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1); [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1); childBox.x1 = allocWidth - cornerWidth; childBox.x2 = allocWidth; childBox.y1 = allocHeight; childBox.y2 = allocHeight + cornerHeight; this._rightCorner.actor.allocate(childBox, flags); this._updatePanelBarriers(); }, /** * _updatePanelVisibility: * * Checks whether the panel should show based on the autohide settings and * position of mouse/active window. It then calls the _queueShowHidePanel * function to show or hide the panel as necessary. */ _updatePanelVisibility: function() { // false = autohide, true = always show, intel = Intelligent switch (this._autohideSettings) { case "false": this._shouldShow = true; break; case "true": this._shouldShow = this._mouseEntered; break; default: if (this._mouseEntered || !global.display.focus_window || global.display.focus_window.get_window_type() == Meta.WindowType.DESKTOP) { this._shouldShow = true; break; } /* Calculate the y instead of getting the actor y since the * actor might be hidden*/ let y = this.bottomPosition ? this.monitor.y + this.monitor.height - this.actor.height : this.monitor.y; let a = this.actor; let b = global.display.focus_window.get_compositor_private(); /* Magic to check whether the panel position overlaps with the * current focused window */ this._shouldShow = !(Math.max(a.x, b.x) < Math.min(a.x + a.width, b.x + b.width) && Math.max(y, b.y) < Math.min(y + a.height, b.y + b.height)); } if (this._panelEditMode) this._shouldShow = true; this._queueShowHidePanel(); }, /** * _queueShowHidePanel: * * Makes the panel show or hide after a delay specified by * panels-show-delay and panels-hide-delay. */ _queueShowHidePanel: function() { if (this._showHideTimer) { Mainloop.source_remove(this._showHideTimer); this._showHideTimer = 0; } /* Use a timeout_add even if delay is 0 to avoid "flashing" of panel. * Otherwise, if, say hideDelay is 0 and showDelay is 1000, when you * move over an applet, leave and enter events are fired consecutively. * Then the leave-event causes the panel hides instantly, causing a * further leave-event (since the mouse actually left the panel), which * clears the showPanel timer, and the panel won't show up again. If a * timeout_add is used for showDelay, the hide timeout will be cancelled * by the coming enter-event, and the panel remains open. */ if (this._shouldShow) { let showDelay = this._getProperty(PANEL_SHOW_DELAY_KEY, "i"); this._showHideTimer = Mainloop.timeout_add(showDelay, Lang.bind(this, this._showPanel)) } else { let hideDelay = this._getProperty(PANEL_HIDE_DELAY_KEY, "i"); this._showHideTimer = Mainloop.timeout_add(hideDelay, Lang.bind(this, this._hidePanel)) } }, _enterPanel: function() { this._mouseEntered = true; this._updatePanelVisibility(); }, _leavePanel:function() { this._mouseEntered = false; this._updatePanelVisibility(); }, /** * disable: * * Disables the panel by settings the opacity to 0 and hides if autohide is * enable. The actor is then hidden after the animation. */ disable: function() { this._disabled = true; this._leavePanel(); Tweener.addTween(this.actor, { opacity: 0, time: AUTOHIDE_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this.actor.hide }); }, /** * enable: * * Reverses the effects of the disable function. */ enable: function() { this._disabled = false; this.actor.show(); Tweener.addTween(this.actor, { opacity: 255, time: AUTOHIDE_ANIMATION_TIME, transition: 'easeOutQuad' }); }, /** * _showPanel: * * A function to force the panel to show. This has no effect if the panel * is disabled. */ _showPanel: function() { this._showHideTimer = 0; if (this._disabled) return; if (!this._hidden) return; // Force the panel to be on top (hack to correct issues when switching workspace) Main.layoutManager._windowsRestacked(); let height = this.actor.get_height(); let animationTime = AUTOHIDE_ANIMATION_TIME; let y = this.bottomPosition ? this.monitor.y + this.monitor.height - height : this.monitor.y let params = { y: height - 1, time: animationTime + 0.1, transition: 'easeOutQuad' }; Tweener.addTween(this._leftCorner.actor, params); Tweener.addTween(this._rightCorner.actor, params); this._leftBox.show(); this._centerBox.show(); this._rightBox.show(); Tweener.addTween(this.actor, { y: y, time: animationTime, transition: 'easeOutQuad', onUpdate: Lang.bind(this, function(origY, bottomPosition) { // Force the layout manager to update the input region Main.layoutManager._chrome.updateRegions() let height = Math.abs(this.actor.y - origY); let y = bottomPosition? 0 : this.actor.height - height; this.actor.set_clip(0, y, this.monitor.width, height); }), onUpdateParams: [this.bottomPosition ? this.monitor.y + this.monitor.height : this.monitor.y - height, this.bottomPosition] }); params = { opacity: 255, time: animationTime+0.2, transition: 'easeOutQuad' }; Tweener.addTween(this._leftBox, params); Tweener.addTween(this._centerBox, params); Tweener.addTween(this._rightBox, params); this._hidden = false; }, /** * _hidePanel: * @force (boolean): whether or not to force the hide. * * This hides the panel unless this._shouldShow is false. This behaviour is * overridden if the @force argument is set to true. However, the panel * will always not be hidden if a menu is open, regardless of the value of * @force. */ _hidePanel: function(force) { this._showHideTimer = 0; if ((this._shouldShow && !force) || global.menuStackLength > 0) return; // Force the panel to be on top (hack to correct issues when switching workspace) Main.layoutManager._windowsRestacked(); let height = this.actor.get_height(); let animationTime = AUTOHIDE_ANIMATION_TIME; let y = this.bottomPosition ? this.monitor.y + this.monitor.height - 1 : this.monitor.y - height + 1; Tweener.addTween(this.actor, { y: y, time: animationTime, transition: 'easeOutQuad', onUpdate: Lang.bind(this, function(targetY, bottomPosition) { // Force the layout manager to update the input region Main.layoutManager._chrome.updateRegions() let height = Math.abs(this.actor.y - targetY) + 1; let y = bottomPosition ? 0 : this.actor.height - height; this.actor.set_clip(0, y, this.monitor.width, height); }), onComplete: Lang.bind(this, function() { this._leftBox.hide(); this._centerBox.hide(); this._rightBox.hide(); }), onUpdateParams: [y, this.bottomPosition] }); let params = { y: 0, time: animationTime, transition: 'easeOutQuad' }; Tweener.addTween(this._leftCorner.actor, params); Tweener.addTween(this._rightCorner.actor, params); params = { opacity: 0, time: Math.max(0, animationTime - 0.1), transition: 'easeOutQuad' }; Tweener.addTween(this._leftBox, params); Tweener.addTween(this._centerBox, params); Tweener.addTween(this._rightBox, params); this._hidden = true; }, }; Cinnamon-2.8.6/js/ui/themeManager.js0000664000175000017500000000541512625666430016220 0ustar fabiofabio// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- // Load shell theme from ~/.themes/name/gnome-shell const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Main = imports.ui.main; const Signals = imports.signals; const SETTINGS_SCHEMA = 'org.cinnamon.theme'; const SETTINGS_KEY = 'name'; function ThemeManager() { this._init(); } ThemeManager.prototype = { _init: function() { this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA }); this._changedId = this._settings.connect('changed::'+SETTINGS_KEY, Lang.bind(this, this._changeTheme)); this._changeTheme(); }, _findTheme: function(themeName) { let themeDirectory = null; let path = GLib.build_filenamev([GLib.get_home_dir(), '.themes', themeName, 'cinnamon']); let file = Gio.file_new_for_path(GLib.build_filenamev([path, 'cinnamon.css'])); if (file.query_exists(null)) themeDirectory = path; else { let sysdirs = GLib.get_system_data_dirs(); for (let i = 0; i < sysdirs.length; i++) { path = GLib.build_filenamev([sysdirs[i], 'themes', themeName, 'cinnamon']); let file = Gio.file_new_for_path(GLib.build_filenamev([path, 'cinnamon.css'])); if (file.query_exists(null)) { themeDirectory = path; break; } } } return themeDirectory; }, _changeTheme: function() { let iconTheme = Gtk.IconTheme.get_default(); if (this.themeDirectory) { let searchPath = iconTheme.get_search_path(); for (let i = 0; i < searchPath.length; i++) { if (searchPath[i] == this.themeDirectory) { searchPath.splice(i,1); iconTheme.set_search_path(searchPath); break; } } } let _stylesheet = null; let _themeName = this._settings.get_string(SETTINGS_KEY); if (_themeName) { this.themeDirectory = this._findTheme(_themeName); if (this.themeDirectory) _stylesheet = GLib.build_filenamev([this.themeDirectory, 'cinnamon.css']); } if (_stylesheet) global.log('loading user theme: ' + _stylesheet); else global.log('loading default theme'); Main.setThemeStylesheet(_stylesheet); Main.loadTheme(); if (this.themeDirectory) { iconTheme.append_search_path(this.themeDirectory); global.log('added icon directory: ' + this.themeDirectory); } this.emit('theme-set'); } }; Signals.addSignalMethods(ThemeManager.prototype); Cinnamon-2.8.6/js/ui/workspace.js0000664000175000017500000020331212625666430015615 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Signals = imports.signals; const DND = imports.ui.dnd; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const Overview = imports.ui.overview; const PopupMenu = imports.ui.popupMenu; const Tweener = imports.ui.tweener; const PointerTracker = imports.misc.pointerTracker; const GridNavigator = imports.misc.gridNavigator; const WindowUtils = imports.misc.windowUtils; const FOCUS_ANIMATION_TIME = 0.15; const WINDOW_DND_SIZE = 256; const SCROLL_SCALE_AMOUNT = 50; const LIGHTBOX_FADE_TIME = 0.1; const CLOSE_BUTTON_FADE_TIME = 0.1; const BUTTON_LAYOUT_SCHEMA = 'org.cinnamon.muffin'; const BUTTON_LAYOUT_KEY = 'button-layout'; const DEMANDS_ATTENTION_CLASS_NAME = "window-list-item-demands-attention"; // Define a layout scheme for small window counts. For larger // counts we fall back to an algorithm. We need more schemes here // unless we have a really good algorithm. // Each triplet is [xCenter, yCenter, scale] where the scale // is relative to the width of the workspace. const POSITIONS = { 1: [[0.5, 0.525, 0.875]], 2: [[0.25, 0.525, 0.48], [0.75, 0.525, 0.48]], 3: [[0.25, 0.275, 0.48], [0.75, 0.275, 0.48], [0.5, 0.75, 0.48]], 4: [[0.25, 0.275, 0.47], [0.75, 0.275, 0.47], [0.25, 0.75, 0.47], [0.75, 0.75, 0.47]], 5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]], 6: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.165, 0.75, 0.32], [0.495, 0.75, 0.32], [0.825, 0.75, 0.32]] }; const DEFAULT_SLOT_FRACTION = 0.825; const WINDOWOVERLAY_ICON_SIZE = 32; function _interpolate(start, end, step) { return start + (end - start) * step; } function _clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function ScaledPoint(x, y, scaleX, scaleY) { [this.x, this.y, this.scaleX, this.scaleY] = arguments; } ScaledPoint.prototype = { getPosition : function() { return [this.x, this.y]; }, getScale : function() { return [this.scaleX, this.scaleY]; }, setPosition : function(x, y) { [this.x, this.y] = arguments; }, setScale : function(scaleX, scaleY) { [this.scaleX, this.scaleY] = arguments; }, interpPosition : function(other, step) { return [_interpolate(this.x, other.x, step), _interpolate(this.y, other.y, step)]; }, interpScale : function(other, step) { return [_interpolate(this.scaleX, other.scaleX, step), _interpolate(this.scaleY, other.scaleY, step)]; } }; var menuShowing = null; var menuClone = null; function closeContextMenu(requestor) { let requestorShowingMenu = menuClone && menuClone === requestor; if (menuShowing) { menuShowing.close(); } return requestorShowingMenu; } function WindowClone() { this._init.apply(this, arguments); } WindowClone.prototype = { _init : function(realWindow, myContainer) { this.myContainer = myContainer; this.realWindow = realWindow; this.metaWindow = realWindow.meta_window; this.metaWindow._delegate = this; this.overlay = null; this.origX = realWindow.x; this.origY = realWindow.y; let outerRect = realWindow.meta_window.get_outer_rect(); // The MetaShapedTexture that we clone has a size that includes // the invisible border; this is inconvenient; rather than trying // to compensate all over the place we insert a ClutterGroup into // the hierarchy that is sized to only the visible portion. this.actor = new Clutter.Group({ reactive: true, x: this.origX, y: this.origY, width: outerRect.width, height: outerRect.height }); this.refreshClone(true); // We expect this.actor to be used for all interaction rather than // this.clone; as the former is reactive and the latter // is not, this just works for most cases. However, for DND all // actors are picked, so DND operations would operate on the clone. // To avoid this, we hide it from pick. Cinnamon.util_set_hidden_from_pick(this.clone, true); this.actor._delegate = this; this._stackAbove = null; let sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, this._onRealWindowSizeChanged)); let workspaceChangedId = this.metaWindow.connect('workspace-changed', Lang.bind(this, function(w, oldws) { this.emit('workspace-changed', oldws); })); let realWindowDestroyId = 0; this._disconnectWindowSignals = function() { this._disconnectWindowSignals = function() {}; this.metaWindow.disconnect(workspaceChangedId); this.realWindow.disconnect(sizeChangedId); this.realWindow.disconnect(realWindowDestroyId); }; realWindowDestroyId = this.realWindow.connect('destroy', Lang.bind(this, this._disconnectWindowSignals)); this.myContainer.connect('selection-changed', Lang.bind(this, this._zoomEnd)); this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('scroll-event', Lang.bind(this, this._onScroll)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('leave-event', Lang.bind(this, this._onPointerLeave)); this._windowIsZooming = false; this._zooming = false; this._selected = false; }, refreshClone: function(withTransients) { if (this.clone) {this.clone.destroy();} this.clone = new St.Group({reactive: false}); this.actor.add_actor(this.clone); let [pwidth, pheight] = [this.realWindow.width, this.realWindow.height]; let clones = WindowUtils.createWindowClone(this.metaWindow, 0, 0, withTransients); for (let i in clones) { let clone = clones[i].actor; this.clone.add_actor(clone); let [width, height] = clone.get_size(); clone.set_position(Math.round((pwidth - width) / 2), Math.round((pheight - height) / 2)); } }, setStackAbove: function (actor) { this._stackAbove = actor; if (this._zooming) // We'll fix up the stack after the zooming return; if (this._stackAbove == null) this.actor.lower_bottom(); else this.actor.raise(this._stackAbove); }, destroy: function () { this.actor.destroy(); }, zoomFromOverview: function() { if (this._zooming) { // If the user clicked on the zoomed window, or we are // returning there anyways, then we can zoom right to the // window, but if we are going to some other window, then // we need to cancel the zoom before animating, or it // will look funny. if (!this._selected && this.metaWindow != global.display.focus_window) this._zoomEnd(); } }, _getInvisibleBorderPadding: function() { // We need to adjust the position of the actor because of the // consequences of invisible borders -- in reality, the texture // has an extra set of "padding" around it that we need to trim // down. // The outer rect paradoxically is the smaller rectangle, // containing the positions of the visible frame. The input // rect contains everything, including the invisible border // padding. let outerRect = this.metaWindow.get_outer_rect(); let inputRect = this.metaWindow.get_input_rect(); let [borderX, borderY] = [outerRect.x - inputRect.x, outerRect.y - inputRect.y]; return [borderX, borderY]; }, _onRealWindowSizeChanged: function() { let [borderX, borderY] = this._getInvisibleBorderPadding(); let outerRect = this.metaWindow.get_outer_rect(); this.actor.set_size(outerRect.width, outerRect.height); this.clone.set_position(-borderX, -borderY); this.emit('size-changed'); }, _onDestroy: function() { this._disconnectWindowSignals(); this.metaWindow._delegate = null; this.actor._delegate = null; if (this._zoomLightbox) this._zoomLightbox.destroy(); this.disconnectAll(); }, _onPointerLeave: function (actor, event) { if (this._zoomStep) this._zoomEnd(); }, scrollZoom: function (direction) { if (direction === Clutter.ScrollDirection.UP) { if (this._zoomStep == undefined) this._zoomStart(); if (this._zoomStep < 100) { this._zoomStep += SCROLL_SCALE_AMOUNT; this._zoomUpdate(); } } else if (direction === Clutter.ScrollDirection.DOWN) { if (this._zoomStep > 0) { this._zoomStep -= SCROLL_SCALE_AMOUNT; this._zoomStep = Math.max(0, this._zoomStep); this._zoomUpdate(); } if (this._zoomStep <= 0.0) { this._zoomEnd(); } } else if (direction < 0) { this._zoomEnd(); } }, _onScroll : function (actor, event) { let direction = event.get_scroll_direction(); this.scrollZoom(direction); }, _zoomUpdate : function () { [this.actor.x, this.actor.y] = this._zoomGlobalOrig.interpPosition(this._zoomTarget, this._zoomStep / 100); [this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.interpScale(this._zoomTarget, this._zoomStep / 100); let [width, height] = this.actor.get_transformed_size(); let monitorIndex = this.metaWindow.get_monitor(); let monitor = Main.layoutManager.monitors[monitorIndex]; let availArea = new Meta.Rectangle({ x: monitor.x, y: monitor.y, width: monitor.width, height: monitor.height }); this.actor.x = _clamp(this.actor.x, availArea.x, availArea.x + availArea.width - width); this.actor.y = _clamp(this.actor.y, availArea.y, availArea.y + availArea.height - height); }, _zoomStart : function () { if (!this._zooming) { this.emit('zoom-start'); } this._zooming = true; if (!this._zoomLightbox) this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup, { fadeTime: LIGHTBOX_FADE_TIME }); this._zoomLightbox.show(); this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y); this._zoomGlobalOrig = new ScaledPoint(); let parent = this._origParent = this.actor.get_parent(); let [width, height] = this.actor.get_transformed_size(); this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position()); this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height); global.reparentActor(this.actor, Main.uiGroup); this._zoomLightbox.highlight(this.actor); [this.actor.x, this.actor.y] = this._zoomGlobalOrig.getPosition(); [this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.getScale(); this.actor.raise_top(); this._zoomTarget = new ScaledPoint(0, 0, 1.0, 1.0); this._zoomTarget.setPosition(this.actor.x - (this.actor.width - width) / 2, this.actor.y - (this.actor.height - height) / 2); this._zoomStep = 0; this._zoomUpdate(); }, _zoomEnd : function () { if (!this._zooming) {return;} this._zooming = false; this.emit('zoom-end'); global.reparentActor(this.actor, this._origParent); if (this._stackAbove == null) this.actor.lower_bottom(); // If the workspace has been destroyed while we were reparented to // the stage, _stackAbove will be unparented and we can't raise our // actor above it - as we are bound to be destroyed anyway in that // case, we can skip that step else if (this._stackAbove.get_parent()) this.actor.raise(this._stackAbove); [this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition(); [this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale(); this._zoomLightbox.hide(); this._zoomLocalPosition = undefined; this._zoomLocalScale = undefined; this._zoomGlobalPosition = undefined; this._zoomGlobalScale = undefined; this._zoomTargetPosition = undefined; this._zoomStep = undefined; }, _onButtonPress: function(actor, event) { this.emit('selected', global.get_current_time()); // a button-press on a clone already showing a menu should // not open a new-menu, only close the current menu. this.menuCancelled = closeContextMenu(this); }, _onButtonRelease: function(actor, event) { if ( event.get_button()==1 ) { this._selected = true; this.emit('activated', global.get_current_time()); return true; } if (event.get_button()==2){ this.emit('closed', global.get_current_time()); return true; } if (event.get_button()==3){ if (!this.menuCancelled) { this.emit('context-menu-requested'); } this.menuCancelled = false; return true; } return false; } }; Signals.addSignalMethods(WindowClone.prototype); /** * @windowClone: Corresponding window clone * @parentActor: The actor which will be the parent of all overlay items * such as app icon and window caption */ function WindowOverlay(windowClone, parentActor) { this._init(windowClone, parentActor); } WindowOverlay.prototype = { _init : function(windowClone, parentActor) { let metaWindow = windowClone.metaWindow; this._windowClone = windowClone; this._parentActor = parentActor; this._hidden = false; this._hovering = false; this._isSelected = null; let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_window_app(metaWindow); let icon = null; if (app) { icon = app.create_icon_texture(WINDOWOVERLAY_ICON_SIZE); } if (!icon) { icon = new St.Icon({ icon_name: 'application-default-icon', icon_type: St.IconType.FULLCOLOR, icon_size: WINDOWOVERLAY_ICON_SIZE }); } this.icon = icon; icon.width = WINDOWOVERLAY_ICON_SIZE; icon.height = WINDOWOVERLAY_ICON_SIZE; this._applicationIconBox = new St.Bin({ style_class: 'window-iconbox' }); this._applicationIconBox.set_opacity(255); this._applicationIconBox.add_actor(icon); parentActor.add_actor(this._applicationIconBox); let button = new St.Button({ style_class: 'window-close' }); this.closeButton = button; button._overlap = 0; button.hide(); parentActor.add_actor(button); button.connect('style-changed', Lang.bind(this, this._onStyleChanged)); button.connect('clicked', Lang.bind(this, this.closeWindow)); this.refreshTitle(metaWindow.title); this._updateCaptionId = metaWindow.connect('notify::title', Lang.bind(this, function(w) { this.refreshTitle(w.title); })); this._pointerTracker = new PointerTracker.PointerTracker(); windowClone.actor.connect('motion-event', Lang.bind(this, this._onPointerMotion)); windowClone.actor.connect('leave-event', Lang.bind(this, this._onPointerLeave)); this._idleToggleCloseId = 0; windowClone.actor.connect('destroy', Lang.bind(this, this._onDestroy)); windowClone.connect('zoom-start', Lang.bind(this, this.hide)); windowClone.connect('zoom-end', Lang.bind(this, this.show)); let attentionId = global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention)); let urgentId = global.display.connect('window-marked-urgent', Lang.bind(this, this._onWindowDemandsAttention)); this.disconnectAttentionSignals = function() { global.display.disconnect(attentionId); global.display.disconnect(urgentId); }; // force a style change if we are already on a stage - otherwise // the signal will be emitted normally when we are added if (parentActor.get_stage()) this._onStyleChanged(); }, _onWindowDemandsAttention: function(display, metaWindow) { if (metaWindow != this._windowClone.metaWindow) return; if (!this.title.has_style_class_name(DEMANDS_ATTENTION_CLASS_NAME)) { this.title.add_style_class_name(DEMANDS_ATTENTION_CLASS_NAME); this._applicationIconBox.add_style_class_name(DEMANDS_ATTENTION_CLASS_NAME); } }, refreshTitle: function(titleText) { let name = ''; if (this.title) { name = this.title.name; this._parentActor.remove_actor(this.title); this.title.destroy(); } let title = new St.Label({ style_class: 'window-caption', text: titleText }); this.title = title; title.name = name; title.clutter_text.ellipsize = Pango.EllipsizeMode.END; title._spacing = 0; this._parentActor.add_actor(title); let mw = this._windowClone.metaWindow if (mw.is_urgent && (mw.is_demanding_attention() || mw.is_urgent())) { this.title.add_style_class_name(DEMANDS_ATTENTION_CLASS_NAME); this._applicationIconBox.add_style_class_name(DEMANDS_ATTENTION_CLASS_NAME); } title.connect('style-changed', Lang.bind(this, this._onStyleChanged)); if (this._parentActor.get_stage()) { this._onStyleChanged(); } }, setSelected: function(selected) { if (this._isSelected === selected) { return; } this._isSelected = selected; this.title.name = selected ? 'selected' : ''; this.refreshTitle(this.title.text); if (selected) { this._showCloseButton(); } else { this.hideCloseButton(); } }, hide: function() { this._hidden = true; this.closeButton.hide(); this.title.hide(); this._applicationIconBox.hide(); }, show: function() { this._hidden = false; this._hovering = false; this.title.show(); this._applicationIconBox.show(); }, fadeIn: function() { this.show(); this.title.opacity = 0; this._parentActor.raise_top(); Tweener.addTween(this.title, { opacity: 255, time: CLOSE_BUTTON_FADE_TIME, transition: 'easeOutQuad' }); }, chromeWidth: function () { return this.closeButton.width - this.closeButton._overlap; }, chromeHeights: function () { return [this.closeButton.height - this.closeButton._overlap, this._applicationIconBox.height + this.title._spacing]; }, /** * @cloneX: x position of windowClone * @cloneY: y position of windowClone * @cloneWidth: width of windowClone * @cloneHeight height of windowClone */ // These parameters are not the values retrieved with // get_transformed_position() and get_transformed_size(), // as windowClone might be moving. // See Workspace._showWindowOverlay updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, maxWidth) { let button = this.closeButton; let settings = new Gio.Settings({ schema_id: BUTTON_LAYOUT_SCHEMA }); let layout = settings.get_string(BUTTON_LAYOUT_KEY); let rtl = St.Widget.get_default_direction() == St.TextDirection.RTL; let split = layout.split(":"); let side; if (split[0].indexOf("close") > -1) side = rtl ? St.Side.RIGHT : St.Side.LEFT; else side = rtl ? St.Side.LEFT : St.Side.RIGHT; let buttonX; let buttonY = cloneY - (button.height - button._overlap); if (side == St.Side.LEFT) buttonX = cloneX - (button.width - button._overlap); else buttonX = cloneX + (cloneWidth - button._overlap); button.set_position(Math.floor(buttonX), Math.floor(buttonY)); this.updateIconCaptionWidth = Lang.bind(this, function() { let title = this.title; let iconWidth = this._applicationIconBox.width + title._spacing; title.width = Math.min(maxWidth - iconWidth, title.width); let titleX = cloneX + (iconWidth + cloneWidth - title.width) / 2; let titleY = cloneY + cloneHeight + title._spacing + (WINDOWOVERLAY_ICON_SIZE/2) - (title.height/2); title.set_position(Math.floor(titleX), Math.floor(titleY)); let icon = this._applicationIconBox; let iconX = titleX - iconWidth; let iconY = cloneY + cloneHeight + title._spacing; icon.set_position(Math.floor(iconX), Math.floor(iconY)); }); this.updateIconCaptionWidth(); }, closeWindow: function() { let metaWindow = this._windowClone.metaWindow; let workspace = metaWindow.get_workspace(); if (this._disconnectWindowAdded) {this._disconnectWindowAdded();} let windowAddedId = workspace.connect('window-added',Lang.bind(this, function(ws, win){ if (this._disconnectWindowAdded) {this._disconnectWindowAdded();} if (win.get_transient_for() == metaWindow) { // use an idle handler to avoid mapping problems - // see comment in Workspace._windowAdded Mainloop.idle_add(Lang.bind(this, function() { this._windowClone.emit('activated'); return false; })); } })); this._disconnectWindowAdded = Lang.bind(this, function() { workspace.disconnect(windowAddedId); this._disconnectWindowAdded = 0; }); metaWindow.delete(global.get_current_time()); }, _onDestroy: function() { if (this._disconnectWindowAdded) {this._disconnectWindowAdded();} if (this._idleToggleCloseId > 0) { Mainloop.source_remove(this._idleToggleCloseId); this._idleToggleCloseId = 0; } this.disconnectAttentionSignals(); this._windowClone.metaWindow.disconnect(this._updateCaptionId); this.title.destroy(); this.closeButton.destroy(); this.icon.destroy(); this._applicationIconBox.destroy(); }, _onPointerMotion: function() { if (!this._pointerTracker.hasMoved()) {return;} // We might get motion events on the clone while the overlay is // hidden, e.g. during animations, we ignore these events, // as the close button will be shown as needed when the overlays // are shown again if (this._hidden) return; if (this._hovering) return; this._hovering = true; this._showCloseButton(); }, _showCloseButton: function() { this._parentActor.raise_top(); this.closeButton.show(); this.emit('show-close-button'); }, _onPointerLeave: function() { if (!this._pointerTracker.hasMoved()) {return;} this._hovering = false; this._idleHideCloseButton(); }, _idleHideCloseButton: function() { if (this._idleToggleCloseId == 0) this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton)); }, _idleToggleCloseButton: function() { this._idleToggleCloseId = 0; if (!this._windowClone.actor.has_pointer && !this.closeButton.has_pointer) { this.closeButton.hide(); } return false; }, hideCloseButton: function() { if (this._idleToggleCloseId > 0) { Mainloop.source_remove(this._idleToggleCloseId); this._idleToggleCloseId = 0; } this.closeButton.hide(); }, _onStyleChanged: function() { let titleNode = this.title.get_theme_node(); this.title._spacing = titleNode.get_length('-cinnamon-caption-spacing'); let closeNode = this.closeButton.get_theme_node(); this.closeButton._overlap = closeNode.get_length('-cinnamon-close-overlap'); this._parentActor.queue_relayout(); if (this.updateIconCaptionWidth) { this.updateIconCaptionWidth(); } } }; Signals.addSignalMethods(WindowOverlay.prototype); const WindowPositionFlags = { INITIAL: 1 << 0, ANIMATE: 1 << 1 }; function WorkspaceMonitor() { this._init.apply(this, arguments); } WorkspaceMonitor.prototype = { _init : function(metaWorkspace, monitorIndex, workspace, hasFocus) { this._myWorkspace = workspace; this.metaWorkspace = metaWorkspace; this._x = 0; this._y = 0; this._width = 0; this._height = 0; this._margin = 0; this._slotWidth = 0; this.monitorIndex = monitorIndex; this._monitor = Main.layoutManager.monitors[this.monitorIndex]; this._windowOverlaysGroup = new Clutter.Group(); // Without this the drop area will be overlapped. this._windowOverlaysGroup.set_size(0, 0); this.actor = new Clutter.Group(); this.actor.set_size(0, 0); this._dropRect = new Clutter.Rectangle({ opacity: 0 }); this._dropRect._delegate = this; this.actor.add_actor(this._dropRect); this.actor.add_actor(this._windowOverlaysGroup); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); Main.overview.connect('overview-background-button-press', function() { closeContextMenu(); }); this.stickyCallbackId = workspace.myView.connect('sticky-detected', Lang.bind(this, function(box, metaWindow) { this._doAddWindow(metaWindow); })); let windows = global.get_window_actors().filter(this._isMyWindow, this); // Create clones for windows that should be // visible in the Overview this._windows = []; for (let i = 0; i < windows.length; i++) { if (this._isOverviewWindow(windows[i])) { this._addWindowClone(windows[i]); } } // Track window changes if (this.metaWorkspace) { this._windowAddedId = this.metaWorkspace.connect('window-added', Lang.bind(this, this._windowAdded)); this._windowRemovedId = this.metaWorkspace.connect('window-removed', Lang.bind(this, this._windowRemoved)); } this._windowEnteredMonitorId = global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor)); this._windowLeftMonitorId = global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor)); this._repositionWindowsId = 0; this.leavingOverview = false; this._kbWindowIndex = 0; // index of the current keyboard-selected window if (hasFocus) { this.onInitialPositionWindows = Lang.bind(this, function() { // default-select the first window this.selectAnotherWindow(Clutter.Home); Mainloop.idle_add(Lang.bind(this, function() { // if keyboard focus is at the default position, // make sure that the close button is drawn, // which must done a little bit later if (this._kbWindowIndex === 0) { this.selectAnotherWindow(Clutter.Home); } })); }); } }, selectAnotherWindow: function(symbol) { let numWindows = this._windows.length; if (numWindows === 0) { return false; } let currentIndex = this._kbWindowIndex; let numCols = Math.ceil(Math.sqrt(numWindows)); let nextIndex = GridNavigator.nextIndex(numWindows, numCols, currentIndex, symbol); if (nextIndex < 0) { return false; // not handled } this.selectIndex(nextIndex); return true; }, showActiveSelection: function() { this.selectIndex(this._kbWindowIndex); }, selectIndex: function(index) { this._kbWindowIndex = index; let activeClone = null; if (index > -1 && index < this._windows.length) { activeClone = this._windows[this._kbWindowIndex]; } this._myWorkspace.selectActiveClone(activeClone, this); }, selectClone: function(clone) { this.selectIndex(this._windows.indexOf(clone)); }, _onCloneContextMenuRequested: function(clone) { menuShowing = new WindowContextMenu(clone.actor, clone.metaWindow, Lang.bind(this, function() { menuShowing = null; menuClone = null; this._myWorkspace.emit('focus-refresh-required'); })); menuClone = clone; menuShowing.toggle(); }, showMenuForSelectedWindow: function() { if (this._kbWindowIndex > -1 && this._kbWindowIndex < this._windows.length) { let window = this._windows[this._kbWindowIndex]; this._onCloneContextMenuRequested(window); } return false; }, activateSelectedWindow: function() { if (this._kbWindowIndex > -1 && this._kbWindowIndex < this._windows.length) { this._onCloneActivated(this._windows[this._kbWindowIndex], global.get_current_time()); return true; } return false; }, zoomSelectedWindow: function(direction) { if (this._kbWindowIndex > -1 && this._kbWindowIndex < this._windows.length) { this._windows[this._kbWindowIndex].scrollZoom(direction); } }, closeSelectedWindow: function() { if (this._kbWindowIndex > -1 && this._kbWindowIndex < this._windows.length) { this._windows[this._kbWindowIndex].overlay.closeWindow(); } }, moveSelectedWindowToNextMonitor: function() { if (this._kbWindowIndex > -1 && this._kbWindowIndex < this._windows.length) { let monitorCount = Main.layoutManager.monitors.length; if (monitorCount < 2) return; let nextIndex = (this._windows[this._kbWindowIndex].metaWindow.get_monitor() + monitorCount + 1) % monitorCount; this._windows[this._kbWindowIndex].metaWindow.move_to_monitor(nextIndex); } }, setGeometry: function(x, y, width, height, margin) { this._x = x; this._y = y; this._width = width; this._height = height; this._margin = margin; // This is sometimes called during allocation, so we do this later Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function () { this.positionWindows(WindowPositionFlags.ANIMATE); return false; })); }, _lookupIndex: function (metaWindow) { for (let i = 0; i < this._windows.length; i++) { if (this._windows[i].metaWindow == metaWindow) { return i; } } return -1; }, isEmpty: function() { return this._windows.length === 0; }, /** * _getSlotGeometry: * @slot: A layout slot * * Returns: the screen-relative [x, y, width, height] * of a given window layout slot. */ _getSlotGeometry: function(slot) { let [xCenter, yCenter, fraction] = slot; let width = (this._width - this._margin * 2) * fraction; let height = (this._height - this._margin * 2) * fraction; let x = this._x + this._margin + xCenter * (this._width - this._margin * 2) - width / 2 ; let y = this._y + this._margin + yCenter * (this._height - this._margin * 2) - height / 2; return [x, y, width, height]; }, /** * _computeWindowLayout: * @metaWindow: A #MetaWindow * @slot: A layout slot * * Given a window and slot to fit it in, compute its * screen-relative [x, y, scale] where scale applies * to both X and Y directions. */ _computeWindowLayout: function(metaWindow, slot) { let [x, y, width, height] = this._getSlotGeometry(slot); let rect = metaWindow.get_outer_rect(); let buttonOuterHeight, captionHeight; let buttonOuterWidth = 0; if (this._windows.length) { [buttonOuterHeight, captionIconHeight] = this._windows[0].overlay.chromeHeights(); buttonOuterWidth = this._windows[0].overlay.chromeWidth(); } else { [buttonOuterHeight, captionIconHeight] = [0, 0]; } let scale = Math.min((width - buttonOuterWidth) / rect.width, (height - buttonOuterHeight - captionIconHeight) / rect.height, 1.0); x = Math.floor(x + (width - scale * rect.width) / 2); y = Math.floor(y + ((height - (scale * rect.height)) / 2) - captionIconHeight); return [x, y, scale]; }, /** * positionWindows: * @flags: * INITIAL - this is the initial positioning of the windows. * ANIMATE - Indicates that we need animate changing position. */ positionWindows : function(flags) { if (Main.expo.visible) return; if (this._repositionWindowsId > 0) { Mainloop.source_remove(this._repositionWindowsId); this._repositionWindowsId = 0; } closeContextMenu(); let clones = this._windows.slice(); let initialPositioning = flags & WindowPositionFlags.INITIAL; let animate = flags & WindowPositionFlags.ANIMATE; // Start the animations let slots = this._computeAllWindowSlots(clones.length); //clones = this._orderWindowsByMotionAndStartup(clones, slots); let currentWorkspace = global.screen.get_active_workspace(); let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace; for (let i = 0; i < clones.length; i++) { let slot = slots[i]; let clone = clones[i]; let metaWindow = clone.metaWindow; let mainIndex = this._lookupIndex(metaWindow); let overlay = clone.overlay; let [x, y, scale] = this._computeWindowLayout(metaWindow, slot); if (overlay) overlay.hide(); if (animate && isOnCurrentWorkspace) { if (!metaWindow.showing_on_its_workspace()) { /* Hidden windows should fade in and grow * therefore we need to resize them now so they * can be scaled up later */ if (initialPositioning) { clone.actor.opacity = 0; clone.actor.scale_x = 0; clone.actor.scale_y = 0; clone.actor.x = x; clone.actor.y = y; } // Make the window slightly transparent to indicate it's hidden Tweener.addTween(clone.actor, { opacity: 255, time: Overview.ANIMATION_TIME, transition: 'easeInQuad' }); } Tweener.addTween(clone.actor, { x: x, y: y, scale_x: scale, scale_y: scale, time: Overview.ANIMATION_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this._showWindowOverlay(clone, true); }) }); } else { clone.actor.set_position(x, y); clone.actor.set_scale(scale, scale); this._showWindowOverlay(clone, isOnCurrentWorkspace); } } if (this.onInitialPositionWindows) { this.onInitialPositionWindows(); this.onInitialPositionWindows = null; } }, syncStacking: function(stackIndices) { // Only on the first invocation do we want to affect the // permanent sort order. After that, we don't want major // upheavals to the sort order. let clones = !this._stackedOnce ? this._windows : this._windows.slice(); this._stackedOnce = true; clones.sort(function (a, b) { let minimizedA = a.metaWindow.minimized ? 1 : 0; let minimizedB = b.metaWindow.minimized ? 1 : 0; let minimizedDiff = minimizedA - minimizedB; return minimizedDiff || stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; }); let clones = clones.slice().reverse(); let below = this._dropRect; for (let i = 0; i < clones.length; i++) { let clone = clones[i]; clone.setStackAbove(below); below = clone.actor; } }, _showWindowOverlay: function(clone, fade) { if (this._slotWidth) { // This is a little messy and complicated because when we // start the fade-in we may not have done the final positioning // of the workspaces. (Tweener doesn't necessarily finish // all animations before calling onComplete callbacks.) // So we need to manually compute where the window will // be after the workspace animation finishes. let [cloneX, cloneY] = clone.actor.get_position(); let [cloneWidth, cloneHeight] = clone.actor.get_size(); cloneWidth = clone.actor.scale_x * cloneWidth; cloneHeight = clone.actor.scale_y * cloneHeight; clone.overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, this._slotWidth); if (fade) clone.overlay.fadeIn(); else clone.overlay.show(); } }, _showAllOverlays: function() { let currentWorkspace = global.screen.get_active_workspace(); for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; this._showWindowOverlay(clone, this.metaWorkspace == null || this.metaWorkspace == currentWorkspace); } }, _delayedWindowRepositioning: function() { if (this._windowIsZooming) return true; let [x, y, mask] = global.get_pointer(); let pointerHasMoved = (this._cursorX != x && this._cursorY != y); let inWorkspace = (this._x < x && x < this._x + this._width && this._y < y && y < this._y + this._height); if (pointerHasMoved && inWorkspace) { // store current cursor position this._cursorX = x; this._cursorY = y; return true; } this.positionWindows(WindowPositionFlags.ANIMATE); this._myWorkspace.emit('focus-refresh-required'); this._repositionWindowsId = 0; return false; }, showWindowsOverlays: function() { if (this.leavingOverview) return; this._windowOverlaysGroup.show(); this._showAllOverlays(); }, hideWindowsOverlays: function() { this._windowOverlaysGroup.hide(); }, _doRemoveWindow : function(metaWin) { let win = metaWin.get_compositor_private(); // find the position of the window in our list let index = this._lookupIndex (metaWin); if (index == -1) return; // Check if window still should be here if (win && this._isMyWindow(win)) return; let clone = this._windows[index]; this._windows.splice(index, 1); // If metaWin.get_compositor_private() returned non-NULL, that // means the window still exists (and is just being moved to // another workspace or something), so set its overviewHint // accordingly. (If it returned NULL, then the window is being // destroyed; we'd like to animate this, but it's too late at // this point.) if (win) { let [stageX, stageY] = clone.actor.get_transformed_position(); let [stageWidth, stageHeight] = clone.actor.get_transformed_size(); win._overviewHint = { x: stageX, y: stageY, scale: stageWidth / clone.actor.width }; } if (this._kbWindowIndex >= this._windows.length) { this._kbWindowIndex = this._windows.length - 1; } if (clone === this._myWorkspace._activeClone) { this.selectIndex(this._kbWindowIndex); } clone.destroy(); // We need to reposition the windows; to avoid shuffling windows // around while the user is interacting with the workspace, we delay // the positioning until the pointer remains still for at least 750 ms // or is moved outside the workspace // remove old handler if (this._repositionWindowsId > 0) { Mainloop.source_remove(this._repositionWindowsId); this._repositionWindowsId = 0; } // setup new handler let [x, y, mask] = global.get_pointer(); this._cursorX = x; this._cursorY = y; this._repositionWindowsId = Mainloop.timeout_add(750, Lang.bind(this, this._delayedWindowRepositioning)); }, _doAddWindow : function(metaWin) { if (this.leavingOverview) return; let win = metaWin.get_compositor_private(); if (!win) { // Newly-created windows are added to a workspace before // the compositor finds out about them... Mainloop.idle_add(Lang.bind(this, function () { if (this.actor && metaWin.get_compositor_private() && metaWin.get_workspace() == this.metaWorkspace) this._doAddWindow(metaWin); return false; })); return; } // We might have the window in our list already if it was on all workspaces and // now was moved to this workspace if (this._lookupIndex (metaWin) != -1) { return; } if (!this._isMyWindow(win) || !this._isOverviewWindow(win)){ return; } let clone = this._addWindowClone(win); if (win._overviewHint) { let x = win._overviewHint.x - this.actor.x; let y = win._overviewHint.y - this.actor.y; let scale = win._overviewHint.scale; delete win._overviewHint; clone.actor.set_position (x, y); clone.actor.set_scale (scale, scale); } else { // Position new windows at the top corner of the workspace rather // than where they were placed for real to avoid the window // being clipped to the workspaceView. Its not really more // natural for the window to suddenly appear in the overview // on some seemingly random location anyway. clone.actor.set_position (this._x, this._y); } if (this.actor.get_stage()) { this.positionWindows(WindowPositionFlags.ANIMATE); this._myWorkspace.emit('focus-refresh-required'); } }, _windowAdded : function(metaWorkspace, metaWin) { this._doAddWindow(metaWin); }, _windowRemoved : function(metaWorkspace, metaWin) { this._doRemoveWindow(metaWin); }, _windowEnteredMonitor : function(metaScreen, monitorIndex, metaWin) { if (monitorIndex == this.monitorIndex) { this._doAddWindow(metaWin); } }, _windowLeftMonitor : function(metaScreen, monitorIndex, metaWin) { if (monitorIndex == this.monitorIndex) { this._doRemoveWindow(metaWin); } }, // check for maximized windows on the workspace hasMaximizedWindows: function() { for (let i = 0; i < this._windows.length; i++) { let metaWindow = this._windows[i].metaWindow; if (metaWindow.showing_on_its_workspace() && metaWindow.maximized_horizontally && metaWindow.maximized_vertically) return true; } return false; }, // Animate the full-screen to Overview transition. zoomToOverview : function() { // Position and scale the windows. if (Main.overview.animationInProgress) this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL); else this.positionWindows(WindowPositionFlags.INITIAL); }, // Animates the return from Overview mode zoomFromOverview : function() { let currentWorkspace = global.screen.get_active_workspace(); this.leavingOverview = true; this.hideWindowsOverlays(); if (this._repositionWindowsId > 0) { Mainloop.source_remove(this._repositionWindowsId); this._repositionWindowsId = 0; } this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this, this._doneLeavingOverview)); if (this.metaWorkspace != null && this.metaWorkspace != currentWorkspace) return; // Position and scale the windows. for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; clone.zoomFromOverview(); if (clone.metaWindow.showing_on_its_workspace()) { Tweener.addTween(clone.actor, { x: clone.origX, y: clone.origY, scale_x: 1.0, scale_y: 1.0, time: Overview.ANIMATION_TIME, opacity: 255, transition: 'easeOutQuad' }); } else { // The window is hidden, make it shrink and fade it out Tweener.addTween(clone.actor, { scale_x: 0, scale_y: 0, opacity: 0, time: Overview.ANIMATION_TIME, transition: 'easeOutQuad' }); } } }, destroy : function() { this.actor.destroy(); }, _onDestroy: function(actor) { closeContextMenu(); if (this._overviewHiddenId) { Main.overview.disconnect(this._overviewHiddenId); this._overviewHiddenId = 0; } Tweener.removeTweens(actor); this._myWorkspace.myView.disconnect(this.stickyCallbackId); if (this.metaWorkspace) { this.metaWorkspace.disconnect(this._windowAddedId); this.metaWorkspace.disconnect(this._windowRemovedId); } global.screen.disconnect(this._windowEnteredMonitorId); global.screen.disconnect(this._windowLeftMonitorId); if (this._repositionWindowsId > 0) { Mainloop.source_remove(this._repositionWindowsId); this._repositionWindowsId = 0; } // Usually, the windows will be destroyed automatically with // their parent (this.actor), but we might have a zoomed window // which has been reparented to the stage - _windows[0] holds // the desktop window, which is never reparented for (let w = 0; w < this._windows.length; w++) this._windows[w].destroy(); this._windows = []; }, // Sets this.leavingOverview flag to false. _doneLeavingOverview : function() { this.leavingOverview = false; }, // Tests if @win belongs to this workspace _isMyWindow : function (win) { return (this.metaWorkspace == null || Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index()) && (!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex)); }, // Tests if @win should be shown in the Overview _isOverviewWindow : function (win) { let tracker = Cinnamon.WindowTracker.get_default(); return Main.isInteresting(win.get_meta_window()); }, // Create a clone of a (non-desktop) window and add it to the window list _addWindowClone : function(win) { let clone = new WindowClone(win, this); let overlay = new WindowOverlay(clone, this._windowOverlaysGroup); clone.connect('workspace-changed', Lang.bind(this, function() { this._doRemoveWindow(clone.metaWindow); if (clone.metaWindow.is_on_all_workspaces()) { // Muffin appears not to broadcast when a window turns sticky this._myWorkspace.myView.emit('sticky-detected', clone.metaWindow); } })); clone.connect('selected', Lang.bind(this, this._onCloneSelected)); clone.connect('activated', Lang.bind(this, this._onCloneActivated)); clone.connect('closed', Lang.bind(this, this._onCloneClosed)); clone.connect('context-menu-requested', Lang.bind(this, this._onCloneContextMenuRequested)); clone.connect('zoom-start', Lang.bind(this, function(clone) { this.selectClone(clone); this._windowIsZooming = true; })); clone.connect('zoom-end', Lang.bind(this, function() { this._windowIsZooming = false; })); clone.connect('size-changed', Lang.bind(this, function() { this.positionWindows(0); })); this.actor.add_actor(clone.actor); overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose)); this._windows.push(clone); clone.overlay = overlay; return clone; }, _onShowOverlayClose: function (windowOverlay) { for (let i = 0; i < this._windows.length; i++) { let overlay = this._windows[i].overlay; if (overlay == windowOverlay) continue; overlay.hideCloseButton(); } }, _computeAllWindowSlots: function(numberOfWindows) { if (!numberOfWindows) return []; let gridWidth = Math.ceil(Math.sqrt(numberOfWindows)); let gridHeight = Math.ceil(numberOfWindows / gridWidth); let fraction = DEFAULT_SLOT_FRACTION * (1. / gridWidth); this._slotWidth = Math.floor(fraction * this._width); let computeWindowSlot = function(windowIndex, numberOfWindows) { if (numberOfWindows in POSITIONS) return POSITIONS[numberOfWindows][windowIndex]; // If we don't have a predefined scheme for this window count, // arrange the windows in a grid pattern. let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth; let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight; return [xCenter, yCenter, fraction]; }; let slots = []; for (let i = 0; i < numberOfWindows; i++) { slots.push(computeWindowSlot(i, numberOfWindows)); } return slots; }, _onCloneSelected : function (clone, time) { this.selectClone(clone); }, _onCloneActivated : function (clone, time) { let wsIndex = undefined; if (this.metaWorkspace) wsIndex = this.metaWorkspace.index(); Main.activateWindow(clone.metaWindow, time, wsIndex); }, _onCloneClosed : function (clone, time) { clone.metaWindow.delete(global.get_current_time()); } }; Signals.addSignalMethods(WorkspaceMonitor.prototype); function WindowContextMenu(actor, metaWindow, onClose) { this._init(actor, metaWindow, onClose); } WindowContextMenu.prototype = { __proto__: PopupMenu.PopupComboMenu.prototype, _init: function(actor, metaWindow, onClose) { PopupMenu.PopupComboMenu.prototype._init.call(this, actor); this.name = 'scale-window-context-menu'; Main.uiGroup.add_actor(this.actor); this.actor.hide(); let orientation = St.Side.TOP; this.onClose = onClose; actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.connect('open-state-changed', Lang.bind(this, this._onToggled)); this.metaWindow = metaWindow; this.itemCloseWindow = new PopupMenu.PopupMenuItem(_("Close")); this.itemCloseWindow.connect('activate', Lang.bind(this, this._onCloseWindowActivate)); if (metaWindow.minimized) this.itemMinimizeWindow = new PopupMenu.PopupMenuItem(_("Restore")); else this.itemMinimizeWindow = new PopupMenu.PopupMenuItem(_("Minimize")); this.itemMinimizeWindow.connect('activate', Lang.bind(this, this._onMinimizeWindowActivate)); this.itemMaximizeWindow = new PopupMenu.PopupMenuItem(_("Maximize")); this.itemMaximizeWindow.connect('activate', Lang.bind(this, this._onMaximizeWindowActivate)); this.itemMoveToLeftWorkspace = new PopupMenu.PopupMenuItem(_("Move to left workspace")); this.itemMoveToLeftWorkspace.connect('activate', Lang.bind(this, this._onMoveToLeftWorkspace)); this.itemMoveToRightWorkspace = new PopupMenu.PopupMenuItem(_("Move to right workspace")); this.itemMoveToRightWorkspace.connect('activate', Lang.bind(this, this._onMoveToRightWorkspace)); this.itemOnAllWorkspaces = new PopupMenu.PopupMenuItem(_("Visible on all workspaces")); this.itemOnAllWorkspaces.connect('activate', Lang.bind(this, this._toggleOnAllWorkspaces)); let itemMoveToNewWorkspace = new PopupMenu.PopupMenuItem(_("Move to a new workspace")); itemMoveToNewWorkspace.connect('activate', Lang.bind(this, function() { Main.moveWindowToNewWorkspace(metaWindow, true); })); let monitorItems = []; if (Main.layoutManager.monitors.length > 1) { Main.layoutManager.monitors.forEach(function(monitor, index) { if (index !== metaWindow.get_monitor()) { let itemChangeMonitor = new PopupMenu.PopupMenuItem( _("Move to monitor %d").format(index + 1)); itemChangeMonitor.connect('activate', Lang.bind(this, function() { metaWindow.move_to_monitor(index); })); monitorItems.push(itemChangeMonitor); } }, this); monitorItems.push(new PopupMenu.PopupSeparatorMenuItem()); } let items = monitorItems.concat([ itemMoveToNewWorkspace, this.itemOnAllWorkspaces, this.itemMoveToLeftWorkspace, this.itemMoveToRightWorkspace, new PopupMenu.PopupSeparatorMenuItem(), this.itemMinimizeWindow, this.itemMaximizeWindow, this.itemCloseWindow ]); (orientation == St.Side.BOTTOM ? items : items.reverse()).forEach(function(item) { this.addMenuItem(item); }, this); this.setActiveItem(0); }, _onToggled: function(actor, opening){ if (!opening) { this.onClose(); this.destroy(); return; } if (this.metaWindow.is_on_all_workspaces()) { this.itemOnAllWorkspaces.label.set_text(_("Only on this workspace")); } else { this.itemOnAllWorkspaces.label.set_text(_("Visible on all workspaces")); } if (this.metaWindow.get_workspace().get_neighbor(Meta.MotionDirection.LEFT) != this.metaWindow.get_workspace()) this.itemMoveToLeftWorkspace.actor.show(); else this.itemMoveToLeftWorkspace.actor.hide(); if (this.metaWindow.get_workspace().get_neighbor(Meta.MotionDirection.RIGHT) != this.metaWindow.get_workspace()) this.itemMoveToRightWorkspace.actor.show(); else this.itemMoveToRightWorkspace.actor.hide(); if (this.metaWindow.get_maximized()) { this.itemMaximizeWindow.label.set_text(_("Unmaximize")); }else{ this.itemMaximizeWindow.label.set_text(_("Maximize")); } }, _onCloseWindowActivate: function(actor, event){ this.metaWindow.delete(global.get_current_time()); }, _onMinimizeWindowActivate: function(actor, event){ if (this.metaWindow.minimized) { this.metaWindow.unminimize(global.get_current_time()); } else { this.metaWindow.minimize(global.get_current_time()); } }, _onMaximizeWindowActivate: function(actor, event){ if (this.metaWindow.get_maximized()){ this.metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); }else{ this.metaWindow.maximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); } }, _onMoveToLeftWorkspace: function(actor, event){ let workspace = this.metaWindow.get_workspace().get_neighbor(Meta.MotionDirection.LEFT); if (workspace) { this.metaWindow.change_workspace(workspace); } }, _onMoveToRightWorkspace: function(actor, event){ let workspace = this.metaWindow.get_workspace().get_neighbor(Meta.MotionDirection.RIGHT); if (workspace) { this.metaWindow.change_workspace(workspace); } }, _toggleOnAllWorkspaces: function(actor, event) { if (this.metaWindow.is_on_all_workspaces()) this.metaWindow.unstick(); else this.metaWindow.stick(); }, _onSourceKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { this.menu.toggle(); return true; } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { this.menu.close(); return true; } else if (symbol == Clutter.KEY_Down) { if (!this.menu.isOpen) this.menu.toggle(); this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); return true; } else return false; } }; function Workspace() { this._init.apply(this, arguments); } Workspace.prototype = { _init : function(metaWorkspace, view) { this.metaWorkspace = metaWorkspace; this.myView = view; this.actor = new Clutter.Group(); this.actor.set_size(0, 0); this._monitors = []; this._activeClone = null; this.currentMonitorIndex = Main.layoutManager.primaryIndex; Main.layoutManager.monitors.forEach(function(monitor, ix) { let m = new WorkspaceMonitor(metaWorkspace, ix, this, ix === this.currentMonitorIndex) m.setGeometry(monitor.x, monitor.y, monitor.width, monitor.height, 0); this._monitors.push(m); this.actor.add_actor(m.actor); }, this); this.connect('focus-refresh-required', Lang.bind(this, function() { this.selectNextNonEmptyMonitor(this.currentMonitorIndex - 1, 1); })); }, findNextNonEmptyMonitor: function(start, increment) { let pos = start; for (let i = 0; i < this._monitors.length; ++i) { pos = (this._monitors.length + pos + increment) % this._monitors.length; if (!this._monitors[pos].isEmpty()) { return pos; } } return this.currentMonitorIndex || 0; }, selectNextNonEmptyMonitor: function(start, increment) { this.selectMonitor(this.findNextNonEmptyMonitor(start || 0, increment)); }, selectMonitor: function(index) { this.currentMonitorIndex = index; this._monitors[this.currentMonitorIndex].showActiveSelection(); }, selectActiveClone: function(clone, wsMonitor) { let current = this._activeClone; if (clone) { this.currentMonitorIndex = wsMonitor.monitorIndex; } this._activeClone = clone; if (current !== this._activeClone) { if (current) { current.overlay.setSelected(false); } if (this._activeClone) { this._activeClone.overlay.setSelected(true); } wsMonitor.emit('selection-changed'); } }, _onKeyPress: function(actor, event) { let modifiers = Cinnamon.get_event_state(event); let symbol = event.get_key_symbol(); let ctrlAltMask = Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.MOD1_MASK; if ((symbol === Clutter.ISO_Left_Tab || symbol === Clutter.Tab) && !(modifiers & ctrlAltMask)) { let increment = symbol === Clutter.ISO_Left_Tab ? -1 : 1; this.selectNextNonEmptyMonitor(this.currentMonitorIndex, increment); return true; } let activeMonitor = this._monitors[this.currentMonitorIndex]; if ((symbol === Clutter.m || symbol === Clutter.M || symbol === Clutter.KEY_space) && (modifiers & Clutter.ModifierType.MOD1_MASK) && !(modifiers & Clutter.ModifierType.CONTROL_MASK)) { activeMonitor.showMenuForSelectedWindow(); return true; } if (symbol === Clutter.w && modifiers & Clutter.ModifierType.CONTROL_MASK) { activeMonitor.closeSelectedWindow(); return true; } if ((symbol === Clutter.m || symbol === Clutter.M) && modifiers & Clutter.ModifierType.CONTROL_MASK) { activeMonitor.moveSelectedWindowToNextMonitor(); return true; } if (symbol === Clutter.Return || symbol === Clutter.KEY_space || symbol === Clutter.KP_Enter) { if (activeMonitor.activateSelectedWindow()) { return true; } Main.overview.hide(); return true; } if (symbol === Clutter.plus && modifiers & Clutter.ModifierType.CONTROL_MASK) { activeMonitor.zoomSelectedWindow(Clutter.ScrollDirection.UP); return true; } if (symbol === Clutter.minus && modifiers & Clutter.ModifierType.CONTROL_MASK) { activeMonitor.zoomSelectedWindow(Clutter.ScrollDirection.DOWN); return true; } if (symbol - '48' === 0 && modifiers & Clutter.ModifierType.CONTROL_MASK) { activeMonitor.zoomSelectedWindow(-1); // end zoom return true; } if (modifiers & ctrlAltMask) { return false; } return activeMonitor.selectAnotherWindow(symbol); }, destroy: function() { this._monitors.forEach(function(monitor) { monitor.destroy(); }, this); this.actor.destroy(); }, selectAnotherWindow: function(symbol) { this._monitors[this.currentMonitorIndex].selectAnotherWindow(symbol); }, zoomFromOverview: function() { this._monitors.forEach(function(monitor) { monitor.zoomFromOverview(); }, this); }, zoomToOverview: function() { this._monitors.forEach(function(monitor) { monitor.zoomToOverview(); }, this); this.emit('focus-refresh-required'); }, hasMaximizedWindows: function() { let has = false; this._monitors.forEach(function(monitor) { has = has || monitor.hasMaximizedWindows(); }, this); return has; }, isEmpty: function() { let hasWindows = false; this._monitors.forEach(function(monitor) { hasWindows = hasWindows || !monitor.isEmpty(); }, this); return !hasWindows; }, showWindowsOverlays: function() { this._monitors.forEach(function(monitor) { monitor.showWindowsOverlays(); }, this); }, hideWindowsOverlays: function() { this._monitors.forEach(function(monitor) { monitor.hideWindowsOverlays(); }, this); }, syncStacking: function(arg1) { this._monitors.forEach(function(monitor) { monitor.syncStacking(arg1); }, this); } }; Signals.addSignalMethods(Workspace.prototype); Cinnamon-2.8.6/js/ui/tooltips.js0000664000175000017500000002302512625666430015475 0ustar fabiofabioconst Lang = imports.lang; const Mainloop = imports.mainloop; const St = imports.gi.St; const Applet = imports.ui.applet; const Main = imports.ui.main; const SignalManager = imports.misc.signalManager; const Tweener = imports.ui.tweener; /** * #TooltipBase * @item (Clutter.Actor): The object owning the tooltip. * @visible (boolean): Whether the tooltip is currently visible * @preventShow (boolean): Whether to inhibit the display of the tooltip * @mousePosition (array): The coordinates of the event that triggered the * show. * * This is a base class for other tooltip items to inherit. This cannot be * instantiated. * * All other tooltip items inherit this object. This base class is responsible * for listening to mouse events and determining when to show the tooltip. When * it thinks a tooltip should be shown, it calls `this.show()`. When it thinks * it should be hidden, it calls `this.hide()`. When the @item is destroyed, it * will call `this._destroy()`; * * Any object wishing to implement a tooltip should inherit this class, and * then implement the three functions above. It should be noted that the sole * responsibility of this class is to call the three functions above. It is * thus the user's job to create the tooltip actor and position it correctly in * the `show` function. Example implementations for reference include the * #Tooltips.Tooltip object as well as the `WindowPreview` object in the window * list applet. * * When calling the `show` function, #TooltipBase will set the * `this.mousePosition` to the mouse coordinates at which the event is * triggered. * * When implementing the `show` and `hide` functions, the user should set the * `this.visible` variable to the visibility state of the tooltip. This is * since calling the `show` function does not necessarily actually show the * tooltip, eg. when the tooltip text is empty and the tooltip refuses to show. * The `this.visible` variable should be set properly to reflect the actual * status of the tooltip. * * Finally, if the user wishes to inhibit the display of a tooltip, eg. when * the owner is being dragged, they can set the `this.preventShow` variable to * `true`. */ function TooltipBase(item) { throw new TypeError("Trying to instantiate abstract class TooltipBase"); } TooltipBase.prototype = { _init: function(item) { this.signals = new SignalManager.SignalManager(this); this.signals.connect(item, 'enter-event', this._onEnterEvent); this.signals.connect(item, 'motion-event', this._onMotionEvent); this.signals.connect(item, 'leave-event', this._hide); this.signals.connect(item, 'button-press-event', this._hide); this.signals.connect(item, 'button-release-event', this._hide); this.signals.connect(item, 'destroy', this.destroy); this.signals.connect(item, 'allocation-changed', function() { // An allocation change could mean that the actor has moved, // so hide, but wait until after the allocation cycle. Mainloop.idle_add(Lang.bind(this, function() { this.hide(); })); }); this._showTimer = null; this.visible = false; this.item = item; this.preventShow = false; }, _onMotionEvent: function(actor, event) { if (this._showTimer) { Mainloop.source_remove(this._showTimer); this._showTimer = null; } if (!this.visible) { this._showTimer = Mainloop.timeout_add(300, Lang.bind(this, this._onTimerComplete)); this.mousePosition = event.get_coords(); } }, _onEnterEvent: function(actor, event) { if (!this._showTimer) { this._showTimer = Mainloop.timeout_add(300, Lang.bind(this, this._onTimerComplete)); this.mousePosition = event.get_coords(); } }, _onTimerComplete: function(){ this._showTimer = null; if (!this.preventShow) this.show(); return false; }, _hide: function(actor, event) { if (this._showTimer) { Mainloop.source_remove(this._showTimer); this._showTimer = null; } this.hide(); }, /** * destroy: * * Destroys the tooltip. */ destroy: function() { if (this._showTimer) { Mainloop.source_remove(this._showTimer); this._showTimer = null; } this.signals.disconnectAllSignals(); this._destroy(); } } /** * #Tooltip: * * This is a tooltip item that displays some text. The tooltip will be * displayed such that the top left corner of the label is at the mouse * position. * * This is not suitable for use in applets, since in the case of applets, we * don't want the tooltip at the position of the mouse. Instead, it should * appear above/below the panel without overlapping with the applet. Hence the * #PanelItemTooltip class should be used instead. * * Note that the tooltip refuses to show if the tooltip text is empty. * * Inherits: Tooltips.TooltipBase */ function Tooltip(item, initTitle) { this._init(item, initTitle); } Tooltip.prototype = { __proto__: TooltipBase.prototype, /** * _init: * @item (Clutter.Actor): the actor owning the tooltip * @initTitle (string): the string to display initially */ _init: function(item, initTitle) { TooltipBase.prototype._init.call(this, item); this._tooltip = new St.Label({ name: 'Tooltip' }); this._tooltip.show_on_set_parent = false; if (initTitle) this._tooltip.set_text(initTitle); Main.uiGroup.add_actor(this._tooltip); }, hide: function() { this._tooltip.hide(); this.visible = false; }, show: function() { if (this._tooltip.get_text() == "") return; let tooltipWidth = this._tooltip.get_allocation_box().x2-this._tooltip.get_allocation_box().x1; let monitor = Main.layoutManager.findMonitorForActor(this.item); let tooltipTop = this.mousePosition[1]; var tooltipLeft = this.mousePosition[0]; tooltipLeft = Math.max(tooltipLeft, monitor.x); tooltipLeft = Math.min(tooltipLeft, monitor.x + monitor.width - tooltipWidth); this._tooltip.set_position(tooltipLeft, tooltipTop); this._tooltip.show(); this._tooltip.raise_top(); this.visible = true; }, /** * set_text: * @text (string): new text to display * * Sets the text to display to @text. */ set_text: function(text) { this._tooltip.set_text(text); }, _destroy: function() { this._tooltip.destroy(); } }; /** * #PanelItemTooltip * @_panelItem (Applet.Applet): The applet owning the tooltip * @orientation (St.Side): The orientation of the applet * * A tooltip for panel applets. This is displayed above/below the panel instead * of at exactly the mouse position to avoid covering the applet. * * It is possible that @panelItem is not an applet, but a child of an applet. * An immediate example is for use in the window list, where each individual * item, instead of the applet, has its own tooltip. These objects must have * `panelItem._applet` set as the actual applet, since we need to access the * applet to listen to orientation changes. * * Inherits: Tooltips.Tooltip */ function PanelItemTooltip(panelItem, initTitle, orientation) { this._init(panelItem, initTitle, orientation); } PanelItemTooltip.prototype = { __proto__: Tooltip.prototype, /** * _init: * @panelItem (Applet.Applet): the applet owning the tooltip * @initTitle (string): the initial string of the tooltip * @orientation (St.Side): the orientation of the applet. * * It should be noted that @panelItem is the *applet* owning the tooltip, * while that usually passed to #Tooltips.Tooltip is the *actor*. These are * different objects. */ _init: function(panelItem, initTitle, orientation) { Tooltip.prototype._init.call(this, panelItem.actor, initTitle); this._panelItem = panelItem; this.orientation = orientation; if (this._panelItem instanceof Applet.Applet) { this._panelItem.connect("orientation-changed", Lang.bind(this, this._onOrientationChanged)); } else if (this._panelItem._applet) { this._panelItem._applet.connect("orientation-changed", Lang.bind(this, this._onOrientationChanged)); } }, show: function() { if (this._tooltip.get_text() == "" || global.menuStackLength > 0) return; let tooltipHeight = this._tooltip.get_allocation_box().y2-this._tooltip.get_allocation_box().y1; let tooltipWidth = this._tooltip.get_allocation_box().x2-this._tooltip.get_allocation_box().x1; let monitor = Main.layoutManager.findMonitorForActor(this.item); let tooltipTop; if (this.orientation == St.Side.BOTTOM) { tooltipTop = this.item.get_transformed_position()[1] - tooltipHeight; } else { tooltipTop = this.item.get_transformed_position()[1] + this.item.get_transformed_size()[1]; } let tooltipLeft = this.mousePosition[0]- Math.round(tooltipWidth/2); tooltipLeft = Math.max(tooltipLeft, monitor.x); tooltipLeft = Math.min(tooltipLeft, monitor.x + monitor.width - tooltipWidth); this._tooltip.set_position(tooltipLeft, tooltipTop); this._tooltip.show(); this.visible = true; }, _onOrientationChanged: function(a, orientation) { this.orientation = orientation; } }; Cinnamon-2.8.6/js/ui/keybindings.js0000664000175000017500000001306612625666430016132 0ustar fabiofabioconst Signals = imports.signals; const Main = imports.ui.main; const Gio = imports.gi.Gio; const Lang = imports.lang; const Util = imports.misc.util; const Meta = imports.gi.Meta; const MK = imports.gi.CDesktopEnums.MediaKeyType; const CinnamonDesktop = imports.gi.CinnamonDesktop; const CSD_DBUS_NAME = "org.cinnamon.SettingsDaemon"; const CSD_DBUS_PATH = "/org/cinnamon/SettingsDaemon/KeybindingHandler"; const CUSTOM_KEYS_PARENT_SCHEMA = "org.cinnamon.desktop.keybindings"; const CUSTOM_KEYS_BASENAME = "/org/cinnamon/desktop/keybindings/custom-keybindings"; const CUSTOM_KEYS_SCHEMA = "org.cinnamon.desktop.keybindings.custom-keybinding"; const MEDIA_KEYS_SCHEMA = "org.cinnamon.desktop.keybindings.media-keys"; const iface = "\ \ \ \ \ \ \ \ "; const proxy = Gio.DBusProxy.makeProxyWrapper(iface); function KeybindingManager() { this._init(); } KeybindingManager.prototype = { _init: function() { this._proxy = new proxy(Gio.DBus.session, 'org.cinnamon.SettingsDaemon', '/org/cinnamon/SettingsDaemon/KeybindingHandler'); this.bindings = []; this.kb_schema = Gio.Settings.new(CUSTOM_KEYS_PARENT_SCHEMA); this.setup_custom_keybindings(); this.kb_schema.connect("changed::custom-list", Lang.bind(this, this.on_customs_changed)); this.media_key_settings = new Gio.Settings({ schema_id: MEDIA_KEYS_SCHEMA }); this.media_key_settings.connect("changed", Lang.bind(this, this.setup_media_keys)); this.setup_media_keys(); }, on_customs_changed: function(settings, key) { this.remove_custom_keybindings(); this.setup_custom_keybindings(); }, addHotKey: function(name, bindings_string, callback) { if (!bindings_string) return false; return this.addHotKeyArray(name, bindings_string.split("::"), callback); }, addHotKeyArray: function(name, bindings, callback) { if (this.bindings[name]) { if (this.bindings[name].toString() == bindings.toString()) { return true; } global.display.remove_custom_keybinding(name); } if (!bindings) { global.logError("Missing bindings array for keybinding: " + name); return false; } let empty = true; for (let i = 0; empty && (i < bindings.length); i++) { empty = bindings[i].toString().trim() == ""; } if (empty) { if (this.bindings[name]) this.bindings[name] = undefined; global.display.rebuild_keybindings(); return true; } if (!global.display.add_custom_keybinding(name, bindings, callback)) { global.logError("Warning, unable to bind hotkey with name '" + name + "'. The selected keybinding could already be in use."); global.display.rebuild_keybindings(); return false; } else { this.bindings[name] = bindings; } global.display.rebuild_keybindings(); return true; }, removeHotKey: function(name) { if (this.bindings[name] == undefined) return; global.display.remove_custom_keybinding(name); global.display.rebuild_keybindings(); this.bindings[name] = undefined; }, setup_custom_keybindings: function() { let list = this.kb_schema.get_strv("custom-list"); for (let i = 0; i < list.length; i++) { let custom_path = CUSTOM_KEYS_BASENAME + "/" + list[i] + "/"; let schema = Gio.Settings.new_with_path(CUSTOM_KEYS_SCHEMA, custom_path); let command = schema.get_string("command"); let binding = schema.get_strv("binding"); let name = list[i]; this.addHotKeyArray(name, binding, Lang.bind(this, function() { Util.spawnCommandLine(command); })) } }, remove_custom_keybindings: function() { for (let i in this.bindings) { if (i.indexOf("custom") > -1) { this.removeHotKey(i); } } }, setup_media_keys: function() { for (let i = 0; i < MK.SEPARATOR; i++) { let bindings = this.media_key_settings.get_strv(CinnamonDesktop.desktop_get_media_key_string(i)); this.addHotKeyArray("media-keys-" + i.toString(), bindings, Lang.bind(this, this.on_global_media_key_pressed, i)); } for (let i = MK.SEPARATOR + 1; i < MK.LAST; i++) { let bindings = this.media_key_settings.get_strv(CinnamonDesktop.desktop_get_media_key_string(i)); this.addHotKeyArray("media-keys-" + i.toString(), bindings, Lang.bind(this, this.on_media_key_pressed, i)); } return true; }, on_global_media_key_pressed: function(display, screen, event, kb, action) { this._proxy.HandleKeybindingRemote(action); }, on_media_key_pressed: function(display, screen, event, kb, action) { if (Main.modalCount == 0 && !Main.overview.visible && !Main.expo.visible) this._proxy.HandleKeybindingRemote(action); } }; Signals.addSignalMethods(KeybindingManager.prototype); Cinnamon-2.8.6/js/ui/panelMenu.js0000664000175000017500000001442612625666430015551 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const St = imports.gi.St; const Main = imports.ui.main; const Params = imports.misc.params; const PopupMenu = imports.ui.popupMenu; function ButtonBox(params) { this._init.apply(this, arguments); }; ButtonBox.prototype = { _init: function(params) { params = Params.parse(params, { style_class: 'panel-button' }, true); this.actor = new Cinnamon.GenericContainer(params); this.actor._delegate = this; this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); this._minHPadding = this._natHPadding = 0.0; }, _onStyleChanged: function(actor) { let themeNode = actor.get_theme_node(); this._minHPadding = themeNode.get_length('-minimum-hpadding'); this._natHPadding = themeNode.get_length('-natural-hpadding'); }, _getPreferredWidth: function(actor, forHeight, alloc) { let children = actor.get_children(); let child = children.length > 0 ? children[0] : null; if (child) { [alloc.min_size, alloc.natural_size] = child.get_preferred_width(-1); } else { alloc.min_size = alloc.natural_size = 0; } alloc.min_size += 2 * this._minHPadding; alloc.natural_size += 2 * this._natHPadding; }, _getPreferredHeight: function(actor, forWidth, alloc) { let children = actor.get_children(); let child = children.length > 0 ? children[0] : null; if (child) { [alloc.min_size, alloc.natural_size] = child.get_preferred_height(-1); } else { alloc.min_size = alloc.natural_size = 0; } }, _allocate: function(actor, box, flags) { let children = actor.get_children(); if (children.length == 0) return; let child = children[0]; let [minWidth, natWidth] = child.get_preferred_width(-1); let [minHeight, natHeight] = child.get_preferred_height(-1); let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let childBox = new Clutter.ActorBox(); if (natWidth + 2 * this._natHPadding <= availWidth) { childBox.x1 = this._natHPadding; childBox.x2 = availWidth - this._natHPadding; } else { childBox.x1 = this._minHPadding; childBox.x2 = availWidth - this._minHPadding; } if (natHeight <= availHeight) { childBox.y1 = Math.floor((availHeight - natHeight) / 2); childBox.y2 = childBox.y1 + natHeight; } else { childBox.y1 = 0; childBox.y2 = availHeight; } child.allocate(childBox, flags); }, } function Button(menuAlignment) { this._init(menuAlignment); } Button.prototype = { __proto__: ButtonBox.prototype, _init: function(menuAlignment) { ButtonBox.prototype._init.call(this, { reactive: true, can_focus: true, track_hover: true }); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, Main.applet_side); this.menu.actor.add_style_class_name('panel-menu'); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress)); Main.uiGroup.add_actor(this.menu.actor); this.menu.actor.hide(); }, _onButtonPress: function(actor, event) { if (!this.menu.isOpen) { // Setting the max-height won't do any good if the minimum height of the // menu is higher then the screen; it's useful if part of the menu is // scrollable so the minimum height is smaller than the natural height let monitor = Main.layoutManager.findMonitorForActor(this.launcher.actor); this.menu.actor.style = ('max-height: ' + Math.round(monitor.height - Main.panel.actor.height) + 'px;'); } this.menu.toggle(); }, _onSourceKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { this.menu.toggle(); return true; } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { this.menu.close(); return true; } else if (symbol == Clutter.KEY_Down) { if (!this.menu.isOpen) this.menu.toggle(); this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); return true; } else return false; }, _onMenuKeyPress: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { let focusManager = St.FocusManager.get_for_stage(global.stage); let group = focusManager.get_group(this.actor); if (group) { let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT; group.navigate_focus(this.actor, direction, false); return true; } } return false; }, _onOpenStateChanged: function(menu, open) { if (open) this.actor.add_style_pseudo_class('active'); else this.actor.remove_style_pseudo_class('active'); }, destroy: function() { this.actor._delegate = null; this.menu.destroy(); this.actor.destroy(); this.emit('destroy'); } }; Signals.addSignalMethods(Button.prototype); Cinnamon-2.8.6/js/ui/osdWindow.js0000664000175000017500000001461012625666430015575 0ustar fabiofabioconst St = imports.gi.St; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Main = imports.ui.main; const Mainloop = imports.mainloop; const Tweener = imports.ui.tweener; const Gio = imports.gi.Gio; const Meta = imports.gi.Meta; const LEVEL_ANIMATION_TIME = 0.1; const FADE_TIME = 0.1; const HIDE_TIMEOUT = 1500; const OSD_SIZE = 110; function LevelBar() { this._init(); } LevelBar.prototype = { _init: function() { this._level = 0; this.actor = new St.Bin({ style_class: 'level', x_fill: true, y_fill: true, important: true }); this._bar = new St.DrawingArea(); this._bar.connect('repaint', Lang.bind(this, this._repaint)); this.actor.set_child(this._bar); }, get level() { return this._level; }, set level(value) { let newValue = Math.max(0, Math.min(value, 100)); if (newValue == this._level) return; this._level = newValue; this._bar.queue_repaint(); }, setLevelBarHeight: function(sizeMultiplier) { let themeNode = this.actor.get_theme_node(); let height = themeNode.get_height(); let newHeight = Math.floor(height * sizeMultiplier); this.actor.set_height(newHeight); }, _repaint:function() { let cr = this._bar.get_context(); let node = this.actor.get_theme_node(); let radius = node.get_border_radius(0); Clutter.cairo_set_source_color(cr, node.get_foreground_color()); let [w, h] = this._bar.get_surface_size(); w *= (this._level / 100.0); if (w == 0) return; cr.moveTo(radius, 0); if (w >= radius) cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2.0 * Math.PI); else cr.lineTo(w, 0); if (w >= radius) cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI); else cr.lineTo(w, h); cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI); cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI); cr.fill(); cr.$dispose(); } }; function OsdWindow() { this._init(); } OsdWindow.prototype = { _init: function() { this._popupSize = 0; this._osdSettings = new Gio.Settings({ schema_id: "org.cinnamon" }); this._osdSettings.connect("changed::show-media-keys-osd", Lang.bind(this, this._onOsdSettingsChanged)); this.actor = new St.BoxLayout({ style_class: 'osd-window', vertical: true, important: true }); this._icon = new St.Icon(); this.actor.add(this._icon, { expand: true }); this._level = new LevelBar(); this.actor.add(this._level.actor); this._hideTimeoutId = 0; this._reset(); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._monitorsChanged)); this._onOsdSettingsChanged(); Main.uiGroup.add_child(this.actor); }, setIcon: function(icon) { this._icon.gicon = icon; }, setLevel: function(level) { this._level.actor.visible = (level != undefined); if (level != undefined) { if (this.actor.visible) Tweener.addTween(this._level, { level: level, time: LEVEL_ANIMATION_TIME, transition: 'easeOutQuad' }); else this._level.level = level; } }, show: function() { if (this._osdBaseSize == undefined) return; if (!this._icon.gicon) return; if (!this.actor.visible) { Meta.disable_unredirect_for_screen(global.screen); this._level.setLevelBarHeight(this._sizeMultiplier); this.actor.show(); this.actor.opacity = 0; this.actor.raise_top(); Tweener.addTween(this.actor, { opacity: 255, time: FADE_TIME, transition: 'easeOutQuad' }); } if (this._hideTimeoutId) Mainloop.source_remove(this._hideTimeoutId); this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT, Lang.bind(this, this._hide)); }, _hide: function() { this._hideTimeoutId = 0; Tweener.addTween(this.actor, { opacity: 0, time: FADE_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this._reset(); Meta.enable_unredirect_for_screen(global.screen); }) }); }, _reset: function() { this.actor.hide(); this.setLevel(null); }, _monitorsChanged: function() { let monitor = Main.layoutManager.primaryMonitor; let scaleW = monitor.width / 640.0; let scaleH = monitor.height / 480.0; let scale = Math.min(scaleW, scaleH); this._popupSize = this._osdBaseSize * Math.max(1, scale); let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this._icon.icon_size = this._popupSize / (2 * scaleFactor); this.actor.set_size(this._popupSize, this._popupSize); this.actor.translation_y = (monitor.height + monitor.y) - (this._popupSize + (50 * scaleFactor)); this.actor.translation_x = ((monitor.width / 2) + monitor.x) - (this._popupSize / 2); }, _onOsdSettingsChanged: function() { let currentSize = this._osdSettings.get_string("show-media-keys-osd"); switch (currentSize) { case "disabled": this._osdBaseSize = null; break; case "small": this._sizeMultiplier = 0.7; this._osdBaseSize = Math.floor(OSD_SIZE * this._sizeMultiplier); break; case "large": this._sizeMultiplier = 1.0; this._osdBaseSize = OSD_SIZE; break; default: this._sizeMultiplier = 0.85; this._osdBaseSize = Math.floor(OSD_SIZE * this._sizeMultiplier); } this._monitorsChanged(); } };Cinnamon-2.8.6/js/ui/layout.js0000664000175000017500000007130412625666430015140 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const GLib = imports.gi.GLib; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Signals = imports.signals; const St = imports.gi.St; const Main = imports.ui.main; const Params = imports.misc.params; const ScreenSaver = imports.misc.screenSaver; const Tweener = imports.ui.tweener; const EdgeFlip = imports.ui.edgeFlip; const HotCorner = imports.ui.hotCorner; const DeskletManager = imports.ui.deskletManager; const STARTUP_ANIMATION_TIME = 0.5; const KEYBOARD_ANIMATION_TIME = 0.5; function LayoutManager() { this._init.apply(this, arguments); } LayoutManager.prototype = { _init: function () { this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL); this.monitors = []; this.primaryMonitor = null; this.primaryIndex = -1; this.hotCornerManager = null; this.edgeRight = null; this.edgeLeft = null; this._chrome = new Chrome(this); this.enabledEdgeFlip = global.settings.get_boolean("enable-edge-flip"); this.edgeFlipDelay = global.settings.get_int("edge-flip-delay"); this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', reactive: true, track_hover: true }); this.addChrome(this.keyboardBox, { visibleInFullscreen: true, affectsStruts: true }); this._keyboardHeightNotifyId = 0; this._monitorsChanged(); global.settings.connect("changed::enable-edge-flip", Lang.bind(this, this._onEdgeFlipChanged)); global.settings.connect("changed::edge-flip-delay", Lang.bind(this, this._onEdgeFlipChanged)); global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked)); global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged)); global.window_manager.connect('switch-workspace', Lang.bind(this, this._windowsRestacked)); }, _onEdgeFlipChanged: function(){ this.enabledEdgeFlip = global.settings.get_boolean("enable-edge-flip"); this.edgeFlipDelay = global.settings.get_int("edge-flip-delay"); this.edgeRight.enabled = this.enabledEdgeFlip; this.edgeRight.delay = this.edgeFlipDelay; this.edgeLeft.enabled = this.enabledEdgeFlip; this.edgeLeft.delay = this.edgeFlipDelay; }, _windowsRestacked: function() { this._chrome.updateRegions(); }, // This is called by Main after everything else is constructed; // Certain functions need to access other Main elements that do // not exist yet when the LayoutManager was constructed. init: function() { this._chrome.init(); this.edgeRight = new EdgeFlip.EdgeFlipper(St.Side.RIGHT, Main.wm.actionFlipWorkspaceRight); this.edgeLeft = new EdgeFlip.EdgeFlipper(St.Side.LEFT, Main.wm.actionFlipWorkspaceLeft); this.edgeRight.enabled = this.enabledEdgeFlip; this.edgeRight.delay = this.edgeFlipDelay; this.edgeLeft.enabled = this.enabledEdgeFlip; this.edgeLeft.delay = this.edgeFlipDelay; this.hotCornerManager = new HotCorner.HotCornerManager(); }, _toggleExpo: function() { if (Main.expo.animationInProgress) return; if (Main.overview.visible) { this._activationTime = Date.now() / 1000; Main.overview.hide(); } Main.expo.toggle(); }, _updateMonitors: function() { let screen = global.screen; this.monitors = []; let nMonitors = screen.get_n_monitors(); for (let i = 0; i < nMonitors; i++) this.monitors.push(screen.get_monitor_geometry(i)); if (nMonitors == 1) { this.primaryIndex = this.bottomIndex = 0; } else { // If there are monitors below the primary, then we need // to split primary from bottom. this.primaryIndex = this.bottomIndex = screen.get_primary_monitor(); for (let i = 0; i < this.monitors.length; i++) { let monitor = this.monitors[i]; if (this._isAboveOrBelowPrimary(monitor)) { if (monitor.y > this.monitors[this.bottomIndex].y) this.bottomIndex = i; } } } this.primaryMonitor = this.monitors[this.primaryIndex]; this.bottomMonitor = this.monitors[this.bottomIndex]; }, _updateHotCorners: function() { if (this.hotCornerManager) this.hotCornerManager.updatePosition(this.primaryMonitor, this.bottomMonitor); }, _updateBoxes: function() { this._updateHotCorners(); this.keyboardBox.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height); this.keyboardBox.set_size(this.bottomMonitor.width, -1); this._chrome._queueUpdateRegions(); }, _monitorsChanged: function() { this._updateMonitors(); this._updateBoxes(); this._updateHotCorners(); this.emit('monitors-changed'); }, _isAboveOrBelowPrimary: function(monitor) { let primary = this.monitors[this.primaryIndex]; let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width; let primaryLeft = primary.x, primaryRight = primary.x + primary.width; if ((monitorLeft >= primaryLeft && monitorLeft < primaryRight) || (monitorRight > primaryLeft && monitorRight <= primaryRight) || (primaryLeft >= monitorLeft && primaryLeft < monitorRight) || (primaryRight > monitorLeft && primaryRight <= monitorRight)) return true; return false; }, get focusIndex() { let focusWindow = global.display.focus_window; if (focusWindow) { let wrect = focusWindow.get_outer_rect(); for (let i = 0; i < this.monitors.length; i++) { let monitor = this.monitors[i]; if (monitor.x <= wrect.x && monitor.y <= wrect.y && monitor.x + monitor.width > wrect.x && monitor.y + monitor.height > wrect.y) return i; } } return this.primaryIndex; }, get focusMonitor() { return this.monitors[this.focusIndex]; }, get currentMonitor() { let index = global.screen.get_current_monitor(); return Main.layoutManager.monitors[index]; }, _prepareStartupAnimation: function() { // During the initial transition, add a simple actor to block all events, // so they don't get delivered to X11 windows that have been transformed. this._coverPane = new Clutter.Actor({ opacity: 0, width: global.screen_width, height: global.screen_height, reactive: true }); this.addChrome(this._coverPane); // We need to force an update of the regions now before we scale // the UI group to get the correct allocation for the struts. this._chrome.updateRegions(); Main.panelManager.setPanelsOpacity(0); this.keyboardBox.hide(); let monitor = this.primaryMonitor; let x = monitor.x + monitor.width / 2.0; let y = monitor.y + monitor.height / 2.0; Main.uiGroup.set_pivot_point(x / global.screen_width, y / global.screen_height); Main.uiGroup.scale_x = Main.uiGroup.scale_y = 0.75; Main.uiGroup.opacity = 0; global.background_actor.show(); global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); }, _startupAnimation: function() { // Don't animate the strut this._chrome.freezeUpdateRegions(); Tweener.addTween(Main.uiGroup, { scale_x: 1, scale_y: 1, opacity: 255, time: STARTUP_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._startupAnimationComplete, onCompleteScope: this }); }, _startupAnimationComplete: function() { global.stage.no_clear_hint = true; this._coverPane.destroy(); this._coverPane = null; Main.panelManager.setPanelsOpacity(255); this.keyboardBox.show(); global.window_group.remove_clip(); this._chrome.thawUpdateRegions(); }, showKeyboard: function () { if (Main.messageTray) Main.messageTray.hide(); this.keyboardBox.raise_top(); Tweener.addTween(this.keyboardBox, { anchor_y: this.keyboardBox.height, time: KEYBOARD_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._showKeyboardComplete, onCompleteScope: this }); }, _showKeyboardComplete: function() { // Poke Chrome to update the input shape; it doesn't notice // anchor point changes this._chrome.updateRegions(); this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', Lang.bind(this, function () { this.keyboardBox.anchor_y = this.keyboardBox.height; })); }, hideKeyboard: function (immediate) { if (Main.messageTray) Main.messageTray.hide(); if (this._keyboardHeightNotifyId) { this.keyboardBox.disconnect(this._keyboardHeightNotifyId); this._keyboardHeightNotifyId = 0; } Tweener.addTween(this.keyboardBox, { anchor_y: 0, time: immediate ? 0 : KEYBOARD_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._hideKeyboardComplete, onCompleteScope: this }); }, _hideKeyboardComplete: function() { this._chrome.updateRegions(); }, // addChrome: // @actor: an actor to add to the chrome // @params: (optional) additional params // // Adds @actor to the chrome, and (unless %affectsInputRegion in // @params is %false) extends the input region to include it. // Changes in @actor's size, position, and visibility will // automatically result in appropriate changes to the input // region. // // If %affectsStruts in @params is %true (and @actor is along a // screen edge), then @actor's size and position will also affect // the window manager struts. Changes to @actor's visibility will // NOT affect whether or not the strut is present, however. // // If %visibleInFullscreen in @params is %true, the actor will be // visible even when a fullscreen window should be covering it. addChrome: function(actor, params) { this._chrome.addActor(actor, params); }, // trackChrome: // @actor: a descendant of the chrome to begin tracking // @params: parameters describing how to track @actor // // Tells the chrome to track @actor, which must be a descendant // of an actor added via addChrome(). This can be used to extend the // struts or input region to cover specific children. // // @params can have any of the same values as in addChrome(), // though some possibilities don't make sense (eg, trying to have // a %visibleInFullscreen child of a non-%visibleInFullscreen // parent). By default, @actor has the same params as its chrome // ancestor. trackChrome: function(actor, params) { this._chrome.trackActor(actor, params); }, // untrackChrome: // @actor: an actor previously tracked via trackChrome() // // Undoes the effect of trackChrome() untrackChrome: function(actor) { this._chrome.untrackActor(actor); }, // removeChrome: // @actor: a chrome actor // // Removes @actor from the chrome removeChrome: function(actor) { this._chrome.removeActor(actor); }, findMonitorForActor: function(actor) { return this._chrome.findMonitorForActor(actor); }, isTrackingChrome: function(actor) { return this._chrome._findActor(actor) != -1; } }; Signals.addSignalMethods(LayoutManager.prototype); // This manages Cinnamon "chrome"; the UI that's visible in the // normal mode (ie, outside the Overview), that surrounds the main // workspace content. const defaultParams = { visibleInFullscreen: false, affectsStruts: false, affectsInputRegion: true, addToWindowgroup: false, doNotAdd: false }; function Chrome() { this._init.apply(this, arguments); } Chrome.prototype = { _init: function(layoutManager) { this._layoutManager = layoutManager; this._monitors = []; this._inOverview = false; this._updateRegionIdle = 0; this._freezeUpdateCount = 0; this._trackedActors = []; this._layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout)); global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked)); // Need to update struts on new workspaces when they are added global.screen.connect('notify::n-workspaces', Lang.bind(this, this._queueUpdateRegions)); this._screenSaverActive = false; this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged)); this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) { if (!err) this._onScreenSaverActiveChanged(this._screenSaverProxy, result); })); this._relayout(); }, init: function() { Main.overview.connect('showing', Lang.bind(this, this._overviewShowing)); Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden)); }, addActor: function(actor, params) { let actorData = Params.parse(params, defaultParams); if (actorData.addToWindowgroup) global.window_group.add_actor(actor); else if (!actorData.doNotAdd) Main.uiGroup.add_actor(actor); this._trackActor(actor, params); }, trackActor: function(actor, params) { let ancestor = actor.get_parent(); let index = this._findActor(ancestor); while (ancestor && index == -1) { ancestor = ancestor.get_parent(); index = this._findActor(ancestor); } if (!ancestor) throw new Error('actor is not a descendent of a chrome actor'); let ancestorData = this._trackedActors[index]; if (!params) params = {}; // We can't use Params.parse here because we want to drop // the extra values like ancestorData.actor for (let prop in defaultParams) { if (!params.hasOwnProperty(prop)) params[prop] = ancestorData[prop]; } this._trackActor(actor, params); }, untrackActor: function(actor) { this._untrackActor(actor); }, removeActor: function(actor) { let i = this._findActor(actor); if (i == -1) return; let actorData = this._trackedActors[i]; if (actorData.addToWindowgroup) global.window_group.remove_actor(actor); else Main.uiGroup.remove_actor(actor); this._untrackActor(actor); }, _findActor: function(actor) { for (let i = 0; i < this._trackedActors.length; i++) { let actorData = this._trackedActors[i]; if (actorData.actor == actor) return i; } return -1; }, modifyActorParams: function(actor, params) { let index = this._findActor(actor); if (index == -1) throw new Error('could not find actor in chrome'); for (var i in params){ this._trackedActors[index][i] = params[i]; } this._queueUpdateRegions(); }, _trackActor: function(actor, params) { if (this._findActor(actor) != -1) throw new Error('trying to re-track existing chrome actor'); let actorData = Params.parse(params, defaultParams); actorData.actor = actor; if (actorData.addToWindowgroup) actorData.isToplevel = actor.get_parent() == global.window_group; else actorData.isToplevel = actor.get_parent() == Main.uiGroup; actorData.visibleId = actor.connect('notify::visible', Lang.bind(this, this._queueUpdateRegions)); actorData.allocationId = actor.connect('notify::allocation', Lang.bind(this, this._queueUpdateRegions)); actorData.parentSetId = actor.connect('parent-set', Lang.bind(this, this._actorReparented)); // Note that destroying actor will unset its parent, so we don't // need to connect to 'destroy' too. this._trackedActors.push(actorData); this._queueUpdateRegions(); }, _untrackActor: function(actor) { let i = this._findActor(actor); if (i == -1) return; let actorData = this._trackedActors[i]; this._trackedActors.splice(i, 1); actor.disconnect(actorData.visibleId); actor.disconnect(actorData.allocationId); actor.disconnect(actorData.parentSetId); this._queueUpdateRegions(); }, _actorReparented: function(actor, oldParent) { let i = this._findActor(actor); if (i == -1) return; let actorData = this._trackedActors[i]; let newParent = actor.get_parent(); if (!newParent) this._untrackActor(actor); else{ if (actorData.addToWindowgroup) actorData.isToplevel = (newParent == global.window_group); else actorData.isToplevel = (newParent == Main.uiGroup); } }, _updateVisibility: function() { for (let i = 0; i < this._trackedActors.length; i++) { let actorData = this._trackedActors[i], visible; if (!actorData.isToplevel) continue; if (this._screenSaverActive) visible = false; else if (this._inOverview) visible = true; else if (!actorData.visibleInFullscreen && this.findMonitorForActor(actorData.actor).inFullscreen) visible = false; else visible = true; Main.uiGroup.set_skip_paint(actorData.actor, !visible); } }, _overviewShowing: function() { this._inOverview = true; this._updateVisibility(); this._queueUpdateRegions(); }, _overviewHidden: function() { this._inOverview = false; this._updateVisibility(); this._queueUpdateRegions(); }, _relayout: function() { this._monitors = this._layoutManager.monitors; this._primaryMonitor = this._layoutManager.primaryMonitor; this._updateFullscreen(); this._updateVisibility(); this._queueUpdateRegions(); }, _onScreenSaverActiveChanged: function(proxy, screenSaverActive) { this._screenSaverActive = screenSaverActive; this._updateVisibility(); this._queueUpdateRegions(); }, _findMonitorForRect: function(x, y, w, h) { // First look at what monitor the center of the rectangle is at let cx = x + w/2; let cy = y + h/2; for (let i = 0; i < this._monitors.length; i++) { let monitor = this._monitors[i]; if (cx >= monitor.x && cx < monitor.x + monitor.width && cy >= monitor.y && cy < monitor.y + monitor.height) return monitor; } // If the center is not on a monitor, return the first overlapping monitor for (let i = 0; i < this._monitors.length; i++) { let monitor = this._monitors[i]; if (x + w > monitor.x && x < monitor.x + monitor.width && y + h > monitor.y && y < monitor.y + monitor.height) return monitor; } // otherwise on no monitor return null; }, _findMonitorForWindow: function(window) { return this._findMonitorForRect(window.x, window.y, window.width, window.height); }, // This call guarantees that we return some monitor to simplify usage of it // In practice all tracked actors should be visible on some monitor anyway findMonitorForActor: function(actor) { let [x, y] = actor.get_transformed_position(); let [w, h] = actor.get_transformed_size(); let monitor = this._findMonitorForRect(x, y, w, h); if (monitor) return monitor; return this._primaryMonitor; // Not on any monitor, pretend its on the primary }, _queueUpdateRegions: function() { if (!this._updateRegionIdle && !this._freezeUpdateCount) this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions), Meta.PRIORITY_BEFORE_REDRAW); }, freezeUpdateRegions: function() { if (this._updateRegionIdle) this.updateRegions(); this._freezeUpdateCount++; }, thawUpdateRegions: function() { this._freezeUpdateCount = --this._freezeUpdateCount >= 0 ? this.freezeUpdateCount : 0; this._queueUpdateRegions(); }, _updateFullscreen: function() { let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index()); // Reset all monitors to not fullscreen for (let i = 0; i < this._monitors.length; i++) this._monitors[i].inFullscreen = false; // Ordinary chrome should be visible unless there is a window // with layer FULLSCREEN, or a window with layer // OVERRIDE_REDIRECT that covers the whole screen. // ('override_redirect' is not actually a layer above all // other windows, but this seems to be how muffin treats it // currently...) If we wanted to be extra clever, we could // figure out when an OVERRIDE_REDIRECT window was trying to // partially overlap us, and then adjust the input region and // our clip region accordingly... // @windows is sorted bottom to top. for (let i = windows.length - 1; i > -1; i--) { let window = windows[i]; let metaWindow = window.get_meta_window(); // Skip minimized windows if (!window.showing_on_its_workspace()) continue; if (metaWindow.get_layer() == Meta.StackLayer.FULLSCREEN || metaWindow.is_fullscreen()) { let monitor = this._findMonitorForWindow(window); if (monitor) monitor.inFullscreen = true; } if (metaWindow.is_override_redirect()) { // Check whether the window is screen sized let isScreenSized = (window.x == 0 && window.y == 0 && window.width == global.screen_width && window.height == global.screen_height); if (isScreenSized) { for (let i = 0; i < this._monitors.length; i++) this._monitors[i].inFullscreen = true; } // Or whether it is monitor sized let monitor = this._findMonitorForWindow(window); if (monitor && window.x <= monitor.x && window.x + window.width >= monitor.x + monitor.width && window.y <= monitor.y && window.y + window.height >= monitor.y + monitor.height) monitor.inFullscreen = true; } else break; } }, _windowsRestacked: function() { let wasInFullscreen = []; for (let i = 0; i < this._monitors.length; i++) wasInFullscreen[i] = this._monitors[i].inFullscreen; this._updateFullscreen(); let changed = false; for (let i = 0; i < wasInFullscreen.length; i++) { if (wasInFullscreen[i] != this._monitors[i].inFullscreen) { changed = true; break; } } if (changed) { this._updateVisibility(); this._queueUpdateRegions(); } }, updateRegions: function() { let rects = [], struts = [], i; if (this._updateRegionIdle) { Mainloop.source_remove(this._updateRegionIdle); this._updateRegionIdle = 0; } for (let i = 0; i < this._trackedActors.length; i++) { let actorData = this._trackedActors[i]; if (!actorData.affectsInputRegion && !actorData.affectsStruts) continue; let [x, y] = actorData.actor.get_transformed_position(); let [w, h] = actorData.actor.get_transformed_size(); x = Math.round(x); y = Math.round(y); w = Math.round(w); h = Math.round(h); if (actorData.affectsInputRegion) { let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h}); if (actorData.actor.get_paint_visibility() && !Main.uiGroup.get_skip_paint(actorData.actor)) rects.push(rect); } if (actorData.affectsStruts) { // Limit struts to the size of the screen let x1 = Math.max(x, 0); let x2 = Math.min(x + w, global.screen_width); let y1 = Math.max(y, 0); let y2 = Math.min(y + h, global.screen_height); // Metacity wants to know what side of the monitor the // strut is considered to be attached to. First, we find // the monitor that contains the strut. If the actor is // only touching one edge, or is touching the entire // border of that monitor, then it's obvious which side // to call it. If it's in a corner, we pick a side // arbitrarily. If it doesn't touch any edges, or it // spans the width/height across the middle of the // screen, then we don't create a strut for it at all. let monitor = this.findMonitorForActor(actorData.actor); let side; if (x1 <= monitor.x && x2 >= monitor.x + monitor.width) { if (y1 <= monitor.y) side = Meta.Side.TOP; else if (y2 >= monitor.y + monitor.height) side = Meta.Side.BOTTOM; else continue; } else if (y1 <= monitor.y && y2 >= monitor.y + monitor.height) { if (x1 <= monitor.x) side = Meta.Side.LEFT; else if (x2 >= monitor.x + monitor.width) side = Meta.Side.RIGHT; else continue; } else if (x1 <= monitor.x) side = Meta.Side.LEFT; else if (y1 <= monitor.y) side = Meta.Side.TOP; else if (x2 >= monitor.x + monitor.width) side = Meta.Side.RIGHT; else if (y2 >= monitor.y + monitor.height) side = Meta.Side.BOTTOM; else continue; let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1}); let strut = new Meta.Strut({ rect: strutRect, side: side }); struts.push(strut); } } let enable_stage = true; let top_windows = global.top_window_group.get_children(); for (var i in top_windows){ if (top_windows[i]._windowType != Meta.WindowType.TOOLTIP){ enable_stage = false; break; } } if (enable_stage) global.set_stage_input_region(rects); else global.set_stage_input_region([]); let screen = global.screen; for (let w = 0; w < screen.n_workspaces; w++) { let workspace = screen.get_workspace_by_index(w); workspace.set_builtin_struts(struts); } return false; } }; Cinnamon-2.8.6/js/ui/flashspot.js0000664000175000017500000000256712625666430015633 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const FLASHSPOT_ANIMATION_TIME = 0.15; // seconds function Flashspot(area) { this._init(area); } Flashspot.prototype = { __proto__: Lightbox.Lightbox.prototype, _init: function(area) { Lightbox.Lightbox.prototype._init.call(this, Main.uiGroup, { inhibitEvents: true, width: area.width, height: area.height }); this.actor.style_class = 'flashspot'; this.actor.set_position(area.x, area.y); }, fire: function() { this.actor.opacity = 0; Tweener.addTween(this.actor, { opacity: 255, time: FLASHSPOT_ANIMATION_TIME, transition: 'linear', onComplete: Lang.bind(this, this._onFireShowComplete) }); this.actor.show(); }, _onFireShowComplete: function() { Tweener.addTween(this.actor, { opacity: 0, time: FLASHSPOT_ANIMATION_TIME, transition: 'linear', onComplete: Lang.bind(this, function() { this.destroy(); }) }); } }; Cinnamon-2.8.6/js/ui/backgroundManager.js0000664000175000017500000000644612625666430017242 0ustar fabiofabio// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- const Gio = imports.gi.Gio; const Lang = imports.lang; const Mainloop = imports.mainloop; function BackgroundManager() { this._init(); } BackgroundManager.prototype = { _init: function() { let schema = Gio.SettingsSchemaSource.get_default(); if (!schema.lookup("org.gnome.desktop.background", true)) return this._gnomeSettings = new Gio.Settings({ schema_id: "org.gnome.desktop.background" }); this._cinnamonSettings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.background" }); this._string_keys = ["color-shading-type", "picture-options", "picture-uri", "primary-color", "secondary-color"]; this._string_values = []; this._int_keys = ["picture-opacity"]; this._int_values = []; for (var i in this._string_keys) { this._string_values[i] = this._gnomeSettings.get_string(this._string_keys[i]); } for (var i in this._int_keys) { this._int_values[i] = this._gnomeSettings.get_int(this._int_keys[i]); } this.startTime = new Date().getTime(); this._gnomeSettings.connect('changed', Lang.bind(this, this._onGnomeSettingsChanged)); }, _onGnomeSettingsChanged: function() { let elapsedTime = new Date().getTime() - this.startTime; if (elapsedTime > 60000) { global.log("BackgroundManager: org.gnome.desktop.background changed!"); this._overwriteCinnamonSettings(); } else { let somethingChanged = false; for (var i in this._string_keys) { let key = this._string_keys[i]; let value = this._string_values[i]; let newValue = this._gnomeSettings.get_string(key); if (value != newValue) { global.log("BackgroundManager: org.gnome.desktop.background %s changed (%s -> %s)!".format(key, value, newValue)); this._string_values[i] = newValue; somethingChanged = true; } } for (var i in this._int_keys) { let key = this._int_keys[i]; let value = this._int_values[i]; let newValue = this._gnomeSettings.get_int(key); if (value != newValue) { global.log("BackgroundManager: org.gnome.desktop.background %s changed (%d -> %d)!".format(key, value, newValue)); this._int_values[i] = newValue; somethingChanged = true; } } if (somethingChanged == true) { this._overwriteCinnamonSettings(); } } }, _overwriteCinnamonSettings: function() { for (var key of this._string_keys) { let gnomeValue = this._gnomeSettings.get_string(key); if (this._cinnamonSettings.get_string(key) != gnomeValue) { this._cinnamonSettings.set_string(key, gnomeValue); } } for (var key of this._int_keys) { let gnomeValue = this._gnomeSettings.get_int(key); if (this._cinnamonSettings.get_int(key) != gnomeValue) { this._cinnamonSettings.set_int(key, gnomeValue); } } } }; Cinnamon-2.8.6/js/ui/desklet.js0000664000175000017500000001760212625666430015257 0ustar fabiofabio//-*- indent-tabs-mode: nil-*- const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const GLib = imports.gi.GLib; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Signals = imports.signals; const St = imports.gi.St; const Util = imports.misc.util; const Flashspot = imports.ui.flashspot; const DeskletManager = imports.ui.deskletManager; const DND = imports.ui.dnd; const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const PopupMenu = imports.ui.popupMenu; const Tooltips = imports.ui.tooltips; const Tweener = imports.ui.tweener; const RIGHT_PANEL_POPUP_ANIMATE_TIME = 0.5; const DESKLET_DESTROY_TIME = 0.5; /** * #Desklet * @short_description: Base desklet class to be inherited * @metadata (dictionary): Metadata of desklet * @actor (St.BoxLayout): Actor of desklet * @content (St.Bin): The actor containing the content of the actor * @instance_id (int): Instance id of the desklet * * #Desklet is a base class in which other desklets can inherit */ function Desklet(metadata, desklet_id){ this._init(metadata, desklet_id); } Desklet.prototype = { /** * _init: * @metadata (dictionary): the metadata of the desklet * @desklet_id (int): instance id of the desklet * * Constructor function */ _init: function(metadata, desklet_id){ this.metadata = metadata; this.instance_id = desklet_id; this.actor = new St.BoxLayout({reactive: true, track_hover: true, vertical: true}); this._header = new St.Bin({style_class: 'desklet-header'}); this._header_label = new St.Label(); this._header_label.set_text('Desklet'); this._header.set_child(this._header_label); this.content = new St.Bin(); this.actor.add_actor(this._header); this.actor.add_actor(this.content); this._updateDecoration(); global.settings.connect('changed::desklet-decorations', Lang.bind(this, this._updateDecoration)); this._menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.LEFT, 0); this._menuManager = new PopupMenu.PopupMenuManager(this); this._menuManager.addMenu(this._menu); Main.uiGroup.add_actor(this._menu.actor); this._menu.actor.hide(); this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent)); this._uuid = null; this._dragging = false; this._dragOffset = [0, 0]; this.actor._desklet = this; this.actor._delegate = this; this._drag_end_ids = {"drag-end": 0, "drag-cancelled": 0}; this._draggable = DND.makeDraggable(this.actor, {restoreOnSuccess: true}, Main.deskletContainer.actor); }, /** * setHeader: * @header (string): the header of the desklet * * Sets the header text of the desklet to @header */ setHeader: function(header){ this._header_label.set_text(header); }, /** * setContent: * @actor (Clutter.Actor): actor to be set as child * @params (dictionary): (optional) parameters to be sent * * Sets the content actor of the desklet as @actor */ setContent: function(actor, params){ this.content.set_child(actor, params); }, /** * on_desklet_removed: * * Callback when desklet is removed. To be overridden by individual desklets */ on_desklet_removed: function() { }, /** * destroy: * * Destroys the actor with an fading animation */ destroy: function(){ Tweener.addTween(this.actor, { opacity: 0, transition: 'linear', time: DESKLET_DESTROY_TIME, onComplete: Lang.bind(this, function(){ this.on_desklet_removed(); this.actor.destroy(); })}); this._menu.destroy(); this._menu = null; this._menuManager = null; this.emit('destroy'); }, _updateDecoration: function(){ let dec = global.settings.get_int('desklet-decorations'); let preventDecorations = this.metadata['prevent-decorations']; if (preventDecorations == true){ dec = 0; } switch(dec){ case 0: this._header.hide(); this.content.style_class = 'desklet'; break; case 1: this._header.hide(); this.content.style_class = 'desklet-with-borders'; break; case 2: this._header.show(); this.content.style_class = 'desklet-with-borders-and-header'; break; } }, on_desklet_clicked: function(event) { // Implemented by Desklets }, on_desklet_added_to_desktop_internal: function(userEnabled) { if (userEnabled) { Mainloop.timeout_add(300, Lang.bind(this, function() { let [x, y] = this.actor.get_transformed_position(); let [w, h] = this.actor.get_transformed_size(); let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: w, height: h}); flashspot.fire(); return false; })); } this.on_desklet_added_to_desktop(userEnabled); }, /** * on_desklet_added_to_desktop: * * This function is called by deskletManager when the desklet is added to the desktop. * * This is meant to be overridden in individual applets. */ on_desklet_added_to_desktop: function(userEnabled) { }, _onButtonReleaseEvent: function(actor, event) { if (event.get_button() == 3) { this._menu.toggle(); } else { if (this._menu.isOpen) { this._menu.toggle(); } this.on_desklet_clicked(event); } }, _trackMouse: function() { if(!Main.layoutManager.isTrackingChrome(this.actor)) { Main.layoutManager.addChrome(this.actor, {doNotAdd: true}); this._isTracked = true; } }, _untrackMouse: function() { if(Main.layoutManager.isTrackingChrome(this.actor)) { Main.layoutManager.untrackChrome(this.actor); this._isTracked = false; } }, _onRemoveDesklet: function(){ DeskletManager.removeDesklet(this._uuid, this.instance_id); }, finalizeContextMenu: function() { this.context_menu_separator = new PopupMenu.PopupSeparatorMenuItem(); if (this._menu._getMenuItems().length > 0) { this._menu.addMenuItem(this.context_menu_separator); } this.context_menu_item_about = new PopupMenu.PopupMenuItem(_("About...")) this.context_menu_item_about.connect("activate", Lang.bind(this, this.openAbout)); this._menu.addMenuItem(this.context_menu_item_about); if (!this._meta["hide-configuration"] && GLib.file_test(this._meta["path"] + "/settings-schema.json", GLib.FileTest.EXISTS)) { this.context_menu_item_configure = new PopupMenu.PopupMenuItem(_("Configure...")); this.context_menu_item_configure.connect("activate", Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings desklets " + this._uuid + " " + this.instance_id) })); this._menu.addMenuItem(this.context_menu_item_configure); } this.context_menu_item_remove = new PopupMenu.PopupMenuItem(_("Remove this desklet")); this.context_menu_item_remove.connect("activate", Lang.bind(this, this._onRemoveDesklet)); this._menu.addMenuItem(this.context_menu_item_remove); }, openAbout: function() { new ModalDialog.SpicesAboutDialog(this._meta, "desklets"); } }; Signals.addSignalMethods(Desklet.prototype); Cinnamon-2.8.6/js/ui/overrides.js0000664000175000017500000001570312625666430015626 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio const Lang = imports.lang; const Mainloop = imports.mainloop; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; function init() { overrideGio(); overrideGObject(); overrideMainloop(); overrideJS(); } function check_schema_and_init(obj, method, params) { if (!params.schema) { method.call(obj, params); return; } let listSchemas = Gio.Settings.list_schemas(); if (listSchemas.indexOf(params.schema) != -1) { method.call(obj, params); } else { method.call(obj, { schema_id: "org.cinnamon.invalid-schema" }); log("GSettings schema not found: " + params.schema); throw new Error("GSettings schema not found: " + params.schema); } } function key_exists (obj, key) { return obj.list_keys().indexOf(key) != -1; } function check_key_and_get (obj, method, key) { if (key_exists (obj, key)) { return method.call(obj, key); } else { log("GSettings key not found. schema: " + obj.schema + ", key: " + key); return null; } } function check_key_and_set (obj, method, key, val) { if (key_exists (obj, key)) { return method.call(obj, key, val); } else { log("GSettings key not found. schema: " + obj.schema + ", key: " + key); return false; } } function overrideGio() { Gio._real_init = Gio.Settings.prototype._init; Gio._real_get_value = Gio.Settings.prototype.get_value; Gio._real_set_value = Gio.Settings.prototype.set_value; Gio._real_get_boolean = Gio.Settings.prototype.get_boolean; Gio._real_set_boolean = Gio.Settings.prototype.set_boolean; Gio._real_get_int = Gio.Settings.prototype.get_int; Gio._real_set_int = Gio.Settings.prototype.set_int; Gio._real_get_uint = Gio.Settings.prototype.get_uint; Gio._real_set_uint = Gio.Settings.prototype.set_uint; Gio._real_get_double = Gio.Settings.prototype.get_double; Gio._real_set_double = Gio.Settings.prototype.set_double; Gio._real_get_string = Gio.Settings.prototype.get_string; Gio._real_set_string = Gio.Settings.prototype.set_string; Gio._real_get_strv = Gio.Settings.prototype.get_strv; Gio._real_set_strv = Gio.Settings.prototype.set_strv; Gio._real_get_enum = Gio.Settings.prototype.get_enum; Gio._real_set_enum = Gio.Settings.prototype.set_enum; Gio._real_get_flags = Gio.Settings.prototype.get_flags; Gio._real_set_flags = Gio.Settings.prototype.set_flags; Gio.Settings.prototype._init = function(params) { check_schema_and_init(this, Gio._real_init, params); } Gio.Settings.prototype.get_value = function(key) { return check_key_and_get(this, Gio._real_get_value, key); } Gio.Settings.prototype.set_value = function(key, val) { return check_key_and_set(this, Gio._real_set_value, key, val); } Gio.Settings.prototype.get_boolean = function(key) { return check_key_and_get(this, Gio._real_get_boolean, key); } Gio.Settings.prototype.set_boolean = function(key, val) { return check_key_and_set(this, Gio._real_set_boolean, key, val); } Gio.Settings.prototype.get_int = function(key) { return check_key_and_get(this, Gio._real_get_int, key); } Gio.Settings.prototype.set_int = function(key, val) { return check_key_and_set(this, Gio._real_set_int, key, val); } Gio.Settings.prototype.get_uint = function(key) { return check_key_and_get(this, Gio._real_get_uint, key); } Gio.Settings.prototype.set_uint = function(key, val) { return check_key_and_set(this, Gio._real_set_uint, key, val); } Gio.Settings.prototype.get_double = function(key) { return check_key_and_get(this, Gio._real_get_double, key); } Gio.Settings.prototype.set_double = function(key, val) { return check_key_and_set(this, Gio._real_set_double, key, val); } Gio.Settings.prototype.get_string = function(key) { return check_key_and_get(this, Gio._real_get_string, key); } Gio.Settings.prototype.set_string = function(key, val) { return check_key_and_set(this, Gio._real_set_string, key, val); } Gio.Settings.prototype.get_strv = function(key) { return check_key_and_get(this, Gio._real_get_strv, key); } Gio.Settings.prototype.set_strv = function(key, val) { return check_key_and_set(this, Gio._real_set_strv, key, val); } Gio.Settings.prototype.get_enum = function(key) { return check_key_and_get(this, Gio._real_get_enum, key); } Gio.Settings.prototype.set_enum = function(key, val) { return check_key_and_set(this, Gio._real_set_enum, key, val); } Gio.Settings.prototype.get_flags = function(key) { return check_key_and_get(this, Gio._real_get_flags, key); } Gio.Settings.prototype.set_flags = function(key, val) { return check_key_and_set(this, Gio._real_set_flags, key, val); } } function overrideGObject() { GObject.Object.prototype.disconnect = function(id) { if (GObject.signal_handler_is_connected (this, id)) return GObject.signal_handler_disconnect(this, id); else { log("Invalid or null signal handler id used when attempting to .disconnect from an object."); global.dump_gjs_stack(); return false; } }; } function overrideMainloop() { Mainloop.__real_source_remove = Mainloop.source_remove; Mainloop.source_remove = function (id) { let dump = GLib.MainContext.default().find_source_by_id(id) == null; if (dump) { log("Invalid or null source id used when attempting to run Mainloop.source_remove()"); global.dump_gjs_stack(); } else { Mainloop.__real_source_remove(id); } } } function overrideJS() { String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); } String.prototype.first_cap = function() { return this.charAt(0).toUpperCase(); } Number.prototype.clamp = function(min, max) { return Math.min(Math.max(this, min), max); }; if (!Array.prototype.find) { Array.prototype.find = function(predicate) { if (this === null) { throw new TypeError('Array.prototype.find called on null or undefined'); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } var list = Object(this); var length = list.length >>> 0; var thisArg = arguments[1]; var value; for (var i = 0; i < length; i++) { value = list[i]; if (predicate.call(thisArg, value, i, list)) { return value; } } return undefined; }; Object.defineProperty(Array.prototype, "find", {enumerable: false}); // Or else for (let i in arr) loops will explode; } } Cinnamon-2.8.6/js/ui/runDialog.js0000664000175000017500000004101712625666430015545 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const St = imports.gi.St; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const FileUtils = imports.misc.fileUtils; const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const CinnamonEntry = imports.ui.cinnamonEntry; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const History = imports.misc.history; const MAX_FILE_DELETED_BEFORE_INVALID = 10; const HISTORY_KEY = 'command-history'; const LOCKDOWN_SCHEMA = 'org.cinnamon.desktop.lockdown'; const DISABLE_COMMAND_LINE_KEY = 'disable-command-line'; const TERMINAL_SCHEMA = 'org.cinnamon.desktop.default-applications.terminal'; const EXEC_KEY = 'exec'; const EXEC_ARG_KEY = 'exec-arg'; const SHOW_COMPLETIONS_KEY = 'run-dialog-show-completions'; const ALIASES_KEY = 'run-dialog-aliases'; const DIALOG_GROW_TIME = 0.1; const MAX_COMPLETIONS = 40; const DEVEL_COMMANDS = { 'lg': x => Main.createLookingGlass().open(), 'r': x => global.reexec_self(), 'restart': x => global.reexec_self(), 'debugexit': x => Meta.quit(Meta.ExitCode.ERROR), 'rt': x => Main.themeManager._changeTheme() }; /** * completeCommand: * @text (string): initial string to complete. * * This function finds possible command completions for @text. @text is first * split at whitspaces, and completion is performed on the last segment. Note * that this currently does not recognize escaped whitspaces. * * If the last segment starts with a `/`, then it is considered to be an * absolute path. Otherwise, if it is the first segment (ie. there is only one * segment), it will be treated as a path relative to the home directory or a * path relative to an element of `PATH`. Finally, if it is not an absolute * path and not a first segment, it will be treated as a path relative to the * home directory. * * To perform the completion, first the directory of the last segment is * calculated. For example, if the last segment is `/foo/bar/hel`, then the * directory is `/foo/bar/`. Then everything in the directory is listed, and * it checks which items starts with `hel`. All the items will then be listed * in the `completions` array. The common part of all possible completions is * put in the `postfix` variable, and the tuple `[postfix, completions]` is * returned. * * It is important to note that the returned variables are just the part added * by completion. So if `/foo/bar/hel` completes to `/foo/bar/hello`, then `lo` * is returned, instead of `/foo/bar/hello`. This is the case for both the * `postfix` and `completions` part. * * A special case is when the last segment is empty. In this case, an empty * tuple is returned. * * Returns (array): The tuple `[postfix, completions]`. */ function completeCommand(text) { // Replace an escaped space "\ " with a random unicode character, find the // last space, and then restore "\ " since we don't want to split at escaped strings let last = text.replace(/\\ /g, '\uf001').match(/[^ ]*$/)[0].replace(/\uf001/g, '\\ '); if (last.length == 0) return ["",[]]; let last_path = last.replace(/[^/]*$/, ""); let paths = []; if (last.charAt(0) == '/') { // Match absolute path paths = [last_path]; } else if (last.length != text.length) { // Match filename in home directory paths = [GLib.build_filenamev([GLib.get_home_dir(), last_path])]; } else { // Match file in path or home directory paths = GLib.getenv('PATH').split(':'); paths.push(GLib.get_home_dir()); paths = paths.map(x => GLib.build_filenamev([x, last_path])); } let results = []; paths.forEach(function(path) { try { let file = Gio.File.new_for_path(path); let fileEnum = file.enumerate_children('standard::name,standard::type', Gio.FileQueryInfoFlags.NONE, null); let info; while ((info = fileEnum.next_file(null))) { let name = last_path + info.get_name(); // Escape strings name = name.replace(/ /g, "\\ ") if (info.get_file_type() == Gio.FileType.DIRECTORY) name += "/"; else name += " "; if (name.slice(0, last.length) == last) results.push(name); } } catch (e) { } }); if (results.length == 0) return ["", []]; let common = results.reduce(function(s1, s2) { let k = last.length; let max = Math.min(s1.length, s2.length); while (k < max && s1[k] == s2[k]) k++; return s1.substr(0, k); }); return [common.substring(last.length, common.length), results.map(x => x.substring(last.length, x.length))]; } function RunDialog() { this._init(); } RunDialog.prototype = { __proto__: ModalDialog.ModalDialog.prototype, _init : function() { ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' }); this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA }); this._terminalSettings = new Gio.Settings({ schema_id: TERMINAL_SCHEMA }); global.settings.connect('changed::development-tools', Lang.bind(this, function () { this._enableInternalCommands = global.settings.get_boolean('development-tools'); })); this._enableInternalCommands = global.settings.get_boolean('development-tools'); let label = new St.Label({ style_class: 'run-dialog-label', text: _("Please enter a command:") }); this.contentLayout.add(label, { y_align: St.Align.START }); let entry = new St.Entry({ style_class: 'run-dialog-entry' }); CinnamonEntry.addContextMenu(entry); entry.label_actor = label; this._entryText = entry.clutter_text; this.contentLayout.add(entry, { y_align: St.Align.START }); this.setInitialKeyFocus(this._entryText); this._completionBox = new St.Label({style_class: 'run-dialog-completion-box'}); this.contentLayout.add(this._completionBox); this._completionSelected = 0; this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' }); this.contentLayout.add(this._errorBox, { expand: true }); let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' }); this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE }); this._commandError = false; this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' }); this._errorMessage.clutter_text.line_wrap = true; this._errorBox.add(this._errorMessage, { expand: true, y_align: St.Align.MIDDLE, y_fill: false }); this._errorBox.hide(); this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY, entry: this._entryText, deduplicate: true }); this._entryText.connect('key-press-event', Lang.bind(this, this._onKeyPress)); this._updateCompletionTimer = 0; }, _onKeyPress: function (o, e) { let symbol = e.get_key_symbol(); if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { this.popModal(); if (Cinnamon.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK) this._run(o.get_text(), true); else this._run(o.get_text(), false); if (!this._commandError) this.close(); else { if (!this.pushModal()) this.close(); } return true; } if (symbol == Clutter.Escape) { this.close(); return true; } if (symbol == Clutter.Tab) { this._updateCompletions(true); return true; } if (symbol == Clutter.BackSpace) { this._completionSelected = 0; this._completionBox.hide(); this._oldText = ""; } if (this._completionBox.get_text() != "" && this._completionBox.visible) { if (this._updateCompletionTimer) { Mainloop.source_remove(this._updateCompletionTimer); this._updateCompletionTimer = 0; } this._updateCompletionTimer = Mainloop.timeout_add(200, Lang.bind(this, this._updateCompletions)); return false; } return false; }, // There is different behaviour depending on whether this is called due to // pressing tab or other keys. _updateCompletions: function(tab) { this._updateCompletionTimer = 0; let text = this._entryText.get_text(); /* If update is caused by user pressing key, and the user just finished * a directory path, don't provide new predictions. For example, the * user might have asked for completions when typing /home/us. * Completions provided might be /home/user/ and /home/user2/. * * If user later types "er/" and the text is now /home/user/, then do * not perform completions, since completions will list all files in * /home/user/, which is unexpected. */ if (!tab && text.charAt(text.length - 1) == "/") { this._completionBox.hide(); this._oldText = ""; return; } // Currnet suggested completion is selected. Do not include in query. text = text.slice(0, text.lastIndexOf(this._entryText.get_selection())); /* If update is caused by user typing "tab" and no text has changed * since then, cycle through available completions. */ if (this._oldText == text && tab && this._completionBox.visible) { this._completionSelected ++; this._completionSelected %= this._completions.length; this._showCompletions(text); return; } this._oldText = text; let [postfix, completions] = completeCommand(text); // Keep completions to use for when scrolling, since reading filesystem // every time we scroll lags a lot. this._completions = completions; /* If tab is pressed and there is a common part among all possible * completions, append that common part and stop. Otherwise, list all * possible completions. If there is no possible completion, then hide * completion box. */ if (postfix.length > 0 && tab) { this._entryText.set_text(text + postfix); } else if (completions.length > 0 && global.settings.get_boolean(SHOW_COMPLETIONS_KEY)) { this._completionSelected = 0; this._completionBox.show(); this._showCompletions(text); } else { this._completionBox.hide(); this._oldText = ""; } }, _showCompletions: function(orig) { /* Show a list of possible completions, and allow users to scroll * through them. The scrolling mechanism is done in _updateCompletions, * which provides the current selected index in * this._completionSelected. We simply have to mark this bold in our * list to denote selection, and display it in the entryText (and * select it). * * Show at most MAX_COMPLETIONS=40 completions. If there are too many, * replace with "...", and display only the 40 elements near the * current item. The current item should be 5 items away from the * bottom of the list if possible. */ let text = ""; let i = Math.max(0, Math.min(this._completionSelected - MAX_COMPLETIONS + 5, this._completions.length - MAX_COMPLETIONS)); let end = Math.min(i + MAX_COMPLETIONS, this._completions.length); if (i > 0) { text = "...\n"; i++; } if (end < this._completions.length) end--; for (; i < end; i++) { if (i == this._completionSelected) { text = text + "" + orig + this._completions[i] + "" + "\n"; this._entryText.set_text(orig + this._completions[i]); } else { text = text + orig + this._completions[i] + "\n"; } } if (end < this._completions.length) text += "...\n"; this._completionBox.clutter_text.set_markup(text); this._entryText.set_selection(-1, orig.length); }, _run : function(input, inTerminal) { this._history.addItem(input); this._commandError = false; if (this._enableInternalCommands && input.trim() in DEVEL_COMMANDS) { DEVEL_COMMANDS[input.trim()](); return; } // Aliases is a list of strings of the form a:b, where an instance of // "a" is to be replaced with "b". Replacement is only performed on the // first word let aliases = global.settings.get_strv(ALIASES_KEY); let split = input.split(" "); for (let i = 0; i < aliases.length; i++) { if (split[0] == aliases[i].split(":")[0]) { split[0] = aliases[i].split(":")[1]; break; } } let command = split.join(" "); try { if (inTerminal) { let exec = this._terminalSettings.get_string(EXEC_KEY); let exec_arg = this._terminalSettings.get_string(EXEC_ARG_KEY); command = exec + ' ' + exec_arg + ' ' + input; } Util.trySpawnCommandLine(command); } catch (e) { // Mmmh, that failed - see if @input matches an existing file let path = null; input = input.trim(); if (input.charAt(0) == '/') { path = input; } else { if (input.charAt(0) == '~') input = input.slice(1); path = GLib.build_filenamev([GLib.get_home_dir(), input]); } if (GLib.file_test(path, GLib.FileTest.EXISTS)) { let file = Gio.file_new_for_path(path); try { Gio.app_info_launch_default_for_uri(file.get_uri(), global.create_app_launch_context()); } catch (e) { // The exception from gjs contains an error string like: // Error invoking Gio.app_info_launch_default_for_uri: No application // is registered as handling this file // We are only interested in the part after the first colon. let message = e.message.replace(/[^:]*: *(.+)/, '$1'); this._showError(message); } } else { this._showError(e.message); } } }, _showError : function(message) { this._commandError = true; this._errorMessage.set_text(message); if (!this._errorBox.visible) { let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1); let parentActor = this._errorBox.get_parent(); Tweener.addTween(parentActor, { height: parentActor.height + errorBoxNaturalHeight, time: DIALOG_GROW_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { parentActor.set_height(-1); this._errorBox.show(); }) }); } }, open: function() { this._history.lastItem(); this._errorBox.hide(); this._entryText.set_text(''); this._completionBox.hide(); this._commandError = false; if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY)) return; ModalDialog.ModalDialog.prototype.open.call(this); }, }; Signals.addSignalMethods(RunDialog.prototype); Cinnamon-2.8.6/js/ui/xdndHandler.js0000664000175000017500000001164012625666430016053 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const DND = imports.ui.dnd; function XdndHandler() { this._init(); } XdndHandler.prototype = { _init: function() { // Used to display a clone of the cursor window when the // window group is hidden (like it happens in the overview) this._cursorWindowClone = null; // Used as a drag actor in case we don't have a cursor window clone this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 }); global.stage.add_actor(this._dummy); this._dummy.hide(); // Muffin delays the creation of the output window as long // as possible to avoid flicker. In case a plugin wants to // access it directly it has to connect to the stage's show // signal. (see comment in compositor.c:meta_compositor_manage_screen) global.stage.connect('show', function () { global.init_xdnd(); return false; }); global.connect('xdnd-enter', Lang.bind(this, this._onEnter)); global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged)); global.connect('xdnd-leave', Lang.bind(this, this._onLeave)); this._windowGroupVisibilityHandlerId = 0; }, // Called when the user cancels the drag (i.e release the button) _onLeave: function() { if (this._windowGroupVisibilityHandlerId != 0) { global.window_group.disconnect(this._windowGroupVisibilityHandlerId); this._windowGroupVisibilityHandlerId = 0; } if (this._cursorWindowClone) { this._cursorWindowClone.destroy(); this._cursorWindowClone = null; } this.emit('drag-end'); }, _onEnter: function() { this._windowGroupVisibilityHandlerId = global.window_group.connect('notify::visible', Lang.bind(this, this._onWindowGroupVisibilityChanged)); this.emit('drag-begin', global.get_current_time()); }, _onWindowGroupVisibilityChanged: function() { if (!global.window_group.visible) { if (this._cursorWindowClone) return; let windows = global.get_window_actors(); let cursorWindow = windows[windows.length - 1]; // FIXME: more reliable way? if (!cursorWindow.is_override_redirect()) return; let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION, source: cursorWindow}); this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow }); global.overlay_group.add_actor(this._cursorWindowClone); Cinnamon.util_set_hidden_from_pick(this._cursorWindowClone, true); // Make sure that the clone has the same position as the source this._cursorWindowClone.add_constraint(constraint_position); } else { if (this._cursorWindowClone) { this._cursorWindowClone.destroy(); this._cursorWindowClone = null; } } }, _onPositionChanged: function(obj, x, y) { let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); // Make sure that the cursor window is on top if (this._cursorWindowClone) this._cursorWindowClone.raise_top(); let dragEvent = { x: x, y: y, dragActor: this._cursorWindowClone ? this._cursorWindowClone : this._dummy, source: this, targetActor: pickedActor }; for (let i = 0; i < DND.dragMonitors.length; i++) { let motionFunc = DND.dragMonitors[i].dragMotion; if (motionFunc) { let result = motionFunc(dragEvent); if (result != DND.DragMotionResult.CONTINUE) return; } } while (pickedActor) { if (pickedActor._delegate && pickedActor._delegate.handleDragOver) { let result = pickedActor._delegate.handleDragOver(this, dragEvent.dragActor, x, y, global.get_current_time()); if (result != DND.DragMotionResult.CONTINUE) return; } pickedActor = pickedActor.get_parent(); } } } Signals.addSignalMethods(XdndHandler.prototype); Cinnamon-2.8.6/js/ui/tweener.js0000664000175000017500000004373612625666430015304 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Mainloop = imports.mainloop; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Signals = imports.signals; const Tweener = imports.tweener.tweener; /** * FILE:tweener.js * @short_description: File providing tweening functions * * This is a wrapper around imports.tweener.tweener that adds a bit of * Clutter integration and some additional callbacks: * * 1. If the tweening target is a Clutter.Actor, then the tweenings * will automatically be removed if the actor is destroyed * * 2. If target._delegate.onAnimationStart() exists, it will be * called when the target starts being animated. * * 3. If target._delegate.onAnimationComplete() exists, it will be * called once the target is no longer being animated. * * The onAnimationStart() and onAnimationComplete() callbacks differ * from the tweener onStart and onComplete parameters, in that (1) * they track whether or not the target has *any* tweens attached to * it, as opposed to be called for *each* tween, and (2) * onAnimationComplete() is always called when the object stops being * animated, regardless of whether it stopped normally or abnormally. * * onAnimationComplete() is called at idle time, which means that if a * tween completes and then another is added before returning to the * main loop, the complete callback will not be called (until the new * tween finishes). * * * ActionScript Tweener methods that imports.tweener.tweener doesn't * currently implement: getTweens, getVersion, registerTransition, * setTimeScale, updateTime. * * imports.tweener.tweener methods that we don't re-export: * pauseAllTweens, removeAllTweens, resumeAllTweens. (It would be hard * to clean up properly after removeAllTweens, and also, any code that * calls any of these is almost certainly wrong anyway, because they * affect the entire application.) */ /** * init: * * Function called by main.js when Cinnamon starts */ function init() { Tweener.setFrameTicker(new ClutterFrameTicker()); } function addCaller(target, tweeningParameters) { _wrapTweening(target, tweeningParameters); Tweener.addCaller(target, tweeningParameters); } /** * addTween: * @target (Object): the object to tween * @tweeningParameters (param): parameters * * This makes @target tween according to the parameters of @tweeningParameters. * * @tweeningParameters contains certain tweening parameters that describe the * tween, and the actual things to tween. Everything that is not a tweening * parameter is processed as follows: If you have * ``` * { * ... * x: 7 * ... * } * ``` * In your parameters, then the property @x of the @target object will * be animated to the value of @7. * * The tweening parameters are (shamelessly stolen from the actual Tweener * documentation): * * - @time (real): The duration of the transition in seconds * * - @delay (real): The delay (in seconds) before the transition starts. The * default of this parameter (when omitted) is 0. * * - @skipUpdates (int): How many updates must be skipped before an actual * update is made. This is a powerful property that allows the developer to * enforce a different update rate for a given tweening, as if simulating a * lower frame rate. This is useful on transitions that demand a share of * the CPU that's higher than average on each new update, such as filter * tweenings. A value of 1, for example, means that the tweening engine will * do half of the updates on this transition, since it will update then skip * one update; a value of 2 means it will do one third of the normal * updates, since it will update, then skip two updates. The default value * for this parameter (when omitted) is 0, meaning no update is skipped at * all, and the active tweenings are updated on every frame render. * * - @transition (string/function): The type of transition to use. Different * equations can be used, producing different tweening updates based on time * spent. You can specify this parameter by their internal string names * (which you can find by seeing what's offered in the cinnamon-settings * effects page), or use any custom function to have a customized easing * (see below for examples and a more in-depth description). The default * transition is "easeOutExpo". * \ * If you want to use a custom function as the transition, the function must * receive four parameters: current time on the transition, starting * tweening value, change needed in that value, and total easing duration * (plus an optional object, which will contain any parameter passed as the * transitionParams property of new tweenings). During each tweening, the * transition function will be continuously called, with the first parameter * increasing until it reaches the total duration; it must return the new * expected value. * \ * An example of a custom transition is as follows: * ``` * let myFunc = function(t, b, c, d) { * let ts = (t/=d)*t; * let tc = ts*t; * return b+c*(-97.1975*tc*ts + 257.5975*ts*ts + -234.4*tc + 80*ts + -5*t); * }; * Tweener.addTween(this.actor, {x: 200, time: 1, transition: myFunc}); * ``` * * - @transitionParams (array): extra parameters to pass to the custom * transition function * * - @onStart (function): A function that is called immediately before a * tweening starts. It is called once regardless of the number of properties * involved on the tweening. The function scope (in which the event is * executed) is the target object itself, unless specified by the * onStartScope parameter. * * - @onUpdate (function): A function that is called every time a tweening * updates its properties. The function scope (in which the event is * executed) is the target object itself, unless specified by the * @onUpdateScope parameter. * * - @onComplete (function): A function that is called immediately after a * tweening is completed. It is called once regardless of the number of * properties involved on the tweening. The function scope (in which the * event is executed) is the target object itself, unless specified by the * @onCompleteScope parameter. * * - @onOverwrite (function): A function that is called when tweening is * overwritten. It is called once regardless of the number of properties * involved on the tweening. The function scope is the target object itself, * unless specified by the @onOverwriteScope parameter. * * - @onError (function): A function that gets called when an error occurs * when trying to run a tweening. This is used to handle errors more * commonly thrown by other events (that is, from code not controlled by * Tweener), such as onStart, onUpdate or onComplete. The function scope (in * which the event is executed) is the target object itself, unless * specified by the @onErrorScope parameter. * * - @rounded (boolean): Whether or not the values for this tweening must be * rounded before being applied to their respective properties. This is * useful, for example, when sliding objects that must be positioned on * round pixels, like labels that use pixel fonts; its x and y properties * need to be rounded all the time to avoid blurring the text. This option * acts on all properties for that specific tween. The default value for * this parameter (when omitted) is false. * * - @min (real): The minimum the values of this tweening can take. This is * useful, for example, when you animate the opacity of an object with a * bounce transition and don't want the opacity of an object to fall below * 0. Leave empty for no minimum. * * - @max (real): The maximum the values of this tweening can take. This is * useful, for example, when you animate the opacity of an object with a * bounce transition and don't want the opacity of an object to go above 1. * Leave empty for no maximum. * * - @onStartParams (array): A list of parameters (of any type) to be passed * to the onStart function. * * - @onUpdateParams (array): A list of parameters (of any type) to be passed * to the onUpdate function. * * - @onCompleteParams (array): A list of parameters (of any type) to be * passed to the onComplete function. * * - @onOverwriteParams (array): A list of parameters (of any type) to be * passed to the onOverwrite function. * * - @onStartScope (object): The object in which the onStart function will * be executed. This is needed if you have some specialized code inside the * event function; in that case, references to this. inside the function * will reference to the object defined by this parameter. If omitted, the * tweened object is the scope used instead. * * - @onUpdateScope (object): The object in which the onUpdate function will * be executed. This is needed if you have some specialized code inside the * event function; in that case, references to this. inside the function * will reference to the object defined by this parameter. If omitted, the * tweened object is the scope used instead. * * - @onCompleteScope (object): The object in which the onComplete function * will be executed. This is needed if you have some specialized code inside * the event function; in that case, references to this. inside the function * will reference to the object defined by this parameter. If omitted, the * tweened object is the scope used instead. * * - @onOverwriteScope (object): The object in which the onOverwrite function * will be executed. This is needed if you have some specialized code inside * the event function; in that case, references to this. inside the function * will reference to the object defined by this parameter. If omitted, the * tweened object is the scope used instead. * * - @onErrorScope (object): The object in which the onError function will * be executed. This is needed if you have some specialized code inside the * event function; in that case, references to this. inside the function * will reference to the object defined by this parameter. If omitted, the * tweened object is the scope used instead. * */ function addTween(target, tweeningParameters) { _wrapTweening(target, tweeningParameters); Tweener.addTween(target, tweeningParameters); } function _wrapTweening(target, tweeningParameters) { let state = _getTweenState(target); if (!state.destroyedId) { if (target instanceof Clutter.Actor) { state.actor = target; state.destroyedId = target.connect('destroy', _actorDestroyed); } else if (target.actor && target.actor instanceof Clutter.Actor) { state.actor = target.actor; state.destroyedId = target.actor.connect('destroy', function() { _actorDestroyed(target); }); } } _addHandler(target, tweeningParameters, 'onStart', _tweenStarted); _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted); } function _getTweenState(target) { // If we were paranoid, we could keep a plist mapping targets to // states... but we're not that paranoid. if (!target.__CinnamonTweenerState) _resetTweenState(target); return target.__CinnamonTweenerState; } function _resetTweenState(target) { let state = target.__CinnamonTweenerState; if (state) { if (state.destroyedId) state.actor.disconnect(state.destroyedId); if (state.idleCompletedId) { Mainloop.source_remove(state.idleCompletedId); state.idleCompletedId = 0; } } target.__CinnamonTweenerState = {}; } function _addHandler(target, params, name, handler) { if (params[name]) { let oldHandler = params[name]; let oldScope = params[name + 'Scope']; let oldParams = params[name + 'Params']; let eventScope = oldScope ? oldScope : target; params[name] = function () { oldHandler.apply(eventScope, oldParams); handler(target); }; } else params[name] = function () { handler(target); }; } function _actorDestroyed(target) { _resetTweenState(target); Tweener.removeTweens(target); } function _tweenStarted(target) { let state = _getTweenState(target); let delegate = target._delegate; if (!state.running && delegate && delegate.onAnimationStart) delegate.onAnimationStart(); state.running = true; } function _tweenCompleted(target) { let state = _getTweenState(target); if (!state.idleCompletedId) state.idleCompletedId = Mainloop.idle_add(Lang.bind(null, _idleCompleted, target)); } function _idleCompleted(target) { let state = _getTweenState(target); let delegate = target._delegate; if (!isTweening(target)) { _resetTweenState(target); if (delegate && delegate.onAnimationComplete) delegate.onAnimationComplete(); } state.idleCompletedId = 0; return false; } /** * getTweenCount: * @scope (Object): the object we are interested in * * Returns the number of tweens @scope currently has. */ function getTweenCount(scope) { return Tweener.getTweenCount(scope); } /** * isTweening: * @scope (Object): the object we are interested in * * Returns whether @scope is animating */ function isTweening(scope) { return Tweener.getTweenCount(scope) != 0; } /** * removeTween: * @scope (Object): the object we are interested in * * Removes all tweens running on the object. * * FIXME: removeTweens should be much more powerful, but I have no idea how it * works */ function removeTweens(scope) { if (Tweener.removeTweens.apply(null, arguments)) { // If we just removed the last active tween, clean up if (Tweener.getTweenCount(scope) == 0) _tweenCompleted(scope); return true; } else return false; } /** * pauseTweens: * @scope (Object): the object we are interested in * * Pauses all the tweens running on the object. Can be resumed with * Tweener.resumeTweens * * FIXME: removeTweens should be much more powerful, but I have no idea how it * works */ function pauseTweens() { return Tweener.pauseTweens.apply(null, arguments); } /** * resumeTweens: * @scope (Object): the object we are interested in * * Resumes all the tweens running on the object paused by Tweener.pauseTweens * * FIXME: removeTweens should be much more powerful, but I have no idea how it * works */ function resumeTweens() { return Tweener.resumeTweens.apply(null, arguments); } function registerSpecialProperty(name, getFunction, setFunction, parameters, preProcessFunction) { Tweener.registerSpecialProperty(name, getFunction, setFunction, parameters, preProcessFunction); } function registerSpecialPropertyModifier(name, modifyFunction, getFunction) { Tweener.registerSpecialPropertyModifier(name, modifyFunction, getFunction); } function registerSpecialPropertySplitter(name, splitFunction, parameters) { Tweener.registerSpecialPropertySplitter(name, splitFunction, parameters); } /** * #ClutterFrameTicker: * @short_description: Object used internally for clutter animations * * The 'FrameTicker' object is an object used to feed new frames to * Tweener so it can update values and redraw. The default frame * ticker for Tweener just uses a simple timeout at a fixed frame rate * and has no idea of "catching up" by dropping frames. * * We substitute it with custom frame ticker here that connects * Tweener to a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a * whole lot more sophisticated than a simple timeout at a fixed frame * rate, but at least it knows how to drop frames. (See * HippoAnimationManager for a more sophisticated view of continous * time updates; even better is to pay attention to the vertical * vblank and sync to that when possible.) */ function ClutterFrameTicker() { this._init(); } ClutterFrameTicker.prototype = { FRAME_RATE : 60, _init : function() { // We don't have a finite duration; tweener will tell us to stop // when we need to stop, so use 1000 seconds as "infinity" this._timeline = new Clutter.Timeline({ duration: 1000*1000 }); this._startTime = -1; this._timeline.connect('new-frame', Lang.bind(this, function(timeline, frame) { this._onNewFrame(frame); })); let perf_log = Cinnamon.PerfLog.get_default(); perf_log.define_event("tweener.framePrepareStart", "Start of a new animation frame", ""); perf_log.define_event("tweener.framePrepareDone", "Finished preparing frame", ""); }, _onNewFrame : function(frame) { // If there is a lot of setup to start the animation, then // first frame number we get from clutter might be a long ways // into the animation (or the animation might even be done). // That looks bad, so we always start at the first frame of the // animation then only do frame dropping from there. if (this._startTime < 0) this._startTime = this._timeline.get_elapsed_time(); // currentTime is in milliseconds let perf_log = Cinnamon.PerfLog.get_default(); perf_log.event("tweener.framePrepareStart"); this.emit('prepare-frame'); perf_log.event("tweener.framePrepareDone"); }, getTime : function() { return this._timeline.get_elapsed_time(); }, start : function() { if (St.get_slow_down_factor() > 0) Tweener.setTimeScale(1 / St.get_slow_down_factor()); this._timeline.start(); global.begin_work(); }, stop : function() { this._timeline.stop(); this._startTime = -1; global.end_work(); } }; Signals.addSignalMethods(ClutterFrameTicker.prototype); Cinnamon-2.8.6/js/ui/windowAttentionHandler.js0000664000175000017500000000722412625666430020316 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; function WindowAttentionHandler() { this._init(); } WindowAttentionHandler.prototype = { _init : function() { this._tracker = Cinnamon.WindowTracker.get_default(); global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention)); }, _onWindowDemandsAttention : function(display, window) { // We don't want to show the notification when the window is already focused, // because this is rather pointless. // Some apps (like GIMP) do things like setting the urgency hint on the // toolbar windows which would result into a notification even though GIMP itself is // focused. // We are just ignoring the hint on skip_taskbar windows for now. // (Which is the same behaviour as with metacity + panel) if (!window || window.has_focus() || window.is_skip_taskbar() || (window.get_wm_class() && (window.get_wm_class().indexOf("Skype") > -1 || window.get_wm_class().indexOf("Quassel") > -1 || window.get_wm_class().indexOf("Pidgin") > -1 || window.get_wm_class().indexOf("RetroShare") > -1 || window.get_wm_class().indexOf("Psi-plus") > -1 || window.get_wm_class().indexOf("Kadu") > -1))) return; try { if (this._tracker.is_window_interesting(window)) { if (global.settings.get_boolean("bring-windows-to-current-workspace")) { window.change_workspace(global.screen.get_active_workspace()); } else { if (global.screen.get_active_workspace().index() != window.get_workspace().index()) { window.get_workspace().activate(global.get_current_time()); } } if (!global.settings.get_boolean("prevent-focus-stealing")) { window.activate(global.get_current_time()); } } } catch (e) { global.logError('Error showing window demanding attention', e); } } }; function Source(app, window) { this._init(app, window); } Source.prototype = { __proto__ : MessageTray.Source.prototype, _init: function(app, window) { MessageTray.Source.prototype._init.call(this, app.get_name()); this._window = window; this._app = app; this._setSummaryIcon(this.createNotificationIcon()); this.signalIDs = []; this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); }))); this.signalIDs.push(this._window.connect('focus', Lang.bind(this, function() { this.destroy(); }))); this.signalIDs.push(this._window.connect('unmanaged', Lang.bind(this, function() { this.destroy(); }))); this.connect('destroy', Lang.bind(this, this._onDestroy)); }, _onDestroy : function() { for(let i = 0; i < this.signalIDs.length; i++) { this._window.disconnect(this.signalIDs[i]); } this.signalIDs = []; }, createNotificationIcon : function() { return this._app.create_icon_texture(this.ICON_SIZE); }, open : function(notification) { Main.activateWindow(this._window); this.destroy(); } }; Cinnamon-2.8.6/js/ui/slider.js0000664000175000017500000001653312625666430015110 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cairo = imports.cairo; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const St = imports.gi.St; const Signals = imports.signals; const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */ function Slider(value, flat) { this._init(value, flat); } Slider.prototype = { _init: function(value, flat) { if (isNaN(value)) // Avoid spreading NaNs around throw TypeError('The slider value must be a number'); this._value = Math.max(Math.min(value, 1), 0); this.flat = flat; // Don't draw radius on flat sliders this.actor = new St.DrawingArea({ style_class: 'slider', reactive: true }); this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint)); this.actor.connect('button-press-event', Lang.bind(this, this._startDragging)); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this._releaseId = this._motionId = 0; this._dragging = false; }, setValue: function(value) { if (isNaN(value)) throw TypeError('The slider value must be a number'); this._value = Math.max(Math.min(value, 1), 0); this.actor.queue_repaint(); }, _sliderRepaint: function(area) { let cr = area.get_context(); let themeNode = area.get_theme_node(); let [width, height] = area.get_surface_size(); let handleRadius = themeNode.get_length('-slider-handle-radius'); let sliderWidth = width - 2 * handleRadius; let sliderHeight = themeNode.get_length('-slider-height'); let sliderBorderWidth = themeNode.get_length('-slider-border-width'); let sliderBorderRadius = Math.min(width, sliderHeight) / 2; let sliderBorderColor = themeNode.get_color('-slider-border-color'); let sliderColor = themeNode.get_color('-slider-background-color'); let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color'); let sliderActiveColor = themeNode.get_color('-slider-active-background-color'); const TAU = Math.PI * 2; let handleX = handleRadius + (width - 2 * handleRadius) * this._value; if (this.flat) { // Active part cr.rectangle(0, 0, width, height); Clutter.cairo_set_source_color(cr, sliderActiveColor); cr.fill(); Clutter.cairo_set_source_color(cr, sliderActiveBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); // Remaining part let x = width * this._value; cr.rectangle(x, 0, width-x, height); Clutter.cairo_set_source_color(cr, sliderColor); cr.fill(); Clutter.cairo_set_source_color(cr, sliderBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); } else { cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4); cr.lineTo(handleX, (height - sliderHeight) / 2); cr.lineTo(handleX, (height + sliderHeight) / 2); cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2); Clutter.cairo_set_source_color(cr, sliderActiveColor); cr.fillPreserve(); Clutter.cairo_set_source_color(cr, sliderActiveBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4); cr.lineTo(handleX, (height + sliderHeight) / 2); cr.lineTo(handleX, (height - sliderHeight) / 2); cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2); Clutter.cairo_set_source_color(cr, sliderColor); cr.fillPreserve(); Clutter.cairo_set_source_color(cr, sliderBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); } let handleY = height / 2; let color = themeNode.get_foreground_color(); Clutter.cairo_set_source_color(cr, color); cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI); cr.fill(); cr.$dispose(); }, _startDragging: function(actor, event) { if (this._dragging) // don't allow two drags at the same time return; this.emit('drag-begin'); this._dragging = true; // FIXME: we should only grab the specific device that originated // the event, but for some weird reason events are still delivered // outside the slider if using clutter_grab_pointer_for_device Clutter.grab_pointer(this.actor); this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging)); this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent)); let absX, absY; [absX, absY] = event.get_coords(); this._moveHandle(absX, absY); }, _endDragging: function() { if (this._dragging) { this.actor.disconnect(this._releaseId); this.actor.disconnect(this._motionId); Clutter.ungrab_pointer(); this._dragging = false; this.emit('drag-end'); } return true; }, _onScrollEvent: function (actor, event) { let direction = event.get_scroll_direction(); if (direction == Clutter.ScrollDirection.DOWN) { this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP); } else if (direction == Clutter.ScrollDirection.UP) { this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP); } this.actor.queue_repaint(); this.emit('value-changed', this._value); }, _motionEvent: function(actor, event) { let absX, absY; [absX, absY] = event.get_coords(); this._moveHandle(absX, absY); return true; }, _moveHandle: function(absX, absY) { let relX, relY, sliderX, sliderY; [sliderX, sliderY] = this.actor.get_transformed_position(); relX = absX - sliderX; relY = absY - sliderY; let width = this.actor.width; let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius'); let newvalue; if (relX < handleRadius) newvalue = 0; else if (relX > width - handleRadius) newvalue = 1; else newvalue = (relX - handleRadius) / (width - 2 * handleRadius); this._value = newvalue; this.actor.queue_repaint(); this.emit('value-changed', this._value); }, _onKeyPressEvent: function (actor, event) { let key = event.get_key_symbol(); if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) { let delta = key == Clutter.KEY_Right ? 0.1 : -0.1; this._value = Math.max(0, Math.min(this._value + delta, 1)); this.actor.queue_repaint(); this.emit('value-changed', this._value); this.emit('drag-end'); return true; } return false; }, get value() { return this._value; } }; Signals.addSignalMethods(Slider.prototype); Cinnamon-2.8.6/js/ui/workspacesView.js0000664000175000017500000003362612625666430016644 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Meta = imports.gi.Meta; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Signals = imports.signals; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const Workspace = imports.ui.workspace; const WORKSPACE_SWITCH_TIME = 0.25; const SwipeScrollDirection = { NONE: 0, HORIZONTAL: 1, VERTICAL: 2 }; const SwipeScrollResult = { CANCEL: 0, SWIPE: 1, CLICK: 2 }; function WorkspacesView(workspaces) { this._init(workspaces); } WorkspacesView.prototype = { _init: function(workspaces) { this.actor = new St.Group({ style_class: 'workspaces-view' }); // The actor itself isn't a drop target, so we don't want to pick on its area this.actor.set_size(0, 0); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); // does not work: // this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.actor.connect('style-changed', Lang.bind(this, function() { let node = this.actor.get_theme_node(); this._spacing = node.get_length('spacing'); this._updateWorkspaceActors(false); })); this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedChanged)); this._width = 0; this._height = 0; this._x = 0; this._y = 0; this._workspaceRatioSpacing = 0; this._spacing = 0; this._animating = false; // tweening this._scrolling = false; // swipe-scrolling this._animatingScroll = false; // programatically updating the adjustment let activeWorkspaceIndex = global.screen.get_active_workspace_index(); this._workspaces = []; for (let i = 0; i < global.screen.n_workspaces; i++) { let metaWorkspace = global.screen.get_workspace_by_index(i); this._workspaces[i] = new Workspace.Workspace(metaWorkspace, this); this.actor.add_actor(this._workspaces[i].actor); } this._workspaces[activeWorkspaceIndex].actor.raise_top(); // Position/scale the desktop windows and their children after the // workspaces have been created. This cannot be done first because // window movement depends on the Workspaces object being accessible // as an Overview member. let overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function() { Main.overview.disconnect(overviewShowingId); let activeWorkspaceIndex = global.screen.get_active_workspace_index(); this._workspaces[activeWorkspaceIndex].zoomToOverview(); })); this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex, lower: 0, page_increment: 1, page_size: 1, step_increment: 0, upper: this._workspaces.length }); this._scrollAdjustment.connect('notify::value', Lang.bind(this, this._onScroll)); this._swipeScrollBeginId = 0; this._swipeScrollEndId = 0; let restackedNotifyId = global.screen.connect('restacked', Lang.bind(this, this._onRestacked)); let switchWorkspaceNotifyId = global.window_manager.connect('switch-workspace', Lang.bind(this, this._activeWorkspaceChanged)); let nWorkspacesChangedId = global.screen.connect('notify::n-workspaces', Lang.bind(this, this._workspacesChanged)); this._disconnectHandlers = function() { global.window_manager.disconnect(switchWorkspaceNotifyId); global.screen.disconnect(nWorkspacesChangedId); global.screen.disconnect(restackedNotifyId); }; this._onRestacked(); this.actor.connect('key-press-event', Lang.bind(this, this._onStageKeyPress)); global.stage.set_key_focus(this.actor); let primary = Main.layoutManager.primaryMonitor; this.setGeometry(primary.x, primary.y, primary.width, primary.height, 0); }, _onStageKeyPress: function(actor, event) { let modifiers = Cinnamon.get_event_state(event); let symbol = event.get_key_symbol(); if (symbol == Clutter.Escape) { Main.overview.hide(); return true; } let activeWorkspaceIndex = global.screen.get_active_workspace_index(); let activeWorkspace = this._workspaces[activeWorkspaceIndex]; return activeWorkspace._onKeyPress(actor, event); }, setGeometry: function(x, y, width, height, spacing) { this._width = width; this._height = height; this._x = x; this._y = y; this._workspaceRatioSpacing = spacing; }, getActiveWorkspace: function() { let active = global.screen.get_active_workspace_index(); return this._workspaces[active]; }, getWorkspaceByIndex: function(index) { return this._workspaces[index]; }, hide: function() { let activeWorkspaceIndex = global.screen.get_active_workspace_index(); let activeWorkspace = this._workspaces[activeWorkspaceIndex]; activeWorkspace.actor.raise_top(); activeWorkspace.zoomFromOverview(); }, destroy: function() { for (let w = 0; w < this._workspaces.length; w++) { this._workspaces[w].disconnectAll(); this._workspaces[w].destroy(); } this.actor.destroy(); }, updateWindowPositions: function() { for (let w = 0; w < this._workspaces.length; w++) this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE); }, _scrollToActive: function(showAnimation) { let active = global.screen.get_active_workspace_index(); this._updateWorkspaceActors(showAnimation); Main.wm.showWorkspaceOSD(); this._updateScrollAdjustment(active, showAnimation); }, // Update workspace actors parameters // @showAnimation: iff %true, transition between states _updateWorkspaceActors: function(showAnimation) { let active = global.screen.get_active_workspace_index(); // Animation is turned off in a multi-manager scenario till we fix // the animations so that they respect the monitor boundaries. this._animating = Main.layoutManager.monitors.length < 2 && showAnimation; for (let w = 0; w < this._workspaces.length; w++) { let workspace = this._workspaces[w]; Tweener.removeTweens(workspace.actor); let x = (w - active) * (this._width + this._spacing + this._workspaceRatioSpacing); if (this._animating) { let params = { x: x, time: WORKSPACE_SWITCH_TIME, transition: 'easeOutQuad' }; // we have to call _updateVisibility() once before the // animation and once afterwards - it does not really // matter which tween we use, so we pick the first one ... if (w == 0) { this._updateVisibility(); params.onComplete = Lang.bind(this, function() { this._animating = false; this._updateVisibility(); }); } Tweener.addTween(workspace.actor, params); } else { workspace.actor.set_position(x, 0); if (w == 0) this._updateVisibility(); } } }, _updateVisibility: function() { let active = global.screen.get_active_workspace_index(); for (let w = 0; w < this._workspaces.length; w++) { let workspace = this._workspaces[w]; if (this._animating || this._scrolling) { workspace.hideWindowsOverlays(); workspace.actor.show(); } else { workspace.showWindowsOverlays(); workspace.actor.visible = (w == active); } } }, _updateScrollAdjustment: function(index, showAnimation) { if (this._scrolling) return; this._animatingScroll = true; if (showAnimation) { Tweener.addTween(this._scrollAdjustment, { value: index, time: WORKSPACE_SWITCH_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this._animatingScroll = false; }) }); } else { this._scrollAdjustment.value = index; this._animatingScroll = false; } let active = global.screen.get_active_workspace_index(); this._workspaces[active].zoomToOverview(); }, _workspacesChanged: function() { let removedCount = 0; this._workspaces.slice().forEach(function(workspace, i) { let metaWorkspace = global.screen.get_workspace_by_index(i-removedCount); if (workspace.metaWorkspace != metaWorkspace) { Tweener.removeTweens(workspace.actor); workspace.destroy(); this._workspaces.splice(i - removedCount, 1); ++removedCount; } }, this); while (global.screen.n_workspaces > this._workspaces.length) { let lastWs = global.screen.get_workspace_by_index(this._workspaces.length); let workspace = new Workspace.Workspace(lastWs, this); this._workspaces.push(workspace) this.actor.add_actor(workspace.actor); } this._animating = false; this._updateVisibility(); }, _activeWorkspaceChanged: function(wm, from, to, direction) { if (this._scrolling) return; this._scrollToActive(true); }, _onDestroy: function() { this._scrollAdjustment.run_dispose(); this._disconnectHandlers(); }, _onMappedChanged: function() { if (this.actor.mapped) { let direction = SwipeScrollDirection.HORIZONTAL; Main.overview.setScrollAdjustment(this._scrollAdjustment, direction); this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin', Lang.bind(this, this._swipeScrollBegin)); this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end', Lang.bind(this, this._swipeScrollEnd)); } else { Main.overview.disconnect(this._swipeScrollBeginId); Main.overview.disconnect(this._swipeScrollEndId); } }, _swipeScrollBegin: function() { this._scrolling = true; }, _swipeScrollEnd: function(overview, result) { this._scrolling = false; if (result == SwipeScrollResult.CLICK) { let [x, y, mod] = global.get_pointer(); let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); // Only switch to the workspace when there's no application // windows open. The problem is that it's too easy to miss // an app window and get the wrong one focused. let active = global.screen.get_active_workspace_index(); if (this._workspaces[active].isEmpty() && this.actor.contains(actor)) Main.overview.hide(); } // Make sure title captions etc are shown as necessary this._updateVisibility(); }, _onRestacked: function() { let stack = global.get_window_actors().reverse(); let stackIndices = {}; for (let i = 0; i < stack.length; i++) { // Use the stable sequence for an integer to use as a hash key stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; } for (let i = 0; i < this._workspaces.length; i++) this._workspaces[i].syncStacking(stackIndices); }, // sync the workspaces' positions to the value of the scroll adjustment // and change the active workspace if appropriate _onScroll: function(adj) { if (this._animatingScroll) return; let active = global.screen.get_active_workspace_index(); let current = Math.round(adj.value); if (active != current) { let metaWorkspace = this._workspaces[current].metaWorkspace; metaWorkspace.activate(global.get_current_time()); } let last = this._workspaces.length - 1; let firstWorkspaceX = this._workspaces[0].actor.x; let lastWorkspaceX = this._workspaces[last].actor.x; let workspacesWidth = lastWorkspaceX - firstWorkspaceX; if (adj.upper == 1) return; let currentX = firstWorkspaceX; let newX = - adj.value / (adj.upper - 1) * workspacesWidth; let dx = newX - currentX; for (let i = 0; i < this._workspaces.length; i++) { this._workspaces[i].hideWindowsOverlays(); this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1; this._workspaces[i].actor.x += dx; } }, _onScrollEvent: function (actor, event) { switch ( event.get_scroll_direction() ) { case Clutter.ScrollDirection.UP: Main.wm.actionMoveWorkspaceUp(); break; case Clutter.ScrollDirection.DOWN: Main.wm.actionMoveWorkspaceDown(); break; } } }; Signals.addSignalMethods(WorkspacesView.prototype); Cinnamon-2.8.6/js/ui/slideshowManager.js0000664000175000017500000000306012625666430017111 0ustar fabiofabio// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const dbusIFace = ' \ \ \ \ \ \ '; const proxy = Gio.DBusProxy.makeProxyWrapper(dbusIFace); function SlideshowManager() { this._init(); } SlideshowManager.prototype = { _init: function() { this.proxy = null; this._slideshowSettings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.background.slideshow" }); this._slideshowSettings.connect("changed::slideshow-enabled", Lang.bind(this, this._onSlideshowEnabledChanged)); if (this._slideshowSettings.get_boolean("slideshow-enabled")) { this.begin(); } }, _onSlideshowEnabledChanged: function() { if (this._slideshowSettings.get_boolean("slideshow-enabled")) this.begin(); else this.end(); }, ensureProxy: function() { if (!this.proxy) this.proxy = new proxy(Gio.DBus.session, 'org.Cinnamon.Slideshow', '/org/Cinnamon/Slideshow'); }, begin: function() { this.ensureProxy(); this.proxy.beginRemote(); }, end: function() { this.ensureProxy(); this.proxy.endRemote(); }, getNextImage: function() { this.ensureProxy(); this.proxy.getNextImageRemote(); } }; Cinnamon-2.8.6/js/ui/settings.js0000664000175000017500000010203412625666430015456 0ustar fabiofabio/** * FILE:settings.js * @short_description: File providing settings objects for xlets. * * This file provides the settings API for applets, desklets and extensions. */ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Main = imports.ui.main; const Signals = imports.signals; const Extension = imports.ui.extension; const Mainloop = imports.mainloop; const SETTING_SCHEMA_FILE = "settings-schema.json"; /** * ENUM:BindingDirection * @IN: Set the property at binding time, and automatically update the property * and execute the callback when the setting file changes. This is probably * the most common mode. * * @OUT: Set the property at binding time, and automatically update the setting * file when the property changes. The callback can be omitted when using this * mode, as it will not be used. * * @BIDIRECTIONAL: Combines the effects of `IN` and `OUT`. * * The direction of binding settings */ const BindingDirection = { IN : 1, OUT : 2, BIDIRECTIONAL : 3 }; var BOOLEAN_TYPES = { "checkbox" : { "required-fields": [ "type", "default", "description" ] }, "generic" : { "required-fields": [ "type", "default" ] } }; var STRING_TYPES = { "entry" : { "required-fields": [ "type", "default", "description" ] }, "textview" : { "required-fields": [ "type", "default", "description" ] }, "colorchooser" : { "required-fields": [ "type", "default", "description" ] }, "radiogroup" : { "required-fields": [ "type", "default", "description", "options" ] }, "filechooser" : { "required-fields": [ "type", "description", "default" ] }, "iconfilechooser" : { "required-fields": [ "type", "description", "default" ] }, "combobox" : { "required-fields": [ "type", "default", "description", "options" ] }, "tween" : { "required-fields": [ "type", "default", "description" ] }, "keybinding" : { "required-fields": [ "type", "default", "description" ] }, "generic" : { "required-fields": [ "type", "default" ] } }; var NUMBER_TYPES = { "spinbutton" : { "required-fields": [ "type", "default", "min", "max", "units", "step", "description" ] }, "scale" : { "required-fields": [ "type", "default", "min", "max", "step", "description" ] }, "combobox" : { "required-fields": [ "type", "default", "description", "options" ] }, "radiogroup" : { "required-fields": [ "type", "default", "description", "options" ] }, "generic" : { "required-fields": [ "type", "default" ] } }; var NON_SETTING_TYPES = { "header" : { "required-fields": [ "type", "description" ] }, "separator" : { "required-fields": [ "type" ] }, "button" : { "required-fields": [ "type", "description", "callback" ] }, "label" : { "required-fields": [ "type", "description" ] } }; function _provider(xlet, uuid, instanceId, type, string) { this._init(xlet, uuid, instanceId, type, string); } /** * #_provider: * @short_description: Xlet settings object * * This is the settings object produced in the settings API. */ _provider.prototype = { _init: function (xlet, uuid, instanceId, type, string) { if (type && string) { this.ext_type = type; this.xlet_str = string; } if (!xlet) { global.logError(this.xlet_str + " constructor arguments invalid"); global.logError("First argument MUST be a(n)" + this.xlet_str + " object (use 'this' as the first argument"); return; } if (!uuid) { global.logError(this.xlet_str + "Settings constructor arguments invalid"); global.logError("Missing required UUID as second argument - should be a string:"); global.logError("@.org or something similar"); return; } this.uuid = uuid; this.xlet = xlet; if (!instanceId && this.ext_type != Extension.Type.EXTENSION) { global.logWarning(this.xlet_str + "Settings constructor arguments warning"); global.logWarning("Missing instance ID as third argument"); global.logWarning("The UUID is " + this.uuid); } this.instanceId = instanceId; this.valid = false; this.applet_dir = type.maps.dirs[this.uuid]; if (!this.applet_dir) { global.logError("Could not find installation directory for " + this.uuid); return; } this.multi_instance = this._get_is_multi_instance_xlet(this.uuid); if (this.multi_instance && this.instanceId == undefined) { global.logError(this.xlet_str + "Settings fatal error!"); global.logError("Multi-instanciable xlet with no instance ID supplied"); global.logError("The UUID is " + this.uuid); return; } let fn = this.multi_instance ? instanceId : uuid; let setting_path = (GLib.get_home_dir() + "/" + ".cinnamon" + "/" + "configs" + "/" + this.uuid + "/" + fn + ".json"); this.settings_file = Gio.file_new_for_path(setting_path); // Set up working settings file as ~/.cinnamon/applet_config//.json // If it already exists, check for updates to it (new keys, etc..) if (!this.settings_file.query_exists(null)) { if (!this._create_settings_file()) { global.logError("Problem initializing settings for " + this.uuid); return; } } else { if (!this._maybe_update_settings_file()) { global.logError("Problem updating settings for " + this.uuid); return; } } this.metaBindings = {} if (!this.multi_instance) { this.instanceId = this.uuid; } this.settings_obj = new SettingObj(this, this.settings_file, this.uuid, this.instanceId); this.valid = true; Main.settingsManager.register(this.uuid, this.instanceId, this); }, _get_is_multi_instance_xlet: function(uuid) { return false; }, _create_settings_file: function () { if (!this.settings_file.get_parent().query_exists(null)) { this.settings_file.get_parent().make_directory_with_parents(null) } let orig_file = this.applet_dir.get_child(SETTING_SCHEMA_FILE); if (!orig_file.query_exists(null)) { global.logError("Failed to locate settings schema file for " + this.uuid); return false; } let init_file_contents = Cinnamon.get_file_contents_utf8_sync(orig_file.get_path()); let checksum = global.get_md5_for_string(init_file_contents); let init_json try { init_json = JSON.parse(init_file_contents); } catch (e) { global.logError("Cannot parse settings schema file for %s" + this.uuid + ". Check the structure for missing commas, etc... Error is: " + e); return false; } if (!this._json_validity_check(init_json)) { global.logError("Initial settings schema file is not valid for " + this.uuid); return false; } for (let key in init_json) { init_json[key]["value"] = init_json[key]["default"] } init_json["__md5__"] = checksum; let f = Gio.file_new_for_path(this.settings_file.get_path()); let raw = f.replace(null, false, Gio.FileCreateFlags.NONE, null); let out_file = Gio.BufferedOutputStream.new_sized (raw, 4096); Cinnamon.write_string_to_stream(out_file, JSON.stringify(init_json, null, 4)); out_file.close(null); return true; }, _json_validity_check: function (init_json) { let valid = false; for (let primary_key in init_json) { valid = this._check_for_min_props(init_json[primary_key]) if (!valid) break; } return valid; }, _check_for_min_props: function (node) { if (node["type"] in BOOLEAN_TYPES) { for (let req_field in BOOLEAN_TYPES[node["type"]]["required-fields"]) { if (BOOLEAN_TYPES[node["type"]]["required-fields"][req_field] in node) { continue; } else { return false; } } return true; } else if (node["type"] in STRING_TYPES) { for (let req_field in STRING_TYPES[node["type"]]["required-fields"]) { if (STRING_TYPES[node["type"]]["required-fields"][req_field] in node) { continue; } else { return false; } } return true; } else if (node["type"] in NUMBER_TYPES) { for (let req_field in NUMBER_TYPES[node["type"]]["required-fields"]) { if (NUMBER_TYPES[node["type"]]["required-fields"][req_field] in node) { continue; } else { return false; } } return true; } else if (node["type"] in NON_SETTING_TYPES) { for (let req_field in NON_SETTING_TYPES[node["type"]]["required-fields"]) { if (NON_SETTING_TYPES[node["type"]]["required-fields"][req_field] in node) { continue; } else { return false; } } return true; } else { return false; } }, _maybe_update_settings_file: function () { let orig_file = this.applet_dir.get_child(SETTING_SCHEMA_FILE); if (!orig_file.query_exists(null)) { global.logWarning("Failed to locate settings schema file to check for updates: " + this.uuid); global.logWarning("Something may not be right"); return false; } let init_file_contents = Cinnamon.get_file_contents_utf8_sync(orig_file.get_path()); let checksum = global.get_md5_for_string(init_file_contents); let existing_settings_file = Cinnamon.get_file_contents_utf8_sync(this.settings_file.get_path()); let existing_json; let new_json; try { new_json = JSON.parse(init_file_contents); } catch (e) { global.logError("Problem parsing " + orig_file.get_path() + " while preparing to perform upgrade."); global.logError("Skipping upgrade for now - something may be wrong with the new settings schema file."); return false; } try { existing_json = JSON.parse(existing_settings_file); } catch (e) { global.logError("Problem parsing " + this.settings_file.get_path() + " while preparing to perform upgrade."); global.log("Re-creating settings file."); this.settings_file.delete(null, null); return this._create_settings_file(); } if (existing_json["__md5__"] != checksum) { global.log("Updated settings file detected for " + this.uuid + ". Beginning upgrade of existing settings"); return this._do_upgrade(new_json, existing_json, checksum); } else { return true; } }, _do_upgrade: function(new_json, old_json, checksum) { // First, check the new json for validity if (!this._json_validity_check(new_json)) { global.logError("Upgraded settings schema file is NOT valid for " + this.uuid); global.logError("Aborting settings upgrade."); return false; } /* We're going to iterate through all the keys in the new settings file * Where the key names and types match up, we'll check the current value against * the new max/mins or other factors (if applicable) and add the 'value' key to the new file. * * If the old setting-key doesn't exist in the new file, we'll drop it entirely. * If there are new keys, we'll assign the default value like normal. */ for (let key in new_json) { if (key in old_json) { if (new_json[key]["type"] == old_json[key]["type"]) { if (this._sanity_check(old_json[key]["value"], new_json[key])) { new_json[key]["value"] = old_json[key]["value"]; } else { new_json[key]["value"] = new_json[key]["default"]; } } else { new_json[key]["value"] = new_json[key]["default"]; } } else { new_json[key]["value"] = new_json[key]["default"]; } } new_json["__md5__"] = checksum; if (this.settings_file.delete(null, null)) { let f = Gio.file_new_for_path(this.settings_file.get_path()); let raw = f.replace(null, false, Gio.FileCreateFlags.NONE, null); let out_file = Gio.BufferedOutputStream.new_sized (raw, 4096); Cinnamon.write_string_to_stream(out_file, JSON.stringify(new_json, null, 4)); out_file.close(null); global.log("Upgrade complete"); return true; } else { global.logError("Failed to gain write access to save updated settings for " + this.uuid + "..." + this.instanceId) return false; } }, _sanity_check: function(val, setting) { let found; switch (setting["type"]) { case "spinbutton": case "scale": return (val < setting["max"] && val > setting["min"]); break; case "combobox": found = false; for (let opt in setting["options"]) { if (val == setting["options"][opt]) { found = true; break; } } return found; break; case "radiogroup": found = false; for (let opt in setting["options"]) { if (val == setting["options"][opt] || setting["options"][opt] == "custom") { found = true; break } } return found; break; default: return true; break; } return true; }, remote_set: function (key, payload) { let node = JSON.parse(payload); this.settings_obj.set_node_from_dbus(key, node); }, get_file_path: function() { return this.settings_file.get_path(); }, /* _settings_file_changed: For convenience only, if you want to handle * updating your applet props yourself, connect to this signal on your * AppletSettings object to get notified when the json file changes, * then you can call AppletSettings.getValue(key) to update your props */ _setting_file_changed_notify: function() { this.emit("settings-changed"); }, /* individual key notification, sends old and new value with signal */ _value_changed_notify: function(key, oldval, newval) { this.emit("changed::" + key, key, oldval, newval); }, /** * bindProperty: * @sync_type (Settings.BindingDirection): the direction of the binding * @key_name (string): the id of the setting * @applet_var (string): the applet's property that is used to hold the * setting (eg. `this.value` passes as `"value`") * @applet_callback (function): (optional) the applet method to call * when the setting has changed and the new values set * @user_data: (optional) any extra data/object you wish to pass to the callback * * Bind an applet proprety/varaible to a setting * * Returns (boolean): Whether the bind was successful */ bindProperty: function (sync_type, key_name, applet_var, applet_callback, user_data) { if (!this.valid) { settings_not_initialized_error(this.uuid); return false; } let type = this.settings_obj.get_key_exists_and_type(key_name); if (!applet_callback) applet_callback = function() {}; if (type) { if (type in BOOLEAN_TYPES || type in STRING_TYPES || type in NUMBER_TYPES) { this.metaBindings[key_name] = new _setting(sync_type, this.xlet, key_name, this.settings_obj, applet_var, Lang.bind (this.xlet, applet_callback), user_data); return true; } else { invalid_setting_type_error(key_name, this.uuid, type); return false; } } else { key_not_found_error(key_name, this.uuid); return false; } }, /** * unbindProperty: * @key_name (string): the id of the setting * * Reverses the effect of %bindProperty. * * Returns (boolean): Whether the unbind was successful. */ unbindProperty: function (key_name) { if (this.metaBindings[key_name]) { this.metaBindings[key_name].finalize(); this.metaBindings[key_name] = undefined; return true; } global.logError("unbindProperty failed for " + this.uuid + ". Key name '" + key_name + "' did not exist."); return false; }, /** * finalize: * * Destroys the setting object. */ finalize: function () { this.settings_obj.finalize(); for (let setting in this.metaBindings) { this.metaBindings[setting].finalize(); } this.metaBindings = undefined; this.settings_obj = undefined; Main.settingsManager.unregister(this.uuid, this.instanceId); }, /** * getValue: * @key_name (String): the key name to fetch the value for * * Returns the currently stored value of the key `key_name` * * Returns: The currently stored value of the key */ getValue: function (key_name) { if (key_name in this.settings_obj.json) { return this.settings_obj.get_data(key_name)["value"]; } else { key_not_found_error(key_name, this.uuid); return null; } }, /** * setValue: * @key_name (string): the key name to set the value for * @value: the new value * * Sets the value of @key_name to @value. */ setValue: function (key_name, value) { if (key_name in this.settings_obj.json) { let oldval = this.settings_obj.get_data(key_name)["value"]; if (oldval != value) { this.settings_obj.set_value(key_name, value); } } else { key_not_found_error(key_name, this.uuid); } }, /** * getOptions: * @key_name (String): the key name to fetch the options for * * Returns the current options for the key @key_name. * * Returns: The currently stored options of the key (undefined if the key does not support options) */ getOptions: function (key_name) { if (key_name in this.settings_obj.json) { return this.settings_obj.get_data(key_name)["options"]; } else { key_not_found_error(key_name, this.uuid); return null; } }, /** * setOptions: * @key_name (string): the key name to set the options for * @options: the new options to set * * If @key_name is a key type that supports options, sets the options of @key_name to @options. */ setOptions: function (key_name, options) { if (key_name in this.settings_obj.json) { let oldval = this.settings_obj.get_data(key_name)["options"]; if (!oldval) { global.logWarning("Could not set options for '" + key_name + "' - the key does not support options"); return; } if (oldval != options) { this.settings_obj.set_options(key_name, options); } } else { key_not_found_error(key_name, this.uuid); } } }; Signals.addSignalMethods(_provider.prototype); function settings_not_initialized_error(uuid) { global.logError("Could not set up binding - settings object was not initialized successfully for " + uuid); } function key_not_found_error (key_name, uuid) { global.logError("Could not find setting key '" + key_name + "' for applet/desklet uuid " + uuid); } function invalid_setting_type_error (key_name, uuid, type) { global.logError("Invalid setting type '" + type + "'' for setting key '" + key_name + "' of applet/desklet uuid " + uuid); } /* SettingObj maintains the working json data, updates, writes, * retrieves, etc.. to the individual settings during runtime */ function SettingObj(provider, file, uuid, instanceId) { this._init(provider, file, uuid, instanceId); } SettingObj.prototype = { _init: function (provider, file, uuid, instanceId) { this.provider = provider; this.file = file; this.uuid = uuid; this.instanceId = instanceId; let raw_file = Cinnamon.get_file_contents_utf8_sync(this.file.get_path()); this.json = JSON.parse(raw_file); this.settings_file_monitor = this.file.monitor_file(Gio.FileMonitorFlags.NONE, null); this.monitor_id = this.settings_file_monitor.connect('changed', Lang.bind(this, this._on_file_changed)); this.file_changed_timeout = null; }, save: function() { this.settings_file_monitor.disconnect(this.monitor_id); if (this.file.delete(null, null)) { let f = Gio.file_new_for_path(this.file.get_path()); let raw = f.replace(null, false, Gio.FileCreateFlags.NONE, null); let out_file = Gio.BufferedOutputStream.new_sized (raw, 4096); Cinnamon.write_string_to_stream(out_file, JSON.stringify(this.json, null, 4)); out_file.close(null); } else { global.logError("Failed gain write access to settings file for applet/desklet '" + this.uuid + "', instance ") + this.instanceId; } this.monitor_id = this.settings_file_monitor.connect('changed', Lang.bind(this, this._on_file_changed)); }, get_data: function(key) { return this.json[key]; }, get_key_exists_and_type: function(key) { if (key in this.json) { return this.json[key]["type"]; } else { return null; } }, set_value: function(key, val) { this.json[key]["value"] = val; this.save(); }, set_options: function(key, val) { this.json[key]["options"] = val; this.save(); }, set_node_from_dbus: function(key, node) { let different = false; let old_val = this.json[key]['value']; let new_val = node['value'] if (old_val != new_val) { this.json[key] = node; this.provider._value_changed_notify(key, old_val, new_val); this.emit("setting-file-changed"); this.provider._setting_file_changed_notify(); this.save(); // TODO: This is probably wrong to have here....should be earlier..but i'd rather do everything else first, for responsiveness } }, _on_file_changed: function() { if (this.file_changed_timeout) { Mainloop.source_remove(this.file_changed_timeout); } this.file_changed_timeout = Mainloop.timeout_add(300, Lang.bind(this, this._on_file_changed_timeout)) }, _on_file_changed_timeout: function(monitor, file, n, eventType) { if (this.file.query_exists(null)) { if (eventType !== undefined && eventType != Gio.FileMonitorEvent.CHANGES_DONE_HINT) { return false; } let raw_file = Cinnamon.get_file_contents_utf8_sync(this.file.get_path()); let new_json = JSON.parse(raw_file); for (let key in new_json) { if (this.json[key]['value'] != new_json[key]['value']) { let oldval = this.json[key]['value']; let newval = new_json[key]['value']; this.json[key]['value'] = new_json[key]['value']; this.provider._value_changed_notify(key, oldval, newval); } } this.emit("setting-file-changed"); this.provider._setting_file_changed_notify(); } else { this.settings_file_monitor.disconnect(this.monitor_id); } this.file_changed_timeout = null; return false; }, finalize: function() { if (this.file_changed_timeout) { Mainloop.source_remove(this.file_changed_timeout); } if (this.monitor_id > 0) { this.settings_file_monitor.disconnect(this.monitor_id); } this.settings_file_monitor = null; } }; Signals.addSignalMethods(SettingObj.prototype); // Individual setting types function _setting(sync_type, obj, key_name, settings_obj, applet_var, applet_callback, user_data) { this._init(sync_type, obj, key_name, settings_obj, applet_var, applet_callback, user_data); } _setting.prototype = { _init: function (sync_type, obj, key_name, settings_obj, applet_var, applet_callback, user_data) { this.key_name = key_name; this.settings_obj = settings_obj; this.applet_var = applet_var; this.sync_type = sync_type; obj[applet_var] = this.settings_obj.get_data(this.key_name)["value"]; this.obj = obj; this.cb = applet_callback; this.settings_obj_connection_id = null; if (user_data) { this.user_data = user_data; } if (this.sync_type != BindingDirection.OUT) { this.settings_obj_connection_id = this.settings_obj.connect("setting-file-changed", Lang.bind(this, this._setting_file_changed)); } this._monitor_applet_var(true); }, _monitor_applet_var: function (state) { if (this.sync_type < BindingDirection.OUT) return; state ? this.obj.watch(this.applet_var, Lang.bind(this, this._on_applet_changed_value)) : this.obj.unwatch(this.applet_var); //add a save function for objects or arrays if(typeof(this.obj[this.applet_var]) === "object" && !this.obj[this.applet_var].save){ this.obj[this.applet_var].save = Lang.bind(this, this.set_val, this.obj[this.applet_var]); } }, _on_applet_changed_value: function (obj, oldval, newval) { this.set_val(newval); return newval; }, _setting_file_changed: function () { if (this.sync_type != BindingDirection.OUT) { this.set_applet_var_and_cb(this.get_val()); } }, set_applet_var_and_cb: function (new_val) { if (new_val != this.obj[this.applet_var]) { this._monitor_applet_var(false); this.obj[this.applet_var] = new_val; if (this.user_data) { this.cb(this.user_data); } else { this.cb(); } this._monitor_applet_var(true); } }, get_data: function() { return this.settings_obj.get_data(this.key_name); }, get_val: function() { return this.settings_obj.get_data(this.key_name)["value"]; }, set_val: function(val) { this.settings_obj.set_value(this.key_name, val); }, finalize: function() { this._monitor_applet_var(false); if (this.settings_obj_connection_id) { this.settings_obj.disconnect(this.settings_obj_connection_id); this.settings_obj_connection_id = null; } this.settings_obj = null; this.applet_var = null; this.sync_type = null; this.obj = null; this.cb = null; this.user_data = null; } }; /** * #AppletSettings: * @short_description: Settings object for applets. * * Inherits: Settings._provider */ function AppletSettings(xlet, uuid, instanceId) { this._init(xlet, uuid, instanceId); } AppletSettings.prototype = { __proto__: _provider.prototype, /** * _init: * @xlet (Object): the object variables are binded to (usually `this`) * @uuid (string): uuid of the applet * @instanceId (int): instance id of the applet */ _init: function (xlet, uuid, instanceId) { _provider.prototype._init.call(this, xlet, uuid, instanceId, Extension.Type.APPLET, "Applet"); }, _get_is_multi_instance_xlet: function(uuid) { return Extension.get_max_instances(uuid, this.ext_type) != 1; }, }; /** * #DeskletSettings: * @short_description: Settings object for desklets. * * Inherits: Settings._provider */ function DeskletSettings(xlet, uuid, instanceId) { this._init(xlet, uuid, instanceId); } DeskletSettings.prototype = { __proto__: _provider.prototype, /** * _init: * @xlet (Object): the object variables are binded to (usually `this`) * @uuid (string): uuid of the desklet * @instanceId (int): instance id of the desklet */ _init: function (xlet, uuid, instanceId) { _provider.prototype._init.call(this, xlet, uuid, instanceId, Extension.Type.DESKLET, "Desklet"); }, _get_is_multi_instance_xlet: function(uuid) { return Extension.get_max_instances(uuid, this.ext_type) > 1; } }; /** * #ExtensionSettings: * @short_description: Settings object for extensions. * * Inherits: Settings._provider */ function ExtensionSettings(xlet, uuid) { this._init(xlet, uuid); } ExtensionSettings.prototype = { __proto__: _provider.prototype, /** * _init: * @xlet (Object): the object variables are binded to (usually `this`) * @uuid (string): uuid of the extension */ _init: function (xlet, uuid) { _provider.prototype._init.call(this, xlet, uuid, null, Extension.Type.EXTENSION, "Extension"); }, _get_is_multi_instance_xlet: function(uuid) { return false; } }; function SettingsManager() { this._init(); } SettingsManager.prototype = { _init: function () { this.uuids = {}; }, register: function (uuid, instance_id, obj) { if (!(uuid in this.uuids)) this.uuids[uuid] = {} this.uuids[uuid][instance_id] = obj; }, unregister: function (uuid, instance_id) { this.uuids[uuid][instance_id] = null; } }; Cinnamon-2.8.6/js/ui/notificationDaemon.js0000664000175000017500000006637012625666430017444 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Config = imports.misc.config; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Params = imports.misc.params; const Mainloop = imports.mainloop; let nextNotificationId = 1; // Should really be defined in Gio.js const BusIface = ' \ \ \ \ \ \ \ '; var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface); function Bus() { return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus'); } const NotificationDaemonIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const NotificationClosedReason = { EXPIRED: 1, DISMISSED: 2, APP_CLOSED: 3, UNDEFINED: 4 }; const Urgency = { LOW: 0, NORMAL: 1, CRITICAL: 2 }; const rewriteRules = { 'XChat': [ { pattern: /^XChat: Private message from: (\S*) \(.*\)$/, replacement: '<$1>' }, { pattern: /^XChat: New public message from: (\S*) \((.*)\)$/, replacement: '$2 <$1>' }, { pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/, replacement: '$2 <$1>' } ] }; function NotificationDaemon() { this._init(); } NotificationDaemon.prototype = { _init: function() { this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this); this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications'); this._sources = []; this._senderToPid = {}; this._notifications = {}; this._expireNotifications = []; // List of expiring notifications in order from first to last to expire. this._busProxy = new Bus(); this._expireTimer = 0; Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded)); Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); // Settings this.settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.notifications" }); function setting(self, source, type, camelCase, dashed) { function updater() { self[camelCase] = source["get_"+type](dashed); } source.connect('changed::'+dashed, updater); updater(); } setting(this, this.settings, "boolean", "removeOld", "remove-old"); setting(this, this.settings, "int", "timeout", "timeout"); Cinnamon.WindowTracker.get_default().connect('notify::focus-app', Lang.bind(this, this._onFocusAppChanged)); Main.overview.connect('hidden', Lang.bind(this, this._onFocusAppChanged)); }, // Create an icon for a notification from icon string/path. _iconForNotificationData: function(icon, hints, size) { let textureCache = St.TextureCache.get_default(); // If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon // and don't show a large image. There are currently many applications that use // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets // the 'image-data' hint. These applications don't typically pass in 'app_icon' // argument to Notify() and actually expect the pixbuf to be shown as an icon. // So the logic here does the right thing for this case. If both an icon and either // one of 'image-data' or 'image-path' are specified, we show both an icon and // a large image. if (icon) { if (icon.substr(0, 7) == 'file://') return textureCache.load_uri_async(icon, size, size); else if (icon[0] == '/') { let uri = GLib.filename_to_uri(icon, null); return textureCache.load_uri_async(uri, size, size); } else return new St.Icon({ icon_name: icon, icon_type: St.IconType.FULLCOLOR, icon_size: size }); } else if (hints['image-data']) { let [width, height, rowStride, hasAlpha, bitsPerSample, nChannels, data] = hints['image-data']; return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size); } else if (hints['image-path']) { return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size); } else { let stockIcon; switch (hints.urgency) { case Urgency.LOW: case Urgency.NORMAL: stockIcon = 'gtk-dialog-info'; break; case Urgency.CRITICAL: stockIcon = 'gtk-dialog-error'; break; } return new St.Icon({ icon_name: stockIcon, icon_type: St.IconType.FULLCOLOR, icon_size: size }); } }, _lookupSource: function(title, pid, trayIcon) { for (let i = 0; i < this._sources.length; i++) { let source = this._sources[i]; if (source.pid == pid && (source.initialTitle == title || source.trayIcon == trayIcon)) return source; } return null; }, // Returns the source associated with ndata.notification if it is set. // Otherwise, returns the source associated with the title and pid if // such source is stored in this._sources and the notification is not // transient. If the existing or requested source is associated with // a tray icon and passed in pid matches a pid of an existing source, // the title match is ignored to enable representing a tray icon and // notifications from the same application with a single source. // // If no existing source is found, a new source is created as long as // pid is provided. // // Either a pid or ndata.notification is needed to retrieve or // create a source. _getSource: function(title, pid, ndata, sender, trayIcon) { if (!pid && !(ndata && ndata.notification)) return null; // We use notification's source for the notifications we still have // around that are getting replaced because we don't keep sources // for transient notifications in this._sources, but we still want // the notification associated with them to get replaced correctly. if (ndata && ndata.notification) return ndata.notification.source; let isForTransientNotification = (ndata && ndata.hints['transient'] == true); // We don't want to override a persistent notification // with a transient one from the same sender, so we // always create a new source object for new transient notifications // and never add it to this._sources . if (!isForTransientNotification) { let source = this._lookupSource(title, pid, trayIcon); if (source) { source.setTitle(title); return source; } } let source = new Source(title, pid, sender, trayIcon); source.setTransient(isForTransientNotification); if (!isForTransientNotification) { this._sources.push(source); source.connect('destroy', Lang.bind(this, function() { let index = this._sources.indexOf(source); if (index >= 0) this._sources.splice(index, 1); })); } if (Main.messageTray) Main.messageTray.add(source); return source; }, _startExpire: function() { if (this.removeOld && this._expireNotifications.length && !this._expireTimer) { this._expireTimer = Mainloop.timeout_add_seconds(Math.max((this._expireNotifications[0].expires-Date.now())/1000, 1), Lang.bind(this, this._expireNotification)); } }, _stopExpire: function() { if (this._expireTimer == 0) { return; } Mainloop.source_remove(this._expireTimer); this._expireTimer = 0; }, _restartExpire: function() { this._stopExpire(); this._startExpire(); }, _expireNotification: function() { let ndata = this._expireNotifications[0]; ndata.notification.destroy(MessageTray.NotificationDestroyedReason.EXPIRED); this._expireTimer = 0; return false; }, // Sends a notification to the notification daemon. Returns the id allocated to the notification. NotifyAsync: function(params, invocation) { let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params; let id; for (let hint in hints) { // unpack the variants hints[hint] = hints[hint].deep_unpack(); } // Special Cinnamon specific rewrites for message summaries on the fly. let rewrites = rewriteRules[appName]; if (rewrites) { for (let i = 0; i < rewrites.length; i++) { let rule = rewrites[i]; if (summary.search(rule.pattern) != -1) summary = summary.replace(rule.pattern, rule.replacement); } } hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); // Be compatible with the various hints for image data and image path // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2 if (!hints['image-path'] && hints['image_path']) hints['image-path'] = hints['image_path']; // version 1.1 of the spec if (!hints['image-data']) // not version 1.2 of the spec? if (hints['image_data']) hints['image-data'] = hints['image_data']; // version 1.1 of the spec else if (hints['icon_data'] && !hints['image-path']) // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available hints['image-data'] = hints['icon_data']; let ndata = { appName: appName, icon: icon, summary: summary, body: body, actions: actions, hints: hints, timeout: timeout }; // Does this notification replace another? if (replacesId != 0 && this._notifications[replacesId]) { ndata.id = id = replacesId; ndata.notification = this._notifications[replacesId].notification; } else { replacesId = 0; ndata.id = id = nextNotificationId++; } this._notifications[id] = ndata; // Find expiration timestamp. let expires; if (!timeout || hints.resident || hints.urgency == 2) { // Never expires. expires = ndata.expires = 0; } else if (timeout == -1) { // Default expiration. expires = ndata.expires = Date.now()+this.timeout*1000; } else { // Custom expiration. expires = ndata.expires = Date.now()+timeout; } // Does this notification expire? if (expires != 0) { // Find place in the notification queue. let notifications = this._expireNotifications, i; for (i = notifications.length; i > 0; --i) { // Backwards seach, likely to be faster. if (expires > notifications[i-1].expires) { notifications.splice(i, 0, ndata); break; } } if (i == 0) notifications.unshift(ndata); this._restartExpire() } let sender = invocation.get_sender(); let pid = this._senderToPid[sender]; let source = this._getSource(appName, pid, ndata, sender, null); if (source) { this._notifyForSource(source, ndata); return invocation.return_value(GLib.Variant.new('(u)', [id])); } if (replacesId) { // There's already a pending call to GetConnectionUnixProcessID, // which will see the new notification data when it finishes, // so we don't have to do anything. return invocation.return_value(GLib.Variant.new('(u)', [id])); } this._busProxy.GetConnectionUnixProcessIDRemote(sender, Lang.bind(this, function (result, excp) { // The app may have updated or removed the notification ndata = this._notifications[id]; if (!ndata) return; if (excp) { logError(excp, 'Call to GetConnectionUnixProcessID failed'); return; } let [pid] = result; source = this._getSource(appName, pid, ndata, sender); // We only store sender-pid entries for persistent sources. // Removing the entries once the source is destroyed // would result in the entries associated with transient // sources removed once the notification is shown anyway. // However, keeping these pairs would mean that we would // possibly remove an entry associated with a persistent // source when a transient source for the same sender is // destroyed. if (!source.isTransient) { this._senderToPid[sender] = pid; source.connect('destroy', Lang.bind(this, function() { delete this._senderToPid[sender]; })); } this._notifyForSource(source, ndata); })); return invocation.return_value(GLib.Variant.new('(u)', [id])); }, _notifyForSource: function(source, ndata) { let [id, icon, summary, body, actions, hints, notification, timeout, expires] = [ndata.id, ndata.icon, ndata.summary, ndata.body, ndata.actions, ndata.hints, ndata.notification, ndata.timeout, ndata.expires]; let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE); if (notification == null) { // Create a new notification! notification = new MessageTray.Notification(source, summary, body, { icon: iconActor, bannerMarkup: true }); ndata.notification = notification; notification.connect('destroy', Lang.bind(this, function(n, reason) { delete this._notifications[ndata.id]; let notificationClosedReason; switch (reason) { case MessageTray.NotificationDestroyedReason.EXPIRED: notificationClosedReason = NotificationClosedReason.EXPIRED; break; case MessageTray.NotificationDestroyedReason.DISMISSED: notificationClosedReason = NotificationClosedReason.DISMISSED; break; case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED: notificationClosedReason = NotificationClosedReason.APP_CLOSED; break; } // Remove from expiring? if (ndata.expires) { let notifications = this._expireNotifications; for (var i = 0, j = notifications.length; i < j; ++i) { if (notifications[i] == ndata) { notifications.splice(i, 1); break; } } this._restartExpire(); } this._emitNotificationClosed(ndata.id, notificationClosedReason); })); notification.connect('action-invoked', Lang.bind(this, function(n, actionId) { this._emitActionInvoked(ndata.id, actionId); })); } else { notification.update(summary, body, { icon: iconActor, bannerMarkup: true, clear: true }); } // We only display a large image if an icon is also specified. if (icon && (hints['image-data'] || hints['image-path'])) { let image = null; if (hints['image-data']) { let [width, height, rowStride, hasAlpha, bitsPerSample, nChannels, data] = hints['image-data']; image = St.TextureCache.get_default().load_from_raw(data, hasAlpha, width, height, rowStride, notification.IMAGE_SIZE); } else if (hints['image-path']) { image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null), notification.IMAGE_SIZE, notification.IMAGE_SIZE); } notification.setImage(image); } else { notification.unsetImage(); } if (actions.length) { notification.setUseActionIcons(hints['action-icons'] == true); for (let i = 0; i < actions.length - 1; i += 2) { if (actions[i] == 'default') notification.connect('clicked', Lang.bind(this, function() { this._emitActionInvoked(ndata.id, "default"); })); else notification.addButton(actions[i], actions[i + 1]); } } switch (hints.urgency) { case Urgency.LOW: notification.setUrgency(MessageTray.Urgency.LOW); break; case Urgency.NORMAL: notification.setUrgency(MessageTray.Urgency.NORMAL); break; case Urgency.CRITICAL: notification.setUrgency(MessageTray.Urgency.CRITICAL); break; } notification.setResident(hints.resident == true); // 'transient' is a reserved keyword in JS, so we have to retrieve the value // of the 'transient' hint with hints['transient'] rather than hints.transient notification.setTransient(hints['transient'] == true); let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null; source.processNotification(notification, sourceIconActor); }, CloseNotification: function(id) { let ndata = this._notifications[id]; if (ndata) { if (ndata.notification) ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED); delete this._notifications[id]; } }, GetCapabilities: function() { return [ 'actions', 'action-icons', 'body', // 'body-hyperlinks', // 'body-images', 'body-markup', // 'icon-multi', 'icon-static', 'persistence', // 'sound', ]; }, GetServerInformation: function() { return [ Config.PACKAGE_NAME, 'GNOME', Config.PACKAGE_VERSION, '1.2' ]; }, _onFocusAppChanged: function() { let tracker = Cinnamon.WindowTracker.get_default(); if (!tracker.focus_app) return; for (let i = 0; i < this._sources.length; i++) { let source = this._sources[i]; if (source.app == tracker.focus_app) { source.destroyNonResidentNotifications(); return; } } }, _emitNotificationClosed: function(id, reason) { this._dbusImpl.emit_signal('NotificationClosed', GLib.Variant.new('(uu)', [id, reason])); }, _emitActionInvoked: function(id, action) { this._dbusImpl.emit_signal('ActionInvoked', GLib.Variant.new('(us)', [id, action])); }, _onTrayIconAdded: function(o, icon) { let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon); }, _onTrayIconRemoved: function(o, icon) { let source = this._lookupSource(null, icon.pid, true); if (source) source.destroy(); } }; function Source(title, pid, sender, trayIcon) { this._init(title, pid, sender, trayIcon); } Source.prototype = { __proto__: MessageTray.Source.prototype, _init: function(title, pid, sender, trayIcon) { MessageTray.Source.prototype._init.call(this, title); this.initialTitle = title; this.pid = pid; if (sender) this._nameWatcherId = Gio.DBus.session.watch_name(sender, Gio.BusNameWatcherFlags.NONE, null, Lang.bind(this, this._onNameVanished)); else this._nameWatcherId = 0; this._setApp(); if (this.app) this.title = this.app.get_name(); else this.useNotificationIcon = true; this.trayIcon = trayIcon; if (this.trayIcon) { this._setSummaryIcon(this.trayIcon); this.useNotificationIcon = false; } }, _onNameVanished: function() { // Destroy the notification source when its sender is removed from DBus. // Only do so if this.app is set to avoid removing "notify-send" sources, senders // of which аre removed from DBus immediately. // Sender being removed from DBus would normally result in a tray icon being removed, // so allow the code path that handles the tray icon being removed to handle that case. if (!this.trayIcon && this.app) this.destroy(); }, processNotification: function(notification, icon) { if (!this.app) this._setApp(); if (!this.app && icon) this._setSummaryIcon(icon); let tracker = Cinnamon.WindowTracker.get_default(); if (notification.resident && this.app && tracker.focus_app == this.app) this.pushNotification(notification); else this.notify(notification); }, handleSummaryClick: function() { if (!this.trayIcon) return false; let event = Clutter.get_current_event(); if (event.type() != Clutter.EventType.BUTTON_RELEASE) return false; // Left clicks are passed through only where there aren't unacknowledged // notifications, so it possible to open them in summary mode; right // clicks are always forwarded, as the right click menu is not useful for // tray icons if (event.get_button() == 1 && this.notifications.length > 0) return false; if (Main.overview.visible) { // We can't just connect to Main.overview's 'hidden' signal, // because it's emitted *before* it calls popModal()... let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () { global.disconnect(id); this.trayIcon.click(event); })); Main.overview.hide(); } else { this.trayIcon.click(event); } return true; }, _getApp: function() { let app; app = Cinnamon.WindowTracker.get_default().get_app_from_pid(this.pid); if (app != null) return app; if (this.trayIcon) { app = Cinnamon.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass); if (app != null) return app; } return null; }, _setApp: function() { if (this.app) return; this.app = this._getApp(); if (!this.app) return; // Only override the icon if we were previously using // notification-based icons (ie, not a trayicon) or if it was unset before if (!this.trayIcon) { this.useNotificationIcon = false; let icon = null; if (this.app.get_app_info() != null && this.app.get_app_info().get_icon() != null) { icon = new St.Icon({gicon: this.app.get_app_info().get_icon(), icon_size: this.ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); } if (icon == null) { icon = new St.Icon({icon_name: "application-x-executable", icon_size: this.ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); } this._setSummaryIcon(icon); } }, open: function(notification) { this.destroyNonResidentNotifications(); this.openApp(); }, _lastNotificationRemoved: function() { if (!this.trayIcon) this.destroy(); }, openApp: function() { if (this.app == null) return; let windows = this.app.get_windows(); if (windows.length > 0) { let mostRecentWindow = windows[0]; Main.activateWindow(mostRecentWindow); } }, destroy: function() { if (this._nameWatcherId) { Gio.DBus.session.unwatch_name(this._nameWatcherId); this._nameWatcherId = 0; } MessageTray.Source.prototype.destroy.call(this); } }; Cinnamon-2.8.6/js/ui/lookingGlass.js0000664000175000017500000006026512625666430016263 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const Cogl = imports.gi.Cogl; const Gio = imports.gi.Gio; const Lang = imports.lang; const Meta = imports.gi.Meta; const Signals = imports.signals; const St = imports.gi.St; const System = imports.system; const Extension = imports.ui.extension; const History = imports.misc.history; const Main = imports.ui.main; /* Imports...feel free to add here as needed */ var commandHeader = 'const Clutter = imports.gi.Clutter; ' + 'const GLib = imports.gi.GLib; ' + 'const Gtk = imports.gi.Gtk; ' + 'const Mainloop = imports.mainloop; ' + 'const Meta = imports.gi.Meta; ' + 'const Cinnamon = imports.gi.Cinnamon; ' + 'const Main = imports.ui.main; ' + 'const Lang = imports.lang; ' + 'const Tweener = imports.ui.tweener; ' + /* Utility functions...we should probably be able to use these * in Cinnamon core code too. */ 'const stage = global.stage; ' + 'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' + /* Special lookingGlass functions */ 'const it = Main.lookingGlass.getIt(); ' + 'const a = Lang.bind(Main.lookingGlass, Main.lookingGlass.getWindowApp); '+ 'const w = Lang.bind(Main.lookingGlass, Main.lookingGlass.getWindow); '+ 'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); '; const HISTORY_KEY = 'looking-glass-history'; function objectToString(o) { if (typeof(o) == typeof(objectToString)) { // special case this since the default is way, way too verbose return ''; } else { return '' + o; } } function WindowList() { this._init(); } WindowList.prototype = { _init : function () { this.lastId = 0; this.latestWindowList = []; let tracker = Cinnamon.WindowTracker.get_default(); global.display.connect('window-created', Lang.bind(this, this._updateWindowList)); tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList)); }, getWindowById: function(id) { let windows = global.get_window_actors(); for (let i = 0; i < windows.length; i++) { let metaWindow = windows[i].metaWindow; if(metaWindow._lgId === id) return metaWindow; } return null; }, _updateWindowList: function() { let windows = global.get_window_actors(); let tracker = Cinnamon.WindowTracker.get_default(); let oldWindowList = this.latestWindowList; this.latestWindowList = []; for (let i = 0; i < windows.length; i++) { let metaWindow = windows[i].metaWindow; // Avoid multiple connections if (!metaWindow._lookingGlassManaged) { metaWindow.connect('unmanaged', Lang.bind(this, this._updateWindowList)); metaWindow._lookingGlassManaged = true; metaWindow._lgId = this.lastId; this.lastId++; } let lgInfo = { id: metaWindow._lgId.toString(), title: metaWindow.title, wmclass: metaWindow.get_wm_class(), app: ''}; let app = tracker.get_window_app(metaWindow); if (app != null && !app.is_window_backed()) { lgInfo.app = app.get_id(); } else { lgInfo.app = ''; } // Ignore menus let wtype = metaWindow.get_window_type(); if(wtype != Meta.WindowType.MENU && wtype != Meta.WindowType.DROPDOWN_MENU && wtype != Meta.WindowType.POPUP_MENU) this.latestWindowList.push(lgInfo); } // Make sure the list changed before notifying listeneres let changed = oldWindowList.length != this.latestWindowList.length; if(!changed) { for(let i=0; i j)) i = j; if(i != -1) shortValue = value.substr(0, i) + '.. '; } resultObj.push({ name: key, type: type, value: value, shortValue: shortValue}); } return resultObj; }, getIt: function () { return this._it; }, getResult: function(idx) { return this._results[idx].o; }, getWindow: function(idx) { return this._windowList.getWindowById(idx); }, getWindowApp: function(idx) { let metaWindow = this._windowList.getWindowById(idx) if(metaWindow) { let tracker = Cinnamon.WindowTracker.get_default(); return tracker.get_window_app(metaWindow); } return null; }, // DBus function Eval: function(command) { this._history.addItem(command); let fullCmd = commandHeader + command; let resultObj; /* Set up for some reporting about memory impact and execution speed. The performance impact of CinnamonJS.get_memory_info should be very small, whereas getting a timestamp might involve some memory allocation, so we grab the timestamp first. */ let ts = new Date().getTime(); let memInfo = global.get_memory_info(); try { resultObj = eval(fullCmd); } catch (e) { resultObj = ''; } let memInfo2 = global.get_memory_info(); let ts2 = new Date().getTime(); let tooltip = _("Memory information (Final / Diff):") + "\n"; tooltip += ' uordblks: ' + (memInfo2.glibc_uordblks) + " / " + (memInfo2.glibc_uordblks - memInfo.glibc_uordblks) + "\n" + ' js_bytes: ' + (memInfo2.js_bytes) + " / " + (memInfo2.js_bytes - memInfo.js_bytes) + "\n" + ' gjs_boxed: ' + (memInfo2.gjs_boxed) + " / " + (memInfo2.gjs_boxed - memInfo.gjs_boxed) + "\n" + ' gjs_gobject: ' + (memInfo2.gjs_gobject) + " / " + (memInfo2.gjs_gobject - memInfo.gjs_gobject) + "\n" + ' gjs_function: ' + (memInfo2.gjs_function) + " / " + (memInfo2.gjs_function - memInfo.gjs_function) + "\n" + ' gjs_closure: ' + (memInfo2.gjs_closure) + " / " + (memInfo2.gjs_closure - memInfo.gjs_closure) + "\n"; tooltip += _("Execution time (ms): ") + (ts2 - ts); this._pushResult(command, resultObj, tooltip); return; }, // DBus function GetResults: function() { return [true, this.rawResults]; }, // DBus function AddResult: function(path) { let fullCmd = commandHeader + path; let resultObj; try { resultObj = eval(fullCmd); } catch (e) { resultObj = ''; } this._pushResult(path, resultObj, ""); }, // DBus function GetErrorStack: function() { return [true, Main._errorLogStack]; }, // DBus function GetMemoryInfo: function() { let memInfo = global.get_memory_info(); let result = [ true, memInfo.last_gc_seconds_ago, { 'glibc_uordblks': (memInfo.glibc_uordblks), 'js_bytes': (memInfo.js_bytes), 'gjs_boxed': (memInfo.gjs_boxed), 'gjs_gobject': (memInfo.gjs_gobject), 'gjs_function': (memInfo.gjs_function), 'gjs_closure': (memInfo.gjs_closure) } ] return result; }, // DBus function FullGc: function() { System.gc(); }, // DBus function Inspect: function(path) { try { let result = this.inspect(path); return [true, result]; } catch (e) { global.logError('Error inspecting path: ' + path, e); return [false, []]; } }, // DBus function GetLatestWindowList: function() { try { return [true, this._windowList.latestWindowList]; } catch (e) { global.logError('Error getting latest window list', e); return [false, []]; } }, // DBus function StartInspector: function() { try { let inspector = new Inspector(); inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) { this._pushResult('', target, ""); })); inspector.connect('closed', Lang.bind(this, function() { this.emitInspectorDone(); })); } catch (e) { global.logError('Error starting inspector', e); } }, // DBus function GetExtensionList: function() { try { let extensionList = []; for (let type in Extension.Type) { type = Extension.Type[type]; for(let uuid in type.maps.meta){ let meta = type.maps.meta[uuid]; // There can be cases where we create dummy extension metadata // that's not really a proper extension. Don't bother with these. if (meta.name) { extensionList.push({ status: Extension.getMetaStateString(meta.state), name: meta.name, description: meta.description, uuid: uuid, folder: meta.path, url: meta.url ? meta.url : '', type: type.name, error_message: meta.error ? meta.error : _("Loaded successfully"), error: meta.error ? "true" : "false" // Must use string due to dbus restrictions }); } } } return [true, extensionList]; } catch (e) { global.logError('Error getting the extension list', e); return [false, []]; } }, // DBus function ReloadExtension: function(uuid, type) { Extension.reloadExtension(uuid, Extension.Type[type]); }, emitLogUpdate: function() { this._dbusImpl.emit_signal('LogUpdate', null); }, emitWindowListUpdate: function() { this._dbusImpl.emit_signal('WindowListUpdate', null); }, emitResultUpdate: function() { this._dbusImpl.emit_signal('ResultUpdate', null); }, emitInspectorDone: function() { this._dbusImpl.emit_signal('InspectorDone', null); }, emitExtensionListUpdate: function() { this._dbusImpl.emit_signal('ExtensionListUpdate', null); }, } Cinnamon-2.8.6/js/ui/magnifier.js0000664000175000017500000015171412625666430015570 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Lang = imports.lang; const Mainloop = imports.mainloop; const Signals = imports.signals; const Main = imports.ui.main; const MagnifierDBus = imports.ui.magnifierDBus; const Params = imports.misc.params; // Keep enums in sync with GSettings schemas const MouseTrackingMode = { NONE: 0, CENTERED: 1, PROPORTIONAL: 2, PUSH: 3 }; const ScreenPosition = { NONE: 0, FULL_SCREEN: 1, TOP_HALF: 2, BOTTOM_HALF: 3, LEFT_HALF: 4, RIGHT_HALF: 5 }; const MOUSE_POLL_FREQUENCY = 50; const CROSSHAIRS_CLIP_SIZE = [100, 100]; // Settings const APPLICATIONS_SCHEMA = 'org.cinnamon.desktop.a11y.applications'; const SHOW_KEY = 'screen-magnifier-enabled'; const MAGNIFIER_SCHEMA = 'org.cinnamon.desktop.a11y.magnifier'; const SCREEN_POSITION_KEY = 'screen-position'; const MAG_FACTOR_KEY = 'mag-factor'; const LENS_MODE_KEY = 'lens-mode'; const CLAMP_MODE_KEY = 'scroll-at-edges'; const MOUSE_TRACKING_KEY = 'mouse-tracking'; const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs'; const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness'; const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color'; const CROSS_HAIRS_OPACITY_KEY = 'cross-hairs-opacity'; const CROSS_HAIRS_LENGTH_KEY = 'cross-hairs-length'; const CROSS_HAIRS_CLIP_KEY = 'cross-hairs-clip'; const KEYBINDING_SCHEMA = "org.cinnamon.desktop.keybindings" const ZOOM_IN_KEY = "magnifier-zoom-in" const ZOOM_OUT_KEY = "magnifier-zoom-out" let magDBusService = null; let magInputHandler = null; function Magnifier() { this._init(); } Magnifier.prototype = { _init: function() { // Magnifier is a manager of ZoomRegions. this._zoomRegions = []; this.enabled = false; this._appSettings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA }); this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA }); // Create small clutter tree for the magnified mouse. let xfixesCursor = Cinnamon.XFixesCursor.get_for_stage(global.stage); this._mouseSprite = new Clutter.Texture(); xfixesCursor.update_texture_image(this._mouseSprite); this._cursorRoot = new Clutter.Group(); this._cursorRoot.add_actor(this._mouseSprite); // Create the first ZoomRegion and initialize it according to the // magnification settings. let mask; [this.xMouse, this.yMouse, mask] = global.get_pointer(); let aZoomRegion = new ZoomRegion(this, this._cursorRoot); this._zoomRegions.push(aZoomRegion); let activeAtLaunch = this._settingsInit(aZoomRegion); aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse); xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite)); this._xfixesCursor = xfixesCursor; this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() { this.enabled = this._appSettings.get_boolean(SHOW_KEY); let factor = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); this.setActive(this.enabled && factor > 1.0); })); this.enabled = this._appSettings.get_boolean(SHOW_KEY); // Export to dbus. magDBusService = new MagnifierDBus.CinnamonMagnifier(this.enabled); this.setActive(this.enabled && activeAtLaunch); magInputHandler = new MagnifierInputHandler(this); this.update_mag_id = 0; }, /** * showSystemCursor: * Show the system mouse pointer. */ showSystemCursor: function() { this._xfixesCursor.show(); }, /** * hideSystemCursor: * Hide the system mouse pointer. */ hideSystemCursor: function() { this._xfixesCursor.hide(); }, /** * setActive: * Show/hide all the zoom regions. * @activate: Boolean to activate or de-activate the magnifier. */ setActive: function(activate) { this._zoomRegions.forEach (function(zoomRegion, index, array) { zoomRegion.setActive(activate); }); if (activate) this.startTrackingMouse(); else this.stopTrackingMouse(); // Make sure system mouse pointer is shown when all zoom regions are // invisible. if (!activate) this._xfixesCursor.show(); // Notify interested parties of this change this.emit('active-changed', activate); }, _write_back_mag_factor: function(factor) { this._settings.set_double(MAG_FACTOR_KEY, factor); this.update_mag_id = 0; return false; }, /** * setMagFactor: * @xMagFactor: The power to set the horizontal magnification factor to * of the magnified view. A value of 1.0 means no * magnification. A value of 2.0 doubles the size. * @yMagFactor: The power to set the vertical magnification factor to * of the magnified view. */ setMagFactor: function(xMagFactor, yMagFactor) { let zr = this.getZoomRegions()[0]; zr.setMagFactor(xMagFactor, yMagFactor); if (this.update_mag_id > 0) { Mainloop.source_remove (this.update_mag_id); this.update_mag_id = 0; } this.update_mag_id = Mainloop.timeout_add(1000, Lang.bind(this, this._write_back_mag_factor, xMagFactor)); }, /** * isActive: * @return Whether the magnifier is active (boolean). */ isActive: function() { // Sufficient to check one ZoomRegion since Magnifier's active // state applies to all of them. if (this._zoomRegions.length == 0) return false; else return this._zoomRegions[0].isActive(); }, /** * startTrackingMouse: * Turn on mouse tracking, if not already doing so. */ startTrackingMouse: function() { if (!this._mouseTrackingId) this._mouseTrackingId = Mainloop.timeout_add( MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos) ); }, /** * stopTrackingMouse: * Turn off mouse tracking, if not already doing so. */ stopTrackingMouse: function() { if (this._mouseTrackingId) Mainloop.source_remove(this._mouseTrackingId); this._mouseTrackingId = null; }, /** * isTrackingMouse: * Is the magnifier tracking the mouse currently? */ isTrackingMouse: function() { return !!this._mouseTrackingId; }, /** * scrollToMousePos: * Position all zoom regions' ROI relative to the current location of the * system pointer. * @return true. */ scrollToMousePos: function() { let [xMouse, yMouse, mask] = global.get_pointer(); if (xMouse != this.xMouse || yMouse != this.yMouse) { this.xMouse = xMouse; this.yMouse = yMouse; let sysMouseOverAny = false; this._zoomRegions.forEach(function(zoomRegion, index, array) { if (zoomRegion.scrollToMousePos()) sysMouseOverAny = true; }); if (sysMouseOverAny) this.hideSystemCursor(); else this.showSystemCursor(); } return true; }, /** * createZoomRegion: * Create a ZoomRegion instance with the given properties. * @xMagFactor: The power to set horizontal magnification of the * ZoomRegion. A value of 1.0 means no magnification. A * value of 2.0 doubles the size. * @yMagFactor: The power to set the vertical magnification of the * ZoomRegion. * @roi Object in the form { x, y, width, height } that * defines the region to magnify. Given in unmagnified * coordinates. * @viewPort Object in the form { x, y, width, height } that defines * the position of the ZoomRegion on screen. * @return The newly created ZoomRegion. */ createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) { let zoomRegion = new ZoomRegion(this, this._cursorRoot); zoomRegion.setViewPort(viewPort); // We ignore the redundant width/height on the ROI let fixedROI = new Object(roi); fixedROI.width = viewPort.width / xMagFactor; fixedROI.height = viewPort.height / yMagFactor; zoomRegion.setROI(fixedROI); zoomRegion.addCrosshairs(this._crossHairs); return zoomRegion; }, /** * addZoomRegion: * Append the given ZoomRegion to the list of currently defined ZoomRegions * for this Magnifier instance. * @zoomRegion: The zoomRegion to add. */ addZoomRegion: function(zoomRegion) { if(zoomRegion) { this._zoomRegions.push(zoomRegion); if (!this.isTrackingMouse()) this.startTrackingMouse(); } }, /** * getZoomRegions: * Return a list of ZoomRegion's for this Magnifier. * @return: The Magnifier's zoom region list (array). */ getZoomRegions: function() { return this._zoomRegions; }, /** * clearAllZoomRegions: * Remove all the zoom regions from this Magnfier's ZoomRegion list. */ clearAllZoomRegions: function() { for (let i = 0; i < this._zoomRegions.length; i++) this._zoomRegions[i].setActive(false); this._zoomRegions.length = 0; this.stopTrackingMouse(); this.showSystemCursor(); }, /** * addCrosshairs: * Add and show a cross hair centered on the magnified mouse. */ addCrosshairs: function() { if (!this._crossHairs) this._crossHairs = new Crosshairs(); let thickness = this._settings.get_int(CROSS_HAIRS_THICKNESS_KEY); let color = this._settings.get_string(CROSS_HAIRS_COLOR_KEY); let opacity = this._settings.get_double(CROSS_HAIRS_OPACITY_KEY); let length = this._settings.get_int(CROSS_HAIRS_LENGTH_KEY); let clip = this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY); this.setCrosshairsThickness(thickness); this.setCrosshairsColor(color); this.setCrosshairsOpacity(opacity); this.setCrosshairsLength(length); this.setCrosshairsClip(clip); let theCrossHairs = this._crossHairs; this._zoomRegions.forEach (function(zoomRegion, index, array) { zoomRegion.addCrosshairs(theCrossHairs); }); }, /** * setCrosshairsVisible: * Show or hide the cross hair. * @visible Flag that indicates show (true) or hide (false). */ setCrosshairsVisible: function(visible) { if (visible) { if (!this._crossHairs) this.addCrosshairs(); this._crossHairs.show(); } else { if (this._crossHairs) this._crossHairs.hide(); } }, /** * setCrosshairsColor: * Set the color of the crosshairs for all ZoomRegions. * @color: The color as a string, e.g. '#ff0000ff' or 'red'. */ setCrosshairsColor: function(color) { if (this._crossHairs) { let clutterColor = new Clutter.Color(); clutterColor.from_string(color); this._crossHairs.setColor(clutterColor); } }, /** * getCrosshairsColor: * Get the color of the crosshairs. * @return: The color as a string, e.g. '#0000ffff' or 'blue'. */ getCrosshairsColor: function() { if (this._crossHairs) { let clutterColor = this._crossHairs.getColor(); return clutterColor.to_string(); } else return '#00000000'; }, /** * setCrosshairsThickness: * Set the crosshairs thickness for all ZoomRegions. * @thickness: The width of the vertical and horizontal lines of the * crosshairs. */ setCrosshairsThickness: function(thickness) { if (this._crossHairs) this._crossHairs.setThickness(thickness); }, /** * getCrosshairsThickness: * Get the crosshairs thickness. * @return: The width of the vertical and horizontal lines of the * crosshairs. */ getCrosshairsThickness: function() { if (this._crossHairs) return this._crossHairs.getThickness(); else return 0; }, /** * setCrosshairsOpacity: * @opacity: Value between 0.0 (transparent) and 1.0 (fully opaque). */ setCrosshairsOpacity: function(opacity) { if (this._crossHairs) this._crossHairs.setOpacity(opacity * 255); }, /** * getCrosshairsOpacity: * @return: Value between 0.0 (transparent) and 1.0 (fully opaque). */ getCrosshairsOpacity: function() { if (this._crossHairs) return this._crossHairs.getOpacity() / 255.0; else return 0.0; }, /** * setCrosshairsLength: * Set the crosshairs length for all ZoomRegions. * @length: The length of the vertical and horizontal lines making up the * crosshairs. */ setCrosshairsLength: function(length) { if (this._crossHairs) this._crossHairs.setLength(length); }, /** * getCrosshairsLength: * Get the crosshairs length. * @return: The length of the vertical and horizontal lines making up the * crosshairs. */ getCrosshairsLength: function() { if (this._crossHairs) return this._crossHairs.getLength(); else return 0; }, /** * setCrosshairsClip: * Set whether the crosshairs are clipped at their intersection. * @clip: Flag to indicate whether to clip the crosshairs. */ setCrosshairsClip: function(clip) { if (clip) { if (this._crossHairs) this._crossHairs.setClip(CROSSHAIRS_CLIP_SIZE); } else { // Setting no clipping on crosshairs means a zero sized clip // rectangle. if (this._crossHairs) this._crossHairs.setClip([0, 0]); } }, /** * getCrosshairsClip: * Get whether the crosshairs are clipped by the mouse image. * @return: Whether the crosshairs are clipped. */ getCrosshairsClip: function() { if (this._crossHairs) { let [clipWidth, clipHeight] = this._crossHairs.getClip(); return (clipWidth > 0 && clipHeight > 0); } else return false; }, //// Private methods //// _updateMouseSprite: function() { this._xfixesCursor.update_texture_image(this._mouseSprite); let xHot = this._xfixesCursor.get_hot_x(); let yHot = this._xfixesCursor.get_hot_y(); this._mouseSprite.set_anchor_point(xHot, yHot); }, _settingsInit: function(zoomRegion) { let ret = 1.0; if (zoomRegion) { // Mag factor is accurate to two decimal places. let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); ret = aPref; if (aPref > 1.0) zoomRegion.setMagFactor(aPref, aPref); aPref = this._settings.get_enum(SCREEN_POSITION_KEY); if (aPref) zoomRegion.setScreenPosition(aPref); zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY)); aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); if (aPref) zoomRegion.setMouseTrackingMode(aPref); } let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); this.addCrosshairs(); this.setCrosshairsVisible(showCrosshairs); this._settings.connect('changed::' + SCREEN_POSITION_KEY, Lang.bind(this, this._updateScreenPosition)); this._settings.connect('changed::' + MAG_FACTOR_KEY, Lang.bind(this, this._updateMagFactor)); this._settings.connect('changed::' + LENS_MODE_KEY, Lang.bind(this, this._updateLensMode)); this._settings.connect('changed::' + CLAMP_MODE_KEY, Lang.bind(this, this._updateClampMode)); this._settings.connect('changed::' + MOUSE_TRACKING_KEY, Lang.bind(this, this._updateMouseTrackingMode)); this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY, Lang.bind(this, function() { this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY)); })); this._settings.connect('changed::' + CROSS_HAIRS_THICKNESS_KEY, Lang.bind(this, function() { this.setCrosshairsThickness(this._settings.get_int(CROSS_HAIRS_THICKNESS_KEY)); })); this._settings.connect('changed::' + CROSS_HAIRS_COLOR_KEY, Lang.bind(this, function() { this.setCrosshairsColor(this._settings.get_string(CROSS_HAIRS_COLOR_KEY)); })); this._settings.connect('changed::' + CROSS_HAIRS_OPACITY_KEY, Lang.bind(this, function() { this.setCrosshairsOpacity(this._settings.get_double(CROSS_HAIRS_OPACITY_KEY)); })); this._settings.connect('changed::' + CROSS_HAIRS_LENGTH_KEY, Lang.bind(this, function() { this.setCrosshairsLength(this._settings.get_int(CROSS_HAIRS_LENGTH_KEY)); })); this._settings.connect('changed::' + CROSS_HAIRS_CLIP_KEY, Lang.bind(this, function() { this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY)); })); return ret > 1.0; }, _updateScreenPosition: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { let position = this._settings.get_enum(SCREEN_POSITION_KEY); this._zoomRegions[0].setScreenPosition(position); if (position != ScreenPosition.FULL_SCREEN) this._updateLensMode(); } }, _updateMagFactor: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { // Mag factor is accurate to two decimal places. let magFactor = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); this._zoomRegions[0].setMagFactor(magFactor, magFactor); this.setActive(this.enabled && magFactor > 1.0); } }, _updateLensMode: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { this._zoomRegions[0].setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); } }, _updateClampMode: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { this._zoomRegions[0].setClampScrollingAtEdges( !this._settings.get_boolean(CLAMP_MODE_KEY) ); } }, _updateMouseTrackingMode: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { this._zoomRegions[0].setMouseTrackingMode( this._settings.get_enum(MOUSE_TRACKING_KEY) ); } } }; Signals.addSignalMethods(Magnifier.prototype); function ZoomRegion(magnifier, mouseSourceActor) { this._init(magnifier, mouseSourceActor); } ZoomRegion.prototype = { _init: function(magnifier, mouseSourceActor) { this._magnifier = magnifier; this._mouseTrackingMode = MouseTrackingMode.NONE; this._clampScrollingAtEdges = false; this._lensMode = false; this._screenPosition = ScreenPosition.FULL_SCREEN; this._magView = null; this._uiGroupClone = null; this._mouseSourceActor = mouseSourceActor; this._mouseActor = null; this._crossHairs = null; this._crossHairsActor = null; this._viewPortX = 0; this._viewPortY = 0; this._viewPortWidth = global.screen_width; this._viewPortHeight = global.screen_height; this._xCenter = this._viewPortWidth / 2; this._yCenter = this._viewPortHeight / 2; this._xMagFactor = 1; this._yMagFactor = 1; this._followingCursor = false; }, /** * setActive: * @activate: Boolean to show/hide the ZoomRegion. */ setActive: function(activate) { if (activate && !this.isActive()) { this._createActors(); if (this._isMouseOverRegion()) this._magnifier.hideSystemCursor(); this._updateMagViewGeometry(); this._updateCloneGeometry(); this._updateMousePosition(); global.top_window_group.raise_top(); } else if (!activate && this.isActive()) { global.reparentActor(global.top_window_group, global.stage); this._destroyActors(); } }, /** * isActive: * @return Whether this ZoomRegion is active (boolean). */ isActive: function() { return this._magView != null; }, /** * setMagFactor: * @xMagFactor: The power to set the horizontal magnification factor to * of the magnified view. A value of 1.0 means no * magnification. A value of 2.0 doubles the size. * @yMagFactor: The power to set the vertical magnification factor to * of the magnified view. */ setMagFactor: function(xMagFactor, yMagFactor) { this._changeROI({ xMagFactor: xMagFactor, yMagFactor: yMagFactor, redoCursorTracking: this._followingCursor }); }, /** * getMagFactor: * @return an array, [xMagFactor, yMagFactor], containing the horizontal * and vertical magnification powers. A value of 1.0 means no * magnification. A value of 2.0 means the contents are doubled * in size, and so on. */ getMagFactor: function() { return [this._xMagFactor, this._yMagFactor]; }, /** * setMouseTrackingMode * @mode: One of the enum MouseTrackingMode values. */ setMouseTrackingMode: function(mode) { if (mode >= MouseTrackingMode.NONE && mode <= MouseTrackingMode.PUSH) this._mouseTrackingMode = mode; }, /** * getMouseTrackingMode * @return: One of the enum MouseTrackingMode values. */ getMouseTrackingMode: function() { return this._mouseTrackingMode; }, /** * setViewPort * Sets the position and size of the ZoomRegion on screen. * @viewPort: Object defining the position and size of the view port. * It has members x, y, width, height. The values are in * stage coordinate space. */ setViewPort: function(viewPort) { this._setViewPort(viewPort); this._screenPosition = ScreenPosition.NONE; }, /** * setROI * Sets the "region of interest" that the ZoomRegion is magnifying. * @roi: Object that defines the region of the screen to magnify. It * has members x, y, width, height. The values are in * screen (unmagnified) coordinate space. */ setROI: function(roi) { if (roi.width <= 0 || roi.height <= 0) return; this._followingCursor = false; this._changeROI({ xMagFactor: this._viewPortWidth / roi.width, yMagFactor: this._viewPortHeight / roi.height, xCenter: roi.x + roi.width / 2, yCenter: roi.y + roi.height / 2 }); }, /** * getROI: * Retrieves the "region of interest" -- the rectangular bounds of that part * of the desktop that the magnified view is showing (x, y, width, height). * The bounds are given in non-magnified coordinates. * @return an array, [x, y, width, height], representing the bounding * rectangle of what is shown in the magnified view. */ getROI: function() { let roiWidth = this._viewPortWidth / this._xMagFactor; let roiHeight = this._viewPortHeight / this._yMagFactor; return [this._xCenter - roiWidth / 2, this._yCenter - roiHeight / 2, roiWidth, roiHeight]; }, /** * setLensMode: * Turn lens mode on/off. In full screen mode, lens mode does nothing since * a lens the size of the screen is pointless. * @lensMode: A boolean to set the sense of lens mode. */ setLensMode: function(lensMode) { this._lensMode = lensMode; if (!this._lensMode) this.setScreenPosition (this._screenPosition); }, /** * isLensMode: * Is lens mode on or off? * @return The lens mode state as a boolean. */ isLensMode: function() { return this._lensMode; }, /** * setClampScrollingAtEdges: * Stop vs. allow scrolling of the magnified contents when it scroll beyond * the edges of the screen. * @clamp: Boolean to turn on/off clamping. */ setClampScrollingAtEdges: function(clamp) { this._clampScrollingAtEdges = clamp; if (clamp) this._changeROI(); }, /** * setTopHalf: * Magnifier view occupies the top half of the screen. */ setTopHalf: function() { let viewPort = {}; viewPort.x = 0; viewPort.y = 0; viewPort.width = global.screen_width; viewPort.height = global.screen_height/2; this._setViewPort(viewPort); this._screenPosition = ScreenPosition.TOP_HALF; }, /** * setBottomHalf: * Magnifier view occupies the bottom half of the screen. */ setBottomHalf: function() { let viewPort = {}; viewPort.x = 0; viewPort.y = global.screen_height/2; viewPort.width = global.screen_width; viewPort.height = global.screen_height/2; this._setViewPort(viewPort); this._screenPosition = ScreenPosition.BOTTOM_HALF; }, /** * setLeftHalf: * Magnifier view occupies the left half of the screen. */ setLeftHalf: function() { let viewPort = {}; viewPort.x = 0; viewPort.y = 0; viewPort.width = global.screen_width/2; viewPort.height = global.screen_height; this._setViewPort(viewPort); this._screenPosition = ScreenPosition.LEFT_HALF; }, /** * setRightHalf: * Magnifier view occupies the right half of the screen. */ setRightHalf: function() { let viewPort = {}; viewPort.x = global.screen_width/2; viewPort.y = 0; viewPort.width = global.screen_width/2; viewPort.height = global.screen_height; this._setViewPort(viewPort); this._screenPosition = ScreenPosition.RIGHT_HALF; }, /** * setFullScreenMode: * Set the ZoomRegion to full-screen mode. * Note: disallows lens mode. */ setFullScreenMode: function() { let viewPort = {}; viewPort.x = 0; viewPort.y = 0; viewPort.width = global.screen_width; viewPort.height = global.screen_height; this.setViewPort(viewPort); this._screenPosition = ScreenPosition.FULL_SCREEN; }, /** * setScreenPosition: * Positions the zoom region to one of the enumerated positions on the * screen. * @position: one of Magnifier.FULL_SCREEN, Magnifier.TOP_HALF, * Magnifier.BOTTOM_HALF,Magnifier.LEFT_HALF, or * Magnifier.RIGHT_HALF. */ setScreenPosition: function(inPosition) { switch (inPosition) { case ScreenPosition.FULL_SCREEN: this.setFullScreenMode(); break; case ScreenPosition.TOP_HALF: this.setTopHalf(); break; case ScreenPosition.BOTTOM_HALF: this.setBottomHalf(); break; case ScreenPosition.LEFT_HALF: this.setLeftHalf(); break; case ScreenPosition.RIGHT_HALF: this.setRightHalf(); break; } }, /** * getScreenPosition: * Tell the outside world what the current mode is -- magnifiying the * top half, bottom half, etc. * @return: the current mode. */ getScreenPosition: function() { return this._screenPosition; }, /** * scrollToMousePos: * Set the region of interest based on the position of the system pointer. * @return: Whether the system mouse pointer is over the magnified view. */ scrollToMousePos: function() { this._followingCursor = true; if (this._mouseTrackingMode != MouseTrackingMode.NONE) this._changeROI({ redoCursorTracking: true }); else this._updateMousePosition(); // Determine whether the system mouse pointer is over this zoom region. return this._isMouseOverRegion(); }, /** * scrollContentsTo: * Shift the contents of the magnified view such it is centered on the given * coordinate. * @x: The x-coord of the point to center on. * @y: The y-coord of the point to center on. */ scrollContentsTo: function(x, y) { this._followingCursor = false; this._changeROI({ xCenter: x, yCenter: y }); }, /** * addCrosshairs: * Add crosshairs centered on the magnified mouse. * @crossHairs: Crosshairs instance */ addCrosshairs: function(crossHairs) { this._crossHairs = crossHairs; // If the crossHairs is not already within a larger container, add it // to this zoom region. Otherwise, add a clone. if (crossHairs && this.isActive()) { this._crossHairsActor = crossHairs.addToZoomRegion(this, this._mouseActor); } }, //// Private methods //// _createActors: function() { global.reparentActor(global.top_window_group, Main.uiGroup); // The root actor for the zoom region this._magView = new St.Bin({ style_class: 'magnifier-zoom-region', x_fill: true, y_fill: true }); global.stage.add_actor(this._magView); // hide the magnified region from CLUTTER_PICK_ALL Cinnamon.util_set_hidden_from_pick (this._magView, true); // Append a Clutter.Group to clip the contents of the magnified view. let mainGroup = new Clutter.Group({ clip_to_allocation: true }); this._magView.set_child(mainGroup); // Add a background for when the magnified uiGroup is scrolled // out of view (don't want to see desktop showing through). let background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR }); mainGroup.add_actor(background); // Clone the group that contains all of UI on the screen. This is the // chrome, the windows, etc. this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup }); mainGroup.add_actor(this._uiGroupClone); Main.uiGroup.set_size(global.screen_width, global.screen_height); background.set_size(global.screen_width, global.screen_height); // Add either the given mouseSourceActor to the ZoomRegion, or a clone of // it. if (this._mouseSourceActor.get_parent() != null) this._mouseActor = new Clutter.Clone({ source: this._mouseSourceActor }); else this._mouseActor = this._mouseSourceActor; mainGroup.add_actor(this._mouseActor); if (this._crossHairs) this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor); else this._crossHairsActor = null; }, _destroyActors: function() { if (this._mouseActor == this._mouseSourceActor) this._mouseActor.get_parent().remove_actor (this._mouseActor); if (this._crossHairs) this._crossHairs.removeFromParent(this._crossHairsActor); this._magView.destroy(); this._magView = null; this._uiGroupClone = null; this._mouseActor = null; this._crossHairsActor = null; }, _setViewPort: function(viewPort, fromROIUpdate) { // Sets the position of the zoom region on the screen let width = Math.round(Math.min(viewPort.width, global.screen_width)); let height = Math.round(Math.min(viewPort.height, global.screen_height)); let x = Math.max(viewPort.x, 0); let y = Math.max(viewPort.y, 0); x = Math.round(Math.min(x, global.screen_width - width)); y = Math.round(Math.min(y, global.screen_height - height)); this._viewPortX = x; this._viewPortY = y; this._viewPortWidth = width; this._viewPortHeight = height; this._updateMagViewGeometry(); if (!fromROIUpdate) this._changeROI({ redoCursorTracking: this._followingCursor }); // will update mouse if (this.isActive() && this._isMouseOverRegion()) this._magnifier.hideSystemCursor(); }, _changeROI: function(params) { // Updates the area we are viewing; the magnification factors // and center can be set explicitly, or we can recompute // the position based on the mouse cursor position params = Params.parse(params, { xMagFactor: this._xMagFactor, yMagFactor: this._yMagFactor, xCenter: this._xCenter, yCenter: this._yCenter, redoCursorTracking: false }); if (params.xMagFactor <= 0) params.xMagFactor = this._xMagFactor; if (params.yMagFactor <= 0) params.yMagFactor = this._yMagFactor; this._xMagFactor = params.xMagFactor; this._yMagFactor = params.yMagFactor; if (params.redoCursorTracking && this._mouseTrackingMode != MouseTrackingMode.NONE) { // This depends on this.xMagFactor/yMagFactor already being updated [params.xCenter, params.yCenter] = this._centerFromMousePosition(); } if (this._clampScrollingAtEdges) { let roiWidth = this._viewPortWidth / this._xMagFactor; let roiHeight = this._viewPortHeight / this._yMagFactor; params.xCenter = Math.min(params.xCenter, global.screen_width - roiWidth / 2); params.xCenter = Math.max(params.xCenter, roiWidth / 2); params.yCenter = Math.min(params.yCenter, global.screen_height - roiHeight / 2); params.yCenter = Math.max(params.yCenter, roiHeight / 2); } this._xCenter = params.xCenter; this._yCenter = params.yCenter; // If in lens mode, move the magnified view such that it is centered // over the actual mouse. However, in full screen mode, the "lens" is // the size of the screen -- pointless to move such a large lens around. if (this._lensMode && !this._isFullScreen()) this._setViewPort({ x: this._xCenter - this._viewPortWidth / 2, y: this._yCenter - this._viewPortHeight / 2, width: this._viewPortWidth, height: this._viewPortHeight }, true); this._updateCloneGeometry(); this._updateMousePosition(); }, _isMouseOverRegion: function() { // Return whether the system mouse sprite is over this ZoomRegion. If the // mouse's position is not given, then it is fetched. let mouseIsOver = false; if (this.isActive()) { let xMouse = this._magnifier.xMouse; let yMouse = this._magnifier.yMouse; mouseIsOver = ( xMouse >= this._viewPortX && xMouse < (this._viewPortX + this._viewPortWidth) && yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight) ); } return mouseIsOver; }, _isFullScreen: function() { // Does the magnified view occupy the whole screen? Note that this // doesn't necessarily imply // this._screenPosition = ScreenPosition.FULL_SCREEN; if (this._viewPortX != 0 || this._viewPortY != 0) return false; if (this._viewPortWidth != global.screen_width || this._viewPortHeight != global.screen_height) return false; return true; }, _centerFromMousePosition: function() { // Determines where the center should be given the current cursor // position and mouse tracking mode let xMouse = this._magnifier.xMouse; let yMouse = this._magnifier.yMouse; if (this._mouseTrackingMode == MouseTrackingMode.PROPORTIONAL) { return this._centerFromMouseProportional(xMouse, yMouse); } else if (this._mouseTrackingMode == MouseTrackingMode.PUSH) { return this._centerFromMousePush(xMouse, yMouse); } else if (this._mouseTrackingMode == MouseTrackingMode.CENTERED) { return this._centerFromMouseCentered(xMouse, yMouse); } return null; // Should never be hit }, _centerFromMousePush: function(xMouse, yMouse) { let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI(); let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size(); let xPos = xRoi + widthRoi / 2; let yPos = yRoi + heightRoi / 2; let xRoiRight = xRoi + widthRoi - cursorWidth; let yRoiBottom = yRoi + heightRoi - cursorHeight; if (xMouse < xRoi) xPos -= (xRoi - xMouse); else if (xMouse > xRoiRight) xPos += (xMouse - xRoiRight); if (yMouse < yRoi) yPos -= (yRoi - yMouse); else if (yMouse > yRoiBottom) yPos += (yMouse - yRoiBottom); return [xPos, yPos]; }, _centerFromMouseProportional: function(xMouse, yMouse) { let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI(); let halfScreenWidth = global.screen_width / 2; let halfScreenHeight = global.screen_height / 2; // We want to pad with a constant distance after zooming, so divide // by the magnification factor. let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5; let xPadding = unscaledPadding / this._xMagFactor; let yPadding = unscaledPadding / this._yMagFactor; let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth; // -1 ... 1 let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1 let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding); let yPos = yMouse - yProportion * (heightRoi /2 - yPadding); return [xPos, yPos]; }, _centerFromMouseCentered: function(xMouse, yMouse) { return [xMouse, yMouse]; }, _screenToViewPort: function(screenX, screenY) { // Converts coordinates relative to the (unmagnified) screen to coordinates // relative to the origin of this._magView return [this._viewPortWidth / 2 + (screenX - this._xCenter) * this._xMagFactor, this._viewPortHeight / 2 + (screenY - this._yCenter) * this._yMagFactor]; }, _updateMagViewGeometry: function() { if (!this.isActive()) return; if (this._isFullScreen()) this._magView.add_style_class_name('full-screen'); else this._magView.remove_style_class_name('full-screen'); this._magView.set_size(this._viewPortWidth, this._viewPortHeight); this._magView.set_position(this._viewPortX, this._viewPortY); }, _updateCloneGeometry: function() { if (!this.isActive()) return; this._uiGroupClone.set_scale(this._xMagFactor, this._yMagFactor); this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor); let [x, y] = this._screenToViewPort(0, 0); this._uiGroupClone.set_position(x, y); this._updateMousePosition(); }, _updateMousePosition: function() { if (!this.isActive()) return; let [xMagMouse, yMagMouse] = this._screenToViewPort(this._magnifier.xMouse, this._magnifier.yMouse); xMagMouse = Math.round(xMagMouse); yMagMouse = Math.round(yMagMouse); this._mouseActor.set_position(xMagMouse, yMagMouse); if (this._crossHairsActor) { let [groupWidth, groupHeight] = this._crossHairsActor.get_size(); this._crossHairsActor.set_position(xMagMouse - groupWidth / 2, yMagMouse - groupHeight / 2); } } }; function Crosshairs() { this._init(); } Crosshairs.prototype = { _init: function() { // Set the group containing the crosshairs to three times the desktop // size in case the crosshairs need to appear to be infinite in // length (i.e., extend beyond the edges of the view they appear in). let groupWidth = global.screen_width * 3; let groupHeight = global.screen_height * 3; this._actor = new Clutter.Group({ clip_to_allocation: false, width: groupWidth, height: groupHeight }); this._horizLeftHair = new Clutter.Rectangle(); this._horizRightHair = new Clutter.Rectangle(); this._vertTopHair = new Clutter.Rectangle(); this._vertBottomHair = new Clutter.Rectangle(); this._actor.add_actor(this._horizLeftHair); this._actor.add_actor(this._horizRightHair); this._actor.add_actor(this._vertTopHair); this._actor.add_actor(this._vertBottomHair); this._clipSize = [0, 0]; this._clones = []; this.reCenter(); }, /** * addToZoomRegion * Either add the crosshairs actor to the given ZoomRegion, or, if it is * already part of some other ZoomRegion, create a clone of the crosshairs * actor, and add the clone instead. Returns either the original or the * clone. * @zoomRegion: The container to add the crosshairs group to. * @magnifiedMouse: The mouse actor for the zoom region -- used to * position the crosshairs and properly layer them below * the mouse. * @return The crosshairs actor, or its clone. */ addToZoomRegion: function(zoomRegion, magnifiedMouse) { let crosshairsActor = null; if (zoomRegion && magnifiedMouse) { let container = magnifiedMouse.get_parent(); if (container) { crosshairsActor = this._actor; if (this._actor.get_parent() != null) { crosshairsActor = new Clutter.Clone({ source: this._actor }); this._clones.push(crosshairsActor); } if (this._actor.visible) crosshairsActor.show(); else crosshairsActor.hide(); container.add_actor(crosshairsActor); container.raise_child(magnifiedMouse, crosshairsActor); let [xMouse, yMouse] = magnifiedMouse.get_position(); let [crosshairsWidth, crosshairsHeight] = crosshairsActor.get_size(); crosshairsActor.set_position(xMouse - crosshairsWidth / 2 , yMouse - crosshairsHeight / 2); } } return crosshairsActor; }, /** * removeFromParent: * @childActor: the actor returned from addToZoomRegion * Remove the crosshairs actor from its parent container, or destroy the * child actor if it was just a clone of the crosshairs actor. */ removeFromParent: function(childActor) { if (childActor == this._actor) childActor.get_parent().remove_actor(childActor); else childActor.destroy(); }, /** * setColor: * Set the color of the crosshairs. * @clutterColor: The color as a Clutter.Color. */ setColor: function(clutterColor) { this._horizLeftHair.set_color(clutterColor); this._horizRightHair.set_color(clutterColor); this._vertTopHair.set_color(clutterColor); this._vertBottomHair.set_color(clutterColor); }, /** * getColor: * Get the color of the crosshairs. * @color: The color as a Clutter.Color. */ getColor: function() { let clutterColor = new Clutter.Color(); this._horizLeftHair.get_color(clutterColor); return clutterColor; }, /** * setThickness: * Set the width of the vertical and horizontal lines of the crosshairs. * @thickness */ setThickness: function(thickness) { this._horizLeftHair.set_height(thickness); this._horizRightHair.set_height(thickness); this._vertTopHair.set_width(thickness); this._vertBottomHair.set_width(thickness); this.reCenter(); }, /** * getThickness: * Get the width of the vertical and horizontal lines of the crosshairs. * @return: The thickness of the crosshairs. */ getThickness: function() { return this._horizLeftHair.get_height(); }, /** * setOpacity: * Set how opaque the crosshairs are. * @opacity: Value between 0 (fully transparent) and 255 (full opaque). */ setOpacity: function(opacity) { // set_opacity() throws an exception for values outside the range // [0, 255]. if (opacity < 0) opacity = 0; else if (opacity > 255) opacity = 255; this._horizLeftHair.set_opacity(opacity); this._horizRightHair.set_opacity(opacity); this._vertTopHair.set_opacity(opacity); this._vertBottomHair.set_opacity(opacity); }, /** * getOpacity: * Retriev how opaque the crosshairs are. * @return: A value between 0 (transparent) and 255 (opaque). */ getOpacity: function() { return this._horizLeftHair.get_opacity(); }, /** * setLength: * Set the length of the vertical and horizontal lines in the crosshairs. * @length: The length of the crosshairs. */ setLength: function(length) { this._horizLeftHair.set_width(length); this._horizRightHair.set_width(length); this._vertTopHair.set_height(length); this._vertBottomHair.set_height(length); this.reCenter(); }, /** * getLength: * Get the length of the vertical and horizontal lines in the crosshairs. * @return: The length of the crosshairs. */ getLength: function() { return this._horizLeftHair.get_width(); }, /** * setClip: * Set the width and height of the rectangle that clips the crosshairs at * their intersection * @size: Array of [width, height] defining the size of the clip * rectangle. */ setClip: function(size) { if (size) { // Take a chunk out of the crosshairs where it intersects the // mouse. this._clipSize = size; this.reCenter(); } else { // Restore the missing chunk. this._clipSize = [0, 0]; this.reCenter(); } }, /** * getClip: * Get the dimensions of the clip rectangle. * @return: An array of the form [width, height]. */ getClip: function() { return this._clipSize; }, /** * show: * Show the crosshairs. */ show: function() { this._actor.show(); // Clones don't share visibility. for (let i = 0; i < this._clones.length; i++) this._clones[i].show(); }, /** * hide: * Hide the crosshairs. */ hide: function() { this._actor.hide(); // Clones don't share visibility. for (let i = 0; i < this._clones.length; i++) this._clones[i].hide(); }, /** * reCenter: * Reposition the horizontal and vertical hairs such that they cross at * the center of crosshairs group. If called with the dimensions of * the clip rectangle, these are used to update the size of the clip. * @clipSize: Optional. If present, an array of the form [width, height]. */ reCenter: function(clipSize) { let [groupWidth, groupHeight] = this._actor.get_size(); let leftLength = this._horizLeftHair.get_width(); let rightLength = this._horizRightHair.get_width(); let topLength = this._vertTopHair.get_height(); let bottomLength = this._vertBottomHair.get_height(); let thickness = this._horizLeftHair.get_height(); // Deal with clip rectangle. if (clipSize) this._clipSize = clipSize; let clipWidth = this._clipSize[0]; let clipHeight = this._clipSize[1]; // Note that clip, if present, is not centred on the cross hair // intersection, but biased towards the top left. let left = groupWidth / 2 - clipWidth * 0.25 - leftLength; let right = groupWidth / 2 + clipWidth * 0.75; let top = groupHeight / 2 - clipHeight * 0.25 - topLength - thickness / 2; let bottom = groupHeight / 2 + clipHeight * 0.75 + thickness / 2; this._horizLeftHair.set_position(left, (groupHeight - thickness) / 2); this._horizRightHair.set_position(right, (groupHeight - thickness) / 2); this._vertTopHair.set_position((groupWidth - thickness) / 2, top); this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom); } }; const INCR = 0.1; function MagnifierInputHandler(magnifier) { this._init(magnifier); } MagnifierInputHandler.prototype = { _init: function(magnifier) { this.magnifier = magnifier; this._zoom_in_id = 0; this._zoom_out_id = 0; this.a11y_settings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA }); this.a11y_settings.connect("changed::" + SHOW_KEY, Lang.bind(this, this._refresh_state)); this.keybinding_settings = new Gio.Settings({ schema_id: KEYBINDING_SCHEMA }); this.keybinding_settings.connect("changed", Lang.bind(this, this._refresh_state)); this._refresh_state(); }, _enable_zoom: function() { if (this._zoom_in_id > 0 || this._zoom_out_id > 0) this._disable_zoom(); this._zoom_in_id = global.display.connect('zoom-scroll-in', Lang.bind(this, this._zoom_in)); this._zoom_out_id = global.display.connect('zoom-scroll-out', Lang.bind(this, this._zoom_out)); let kb = this.keybinding_settings.get_strv(ZOOM_IN_KEY); Main.keybindingManager.addHotKeyArray("magnifier-zoom-in", kb, Lang.bind(this, this._zoom_in)); kb = this.keybinding_settings.get_strv(ZOOM_OUT_KEY); Main.keybindingManager.addHotKeyArray("magnifier-zoom-out", kb, Lang.bind(this, this._zoom_out)); }, _disable_zoom: function() { if (this._zoom_in_id > 0) global.display.disconnect(this._zoom_in_id) if (this._zoom_out_id > 0) global.display.disconnect(this._zoom_out_id); this._zoom_in_id = 0; this._zoom_out_id = 0; Main.keybindingManager.removeHotKey("magnifier-zoom-in"); Main.keybindingManager.removeHotKey("magnifier-zoom-out"); }, _refresh_state: function() { this.zoom_active = this.magnifier.isActive(); this.current_zoom = 1.0; if (this.zoom_active) { let zr = this.magnifier.getZoomRegions()[0]; this.current_zoom = zr.getMagFactor()[0]; } if (this.a11y_settings.get_boolean(SHOW_KEY)) this._enable_zoom(); else this._disable_zoom(); }, _zoom_in: function(display, screen, event, kb, action) { if (this.zoom_active) { this.current_zoom *= (1.0 + INCR); } else { this.current_zoom *= (1.0 + INCR); this.magnifier.setActive(true) this.zoom_active = true; } try { this.magnifier.setMagFactor(this.current_zoom, this.current_zoom) } catch (e) { this._refresh_state(); } }, _zoom_out: function(display, screen, event, kb, action) { if (this.zoom_active) { this.current_zoom *= (1.0 - INCR); if (this.current_zoom <= 1.0) { this.current_zoom = 1.0; this.magnifier.setActive(false); this.zoom_active = false; } try { this.magnifier.setMagFactor(this.current_zoom, this.current_zoom) } catch (e) { this._refresh_state(); } } } } Cinnamon-2.8.6/js/ui/hotCorner.js0000664000175000017500000002611412625666430015565 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Lang = imports.lang; const St = imports.gi.St; const Util = imports.misc.util; const Layout = imports.ui.layout; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const Mainloop = imports.mainloop; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; const OVERVIEW_CORNERS_KEY = 'overview-corner'; // Map texts to boolean value const TF = []; TF['true'] = true; TF['false'] = false; function HotCornerManager(){ this._init(); } HotCornerManager.prototype = { _init: function() { this.corners = []; for (let i = 0; i < 4; i++){ // In order: top left; top right; bottom left; bottom right; this.corners.push(new HotCorner()); Main.layoutManager.addChrome(this.corners[i].actor); Main.layoutManager.addChrome(this.corners[i].iconActor, {visibleInFullscreen: false}); } this.parseGSettings(); global.settings.connect('changed::' + OVERVIEW_CORNERS_KEY, Lang.bind(this, this.parseGSettings)); this.updatePosition(Main.layoutManager.primaryMonitor, Main.layoutManager.bottomMonitor); }, parseGSettings: function() { let options = global.settings.get_strv(OVERVIEW_CORNERS_KEY); if (options.length != 4) { global.log(_("Invalid overview options: Incorrect number of corners")); return false; } for (let i = 0; i < 4; i ++) { let elements = options[i].split(':'); this.corners[i].setProperties(elements); } return true; }, updatePosition: function(primaryMonitor, bottomMonitor) { let p_x = primaryMonitor.x; let p_y = primaryMonitor.y; let b_x = bottomMonitor.x; let b_y = bottomMonitor.y + bottomMonitor.height; // Top Left: 0 this.corners[0].actor.set_position(p_x, p_y); this.corners[0].iconActor.set_position(p_x + 1, p_y + 1); // Top Right: 1 this.corners[1].actor.set_position(p_x + primaryMonitor.width - 2, p_y); this.corners[1].iconActor.set_position(p_x + primaryMonitor.width - 33, p_y + 1); // Bottom Left: 2 this.corners[2].actor.set_position(b_x, b_y - 1); this.corners[2].iconActor.set_position(b_x + 1, b_y - 33); // Bottom Right: 3 this.corners[3].actor.set_position(b_x + bottomMonitor.width - 2, b_y - 1); this.corners[3].iconActor.set_position(b_x + bottomMonitor.width - 33, b_y - 33); return true; } } // HotCorner: // // This class manages a "hot corner" that can toggle switching to // overview. function HotCorner() { this._init(); } HotCorner.prototype = { _init : function() { // We use this flag to mark the case where the user has entered the // hot corner and has not left both the hot corner and a surrounding // guard area (the "environs"). This avoids triggering the hot corner // multiple times due to an accidental jitter. this._entered = false; this.action = null; // The action to activate when hot corner is triggered this.hover = false; // Whether the hot corners responds to hover this.icon = false; // Whether the overview corner icon is shown // Construct the hot corner 'ripples' this.actor = new Clutter.Group({ name: 'hot-corner-environs', width: 3, height: 3, reactive: true }); this._corner = new Clutter.Rectangle({ name: 'hot-corner', width: 2, height: 1, opacity: 0, reactive: true }); this._corner._delegate = this; this.actor.add_actor(this._corner); if (St.Widget.get_default_direction() == St.TextDirection.RTL) { this._corner.set_position(this.actor.width - this._corner.width, 0); this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); } else { this._corner.set_position(0, 0); } this._activationTime = 0; this.actor.connect('leave-event', Lang.bind(this, this._onEnvironsLeft)); // Clicking on the hot corner environs should result in the // same behavior as clicking on the hot corner. this.actor.connect('button-release-event', Lang.bind(this, this._onCornerClicked)); // In addition to being triggered by the mouse enter event, // the hot corner can be triggered by clicking on it. This is // useful if the user wants to undo the effect of triggering // the hot corner once in the hot corner. this._corner.connect('enter-event', Lang.bind(this, this._onCornerEntered)); this._corner.connect('button-release-event', Lang.bind(this, this._onCornerClicked)); this._corner.connect('leave-event', Lang.bind(this, this._onCornerLeft)); this.tile_delay = false; global.window_manager.connect('tile', Lang.bind(this, this._tilePerformed)); // Cache the three ripples instead of dynamically creating and destroying them. this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 }); this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 }); this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 }); Main.uiGroup.add_actor(this._ripple1); Main.uiGroup.add_actor(this._ripple2); Main.uiGroup.add_actor(this._ripple3); this._ripple1.hide(); this._ripple2.hide(); this._ripple3.hide(); // Construct the overview corner icon this.iconActor = new St.Button({name: 'overview-corner', reactive: true, track_hover: true}); this.iconActor.connect('button-release-event', Lang.bind(this, this.runAction)); this.iconActor.set_size(32, 32); }, destroy: function() { this.actor.destroy(); }, _tile_delay_cb : function() { this.tile_delay = false; return false; }, _tilePerformed : function(cinnamonwm, actor, targetX, targetY, targetWidth, targetHeight) { this.tile_delay = true; Mainloop.timeout_add(250, Lang.bind(this, this._tile_delay_cb)); }, _animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) { // We draw a ripple by using a source image and animating it scaling // outwards and fading away. We want the ripples to move linearly // or it looks unrealistic, but if the opacity of the ripple goes // linearly to zero it fades away too quickly, so we use Tweener's // 'onUpdate' to give a non-linear curve to the fade-away and make // it more visible in the middle section. ripple._opacity = startOpacity; ripple.set_anchor_point_from_gravity(Clutter.Gravity.CENTER); ripple.visible = true; ripple.opacity = 255 * Math.sqrt(startOpacity); ripple.scale_x = ripple.scale_y = startScale; let [x, y] = this._corner.get_transformed_position(); ripple.x = x; ripple.y = y; Tweener.addTween(ripple, { _opacity: 0, scale_x: finalScale, scale_y: finalScale, delay: delay, time: time, transition: 'linear', onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); }, onComplete: function() { ripple.visible = false; } }); }, setProperties: function(properties){ this.action = properties[0]; this.hover = TF[properties[1]]; this.icon = TF[properties[2]]; if (this.hover) this.actor.show(); else this.actor.hide(); if (this.icon) this.iconActor.show(); else this.iconActor.hide(); }, rippleAnimation: function() { // Show three concentric ripples expanding outwards; the exact // parameters were found by trial and error, so don't look // for them to make perfect sense mathematically this._ripple1.show(); this._ripple2.show(); this._ripple3.show(); // delay time scale opacity => scale this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5); this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25); this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); }, runAction: function(){ this._activationTime = Date.now() / 1000; switch (this.action){ case 'expo': if (!Main.expo.animationInProgress) Main.expo.toggle(); break; case 'scale': if (!Main.overview.animationInProgress) Main.overview.toggle(); break; case 'desktop': global.screen.toggle_desktop(global.get_current_time()); break; default: Util.spawnCommandLine(this.action); } }, _onCornerEntered : function() { if (!this._entered && !this.tile_delay) { this._entered = true; let run = false; if (!(Main.expo.visible || Main.overview.visible)){ run = true; } if ((Main.expo.visible && this.action=='expo') || (Main.overview.visible && this.action=='scale')){ run = true; } if (run){ this.rippleAnimation(); this.runAction(); } } return false; }, _onCornerClicked : function() { if (this.shouldToggleOverviewOnClick()) this.rippleAnimation(); this.runAction(); return true; }, _onCornerLeft : function(actor, event) { if (event.get_related() != this.actor) this._entered = false; // Consume event, otherwise this will confuse onEnvironsLeft return true; }, _onEnvironsLeft : function(actor, event) { if (event.get_related() != this._corner) this._entered = false; return false; }, // Checks if the Activities button is currently sensitive to // clicks. The first call to this function within the // HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being // triggered will return false. This avoids opening and closing // the overview if the user both triggered the hot corner and // clicked the Activities button. shouldToggleOverviewOnClick: function() { if (Main.overview.animationInProgress) return false; if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT) return true; return false; } }; Cinnamon-2.8.6/js/ui/checkBox.js0000664000175000017500000001323712625666430015352 0ustar fabiofabioconst Clutter = imports.gi.Clutter; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Params = imports.misc.params; const Lang = imports.lang; function CheckBoxContainer() { this._init(); } CheckBoxContainer.prototype = { _init: function() { this.actor = new Cinnamon.GenericContainer({ y_align: St.Align.MIDDLE }); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('style-changed', Lang.bind(this, function() { let node = this.actor.get_theme_node(); this._spacing = Math.round(node.get_length('spacing')); })); this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH; this._box = new St.Bin(); this.actor.add_actor(this._box); this.label = new St.Label(); this.label.clutter_text.set_line_wrap(false); this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE); this.actor.add_actor(this.label, { y_fill: true, y_align: St.Align.END }); this._spacing = 0; }, _getPreferredWidth: function(actor, forHeight, alloc) { let node = this.actor.get_theme_node(); forHeight = node.adjust_for_height(forHeight); let [minBoxWidth, natBoxWidth] = this._box.get_preferred_width(forHeight); let boxNode = this._box.get_theme_node(); [minBoxWidth, natBoxWidth] = boxNode.adjust_preferred_width(minBoxWidth, natBoxWidth); let [minLabelWidth, natLabelWidth] = this.label.get_preferred_width(forHeight); let labelNode = this.label.get_theme_node(); [minLabelWidth, natLabelWidth] = labelNode.adjust_preferred_width(minLabelWidth, natLabelWidth); let min = minBoxWidth + minLabelWidth + this._spacing; let nat = natBoxWidth + natLabelWidth + this._spacing; [min, nat] = node.adjust_preferred_width(min, nat); alloc.min_size = min; alloc.natural_size = nat; }, _getPreferredHeight: function(actor, forWidth, alloc) { let [minBoxHeight, natBoxHeight] = this._box.get_preferred_height(-1); let [minLabelHeight, natLabelHeight] = this.label.get_preferred_height(-1); alloc.min_size = Math.max(minBoxHeight, minLabelHeight); alloc.natural_size = Math.max(natBoxHeight, natLabelHeight); }, _allocate: function(actor, box, flags) { let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let childBox = new Clutter.ActorBox(); let [minBoxWidth, natBoxWidth] = this._box.get_preferred_width(-1); let [minBoxHeight, natBoxHeight] = this._box.get_preferred_height(-1); childBox.x1 = box.x1; childBox.x2 = box.x1 + natBoxWidth; if (availHeight > natBoxHeight) childBox.y1 = box.y1 + (availHeight-natBoxHeight)/2; else childBox.y1 = box.y1; childBox.y2 = childBox.y1 + natBoxHeight; this._box.allocate(childBox, flags); let [minLabelWidth, natLabelWidth] = this.label.get_preferred_width(-1); let [minLabelHeight, natLabelHeight] = this.label.get_preferred_height(-1); childBox.x1 = box.x1 + natBoxWidth + this._spacing; childBox.x2 = childBox.x1 + availWidth - natBoxWidth - this._spacing; if (availHeight > natLabelHeight) childBox.y1 = box.y1 + (availHeight-natLabelHeight)/2; else childBox.y1 = box.y1; childBox.y2 = childBox.y1 + natLabelHeight; this.label.allocate(childBox, flags); } }; function CheckBoxBase() { this._init.apply(this, arguments); } CheckBoxBase.prototype = { _init: function(checkedState, params) { this._params = { style_class: 'check-box', button_mask: St.ButtonMask.ONE, toggle_mode: true, can_focus: true, x_fill: true, y_fill: true, y_align: St.Align.MIDDLE }; if (params != undefined) { this._params = Params.parse(params, this._params); } this.actor = new St.Button(this._params); this.actor._delegate = this; this.actor.checked = checkedState; }, setToggleState: function(checkedState) { this.actor.checked = checkedState; }, toggle: function() { this.setToggleState(!this.actor.checked); }, destroy: function() { this.actor.destroy(); } }; function CheckButton() { this._init.apply(this, arguments); } CheckButton.prototype = { __proto__: CheckBoxBase.prototype, _init: function(checkedState, params) { CheckBoxBase.prototype._init.call(this, checkedState, params); this.checkmark = new St.Bin(); this.actor.set_child(this.checkmark); }, }; function CheckBox() { this._init.apply(this, arguments); } CheckBox.prototype = { __proto__: CheckBoxBase.prototype, _init: function(label, params, checkedState) { CheckBoxBase.prototype._init.call(this, checkedState, params); this._container = new CheckBoxContainer(); this.actor.set_child(this._container.actor); if (label) this.setLabel(label); }, setLabel: function(label) { this._container.label.set_text(label); }, getLabelActor: function() { return this._container.label; } }; Cinnamon-2.8.6/js/ui/messageTray.js0000664000175000017500000021700512625666430016107 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Atk = imports.gi.Atk; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const St = imports.gi.St; const GnomeSession = imports.misc.gnomeSession; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const Params = imports.misc.params; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const AppletManager = imports.ui.appletManager; const ANIMATION_TIME = .2; const NOTIFICATION_TIMEOUT = 4; const NOTIFICATION_CRITICAL_TIMEOUT_WITH_APPLET = 10; const SUMMARY_TIMEOUT = 1; const LONGER_SUMMARY_TIMEOUT = 4; const HIDE_TIMEOUT = 0.2; const LONGER_HIDE_TIMEOUT = 0.6; const MAX_SOURCE_TITLE_WIDTH = 180; // We delay hiding of the tray if the mouse is within MOUSE_LEFT_ACTOR_THRESHOLD // range from the point where it left the tray. const MOUSE_LEFT_ACTOR_THRESHOLD = 20; const State = { HIDDEN: 0, SHOWING: 1, SHOWN: 2, HIDING: 3 }; // These reasons are useful when we destroy the notifications received through // the notification daemon. We use EXPIRED for transient notifications that the // user did not interact with, DISMISSED for all other notifications that were // destroyed as a result of a user action, and SOURCE_CLOSED for the notifications // that were requested to be destroyed by the associated source. const NotificationDestroyedReason = { EXPIRED: 1, DISMISSED: 2, SOURCE_CLOSED: 3 }; // Message tray has its custom Urgency enumeration. LOW, NORMAL and CRITICAL // urgency values map to the corresponding values for the notifications received // through the notification daemon. HIGH urgency value is used for chats received // through the Telepathy client. const Urgency = { LOW: 0, NORMAL: 1, HIGH: 2, CRITICAL: 3 } function _fixMarkup(text, allowMarkup) { if (allowMarkup) { // Support &, ", ', < and >, escape all other // occurrences of '&'. let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); // Support , , and , escape anything else // so it displays as raw markup. _text = _text.replace(/<(?!\/?[biu]>)/g, '<'); try { Pango.parse_markup(_text, -1, ''); return _text; } catch (e) {} } // !allowMarkup, or invalid markup return GLib.markup_escape_text(text, -1); } function URLHighlighter(text, lineWrap, allowMarkup) { this._init(text, lineWrap, allowMarkup); } URLHighlighter.prototype = { _init: function(text, lineWrap, allowMarkup) { if (!text) text = ''; this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); this._linkColor = '#ccccff'; this.actor.connect('style-changed', Lang.bind(this, function() { let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); if (hasColor) { let linkColor = color.to_string().substr(0, 7); if (linkColor != this._linkColor) { this._linkColor = linkColor; this._highlightUrls(); } } })); if (lineWrap) { this.actor.clutter_text.line_wrap = true; this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; } this.setMarkup(text, allowMarkup); this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { // Don't try to URL highlight when invisible. // The MessageTray doesn't actually hide us, so // we need to check for paint opacities as well. if (!actor.visible || actor.get_paint_opacity() == 0) return false; // Keep Notification.actor from seeing this and taking // a pointer grab, which would block our button-release-event // handler, if an URL is clicked return this._findUrlAtPos(event) != -1; })); this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { if (!actor.visible || actor.get_paint_opacity() == 0) return false; let urlId = this._findUrlAtPos(event); if (urlId != -1) { let url = this._urls[urlId].url; if (url.indexOf(':') == -1) url = 'http://' + url; try { Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); return true; } catch (e) { // TODO: remove this after gnome 3 release Util.spawn(['gvfs-open', url]); return true; } } return false; })); this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { if (!actor.visible || actor.get_paint_opacity() == 0) return false; let urlId = this._findUrlAtPos(event); if (urlId != -1 && !this._cursorChanged) { global.set_cursor(Cinnamon.Cursor.POINTING_HAND); this._cursorChanged = true; } else if (urlId == -1) { global.unset_cursor(); this._cursorChanged = false; } return false; })); this.actor.connect('leave-event', Lang.bind(this, function() { if (!this.actor.visible || this.actor.get_paint_opacity() == 0) return; if (this._cursorChanged) { this._cursorChanged = false; global.unset_cursor(); } })); }, setMarkup: function(text, allowMarkup) { text = text ? _fixMarkup(text, allowMarkup) : ''; this._text = text; this.actor.clutter_text.set_markup(text); /* clutter_text.text contain text without markup */ this._urls = Util.findUrls(this.actor.clutter_text.text); this._highlightUrls(); }, _highlightUrls: function() { // text here contain markup let urls = Util.findUrls(this._text); let markup = ''; let pos = 0; for (let i = 0; i < urls.length; i++) { let url = urls[i]; let str = this._text.substr(pos, url.pos - pos); markup += str + '' + url.url + ''; pos = url.pos + url.url.length; } markup += this._text.substr(pos); this.actor.clutter_text.set_markup(markup); }, _findUrlAtPos: function(event) { let success; let [x, y] = event.get_coords(); [success, x, y] = this.actor.transform_stage_point(x, y); let find_pos = -1; for (let i = 0; i < this.actor.clutter_text.text.length; i++) { let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i); if (py > y || py + line_height < y || x < px) continue; find_pos = i; } if (find_pos != -1) { for (let i = 0; i < this._urls.length; i++) if (find_pos >= this._urls[i].pos && this._urls[i].pos + this._urls[i].url.length > find_pos) return i; } return -1; } }; function FocusGrabber() { this._init(); } FocusGrabber.prototype = { _init: function() { this.actor = null; this._hasFocus = false; // We use this._prevFocusedWindow and this._prevKeyFocusActor to return the // focus where it previously belonged after a focus grab, unless the user // has explicitly changed that. this._prevFocusedWindow = null; this._prevKeyFocusActor = null; this._focusActorChangedId = 0; this._stageInputModeChangedId = 0; this._capturedEventId = 0; this._togglingFocusGrabMode = false; Main.overview.connect('showing', Lang.bind(this, function() { this._toggleFocusGrabMode(); })); Main.overview.connect('hidden', Lang.bind(this, function() { this._toggleFocusGrabMode(); })); Main.expo.connect('showing', Lang.bind(this, function() { this._toggleFocusGrabMode(); })); Main.expo.connect('hidden', Lang.bind(this, function() { this._toggleFocusGrabMode(); })); }, grabFocus: function(actor) { if (this._hasFocus) return; this.actor = actor; this._prevFocusedWindow = global.display.focus_window; this._prevKeyFocusActor = global.stage.get_key_focus(); if (global.stage_input_mode == Cinnamon.StageInputMode.NONREACTIVE || global.stage_input_mode == Cinnamon.StageInputMode.NORMAL) global.set_stage_input_mode(Cinnamon.StageInputMode.FOCUSED); // Use captured-event to notice clicks outside the focused actor // without consuming them. this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); this._stageInputModeChangedId = global.connect('notify::stage-input-mode', Lang.bind(this, this._stageInputModeChanged)); this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged)); this._hasFocus = true; this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); this.emit('focus-grabbed'); }, _focusActorChanged: function() { let focusedActor = global.stage.get_key_focus(); if (!focusedActor || !this.actor.contains(focusedActor)) { this._prevKeyFocusActor = null; this.ungrabFocus(); } }, _stageInputModeChanged: function() { this.ungrabFocus(); }, _onCapturedEvent: function(actor, event) { let source = event.get_source(); switch (event.type()) { case Clutter.EventType.BUTTON_PRESS: if (!this.actor.contains(source) && !Main.layoutManager.keyboardBox.contains(source)) this.emit('button-pressed', source); break; case Clutter.EventType.KEY_PRESS: let symbol = event.get_key_symbol(); if (symbol == Clutter.Escape) { this.emit('escape-pressed'); return true; } break; } return false; }, ungrabFocus: function() { if (!this._hasFocus) return; if (this._focusActorChangedId > 0) { global.stage.disconnect(this._focusActorChangedId); this._focusActorChangedId = 0; } if (this._stageInputModeChangedId) { global.disconnect(this._stageInputModeChangedId); this._stageInputModeChangedId = 0; } if (this._capturedEventId > 0) { global.stage.disconnect(this._capturedEventId); this._capturedEventId = 0; } this._hasFocus = false; this.emit('focus-ungrabbed'); if (this._prevFocusedWindow && !global.display.focus_window) { global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time()); this._prevFocusedWindow = null; } if (this._prevKeyFocusActor) { global.stage.set_key_focus(this._prevKeyFocusActor); this._prevKeyFocusActor = null; } else { // We don't want to keep any actor inside the previously focused actor focused. let focusedActor = global.stage.get_key_focus(); if (focusedActor && this.actor.contains(focusedActor)) global.stage.set_key_focus(null); } if (!this._togglingFocusGrabMode) this.actor = null; }, // Because we grab focus differently in the overview // and in the main view, we need to change how it is // done when we move between the two. _toggleFocusGrabMode: function() { if (this._hasFocus) { this._togglingFocusGrabMode = true; this.ungrabFocus(); this.grabFocus(this.actor); this._togglingFocusGrabMode = false; } } } Signals.addSignalMethods(FocusGrabber.prototype); // Notification: // @source: the notification's Source // @title: the title // @banner: the banner text // @params: optional additional params // // Creates a notification. In the banner mode, the notification // will show an icon, @title (in bold) and @banner, all on a single // line (with @banner ellipsized if necessary). // // The notification will be expandable if either it has additional // elements that were added to it or if the @banner text did not // fit fully in the banner mode. When the notification is expanded, // the @banner text from the top line is always removed. The complete // @banner text is added as the first element in the content section, // unless 'customContent' parameter with the value 'true' is specified // in @params. // // Additional notification content can be added with addActor() and // addBody() methods. The notification content is put inside a // scrollview, so if it gets too tall, the notification will scroll // rather than continue to grow. In addition to this main content // area, there is also a single-row action area, which is not // scrolled and can contain a single actor. The action area can // be set by calling setActionArea() method. There is also a // convenience method addButton() for adding a button to the action // area. // // @params can contain values for 'customContent', 'body', 'icon', // 'titleMarkup', 'bannerMarkup', 'bodyMarkup', and 'clear' // parameters. // // If @params contains a 'customContent' parameter with the value %true, // then @banner will not be shown in the body of the notification when the // notification is expanded and calls to update() will not clear the content // unless 'clear' parameter with value %true is explicitly specified. // // If @params contains a 'body' parameter, then that text will be added to // the content area (as with addBody()). // // By default, the icon shown is created by calling // source.createNotificationIcon(). However, if @params contains an 'icon' // parameter, the passed in icon will be used. // // If @params contains a 'titleMarkup', 'bannerMarkup', or // 'bodyMarkup' parameter with the value %true, then the corresponding // element is assumed to use pango markup. If the parameter is not // present for an element, then anything that looks like markup in // that element will appear literally in the output. // // If @params contains a 'clear' parameter with the value %true, then // the content and the action area of the notification will be cleared. // The content area is also always cleared if 'customContent' is false // because it might contain the @banner that didn't fit in the banner mode. function Notification(source, title, banner, params) { this._init(source, title, banner, params); } Notification.prototype = { IMAGE_SIZE: 125, _init: function(source, title, banner, params) { this.source = source; this.title = title; this.urgency = Urgency.NORMAL; this.resident = false; // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name this.isTransient = false; this.expanded = false; this._destroyed = false; this._useActionIcons = false; this._customContent = false; this._bannerBodyText = null; this._bannerBodyMarkup = false; this._titleFitsInBannerMode = true; this._titleDirection = St.TextDirection.NONE; this._spacing = 0; this._imageBin = null; this._timestamp = new Date(); this._inNotificationBin = false; this.enter_id = 0; this.leave_id = 0; source.connect('destroy', Lang.bind(this, function (source, reason) { this.destroy(reason); })); this.actor = new St.Button({ accessible_role: Atk.Role.NOTIFICATION }); this.actor._delegate = this; this.actor._parent_container = null; this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); // Transparency on mouse over? if (Main.messageTray.fadeOnMouseover) { // Register to every notification as we intend to support multiple notifications on screen. this.enter_id = this.actor.connect('enter-event', Lang.bind(this, function() { Tweener.addTween(this.actor, { opacity: ((Main.messageTray.fadeOpacity / 100) * 255).clamp(0, 255), time: ANIMATION_TIME, transition: 'easeOutQuad' }); })); this.leave_id = this.actor.connect('leave-event', Lang.bind(this, function() { Tweener.addTween(this.actor, { opacity: (this._table.get_theme_node().get_length('opacity') / global.ui_scale) || 255, time: ANIMATION_TIME, transition: 'easeOutQuad' }); })); } this._table = new St.Table({ name: 'notification', reactive: true }); this._table.connect('style-changed', Lang.bind(this, this._styleChanged)); this.actor.set_child(this._table); this._buttonFocusManager = St.FocusManager.get_for_stage(global.stage); // The first line should have the title, followed by the // banner text, but ellipsized if they won't both fit. We can't // make St.Table or St.BoxLayout do this the way we want (don't // show banner at all if title needs to be ellipsized), so we // use Cinnamon.GenericContainer. this._bannerBox = new Cinnamon.GenericContainer(); this._bannerBox.connect('get-preferred-width', Lang.bind(this, this._bannerBoxGetPreferredWidth)); this._bannerBox.connect('get-preferred-height', Lang.bind(this, this._bannerBoxGetPreferredHeight)); this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); this._table.add(this._bannerBox, { row: 0, col: 1, col_span: 2, x_expand: false, y_expand: false, y_fill: false }); // This is an empty cell that overlaps with this._bannerBox cell to ensure // that this._bannerBox cell expands horizontally, while not forcing the // this._imageBin that is also in col: 2 to expand horizontally. this._table.add(new St.Bin(), { row: 0, col: 2, y_expand: false, y_fill: false }); this._timeLabel = new St.Label(); this._titleLabel = new St.Label(); this._bannerBox.add_actor(this._titleLabel); this._bannerBox.add_actor(this._timeLabel); this._timeLabel.hide(); this._bannerUrlHighlighter = new URLHighlighter(); this._bannerLabel = this._bannerUrlHighlighter.actor; this._bannerBox.add_actor(this._bannerLabel); this.update(title, banner, params); }, // update: // @title: the new title // @banner: the new banner // @params: as in the Notification constructor // // Updates the notification by regenerating its icon and updating // the title/banner. If @params.clear is %true, it will also // remove any additional actors/action buttons previously added. update: function(title, banner, params) { this._timestamp = new Date(); this._inNotificationBin = false; params = Params.parse(params, { customContent: false, body: null, icon: null, titleMarkup: false, bannerMarkup: false, bodyMarkup: false, clear: false }); this._customContent = params.customContent; let oldFocus = global.stage.key_focus; if (this._icon && (params.icon || params.clear)) { this._icon.destroy(); this._icon = null; } // We always clear the content area if we don't have custom // content because it might contain the @banner that didn't // fit in the banner mode. if (this._scrollArea && (!this._customContent || params.clear)) { if (oldFocus && this._scrollArea.contains(oldFocus)) this.actor.grab_key_focus(); this._scrollArea.destroy(); this._scrollArea = null; this._contentArea = null; } if (this._actionArea && params.clear) { if (oldFocus && this._actionArea.contains(oldFocus)) this.actor.grab_key_focus(); this._actionArea.destroy(); this._actionArea = null; this._buttonBox = null; } if (this._imageBin && params.clear) this.unsetImage(); if (!this._scrollArea && !this._actionArea && !this._imageBin) this._table.remove_style_class_name('multi-line-notification'); if (!this._icon) { this._icon = params.icon || this.source.createNotificationIcon(); this._table.add(this._icon, { row: 0, col: 0, x_expand: false, y_expand: false, y_fill: false, y_align: St.Align.START }); } this.title = title; title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : ''; this._titleLabel.clutter_text.set_markup('' + title + ''); this._timeLabel.clutter_text.set_markup(this._timestamp.toLocaleTimeString()); this._timeLabel.hide(); if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL) this._titleDirection = St.TextDirection.RTL; else this._titleDirection = St.TextDirection.LTR; // Let the title's text direction control the overall direction // of the notification - in case where different scripts are used // in the notification, this is the right thing for the icon, and // arguably for action buttons as well. Labels other than the title // will be allocated at the available width, so that their alignment // is done correctly automatically. this._table.set_direction(this._titleDirection); // Unless the notification has custom content, we save this._bannerBodyText // to add it to the content of the notification if the notification is // expandable due to other elements in its content area or due to the banner // not fitting fully in the single-line mode. this._bannerBodyText = this._customContent ? null : banner; this._bannerBodyMarkup = params.bannerMarkup; banner = banner ? banner.replace(/\n/g, ' ') : ''; this._bannerUrlHighlighter.setMarkup(banner, params.bannerMarkup); this._bannerLabel.queue_relayout(); // Add the bannerBody now if we know for sure we'll need it if (this._bannerBodyText && this._bannerBodyText.indexOf('\n') > -1) this._addBannerBody(); if (params.body) this.addBody(params.body, params.bodyMarkup); this._updated(); }, setIconVisible: function(visible) { this._icon.visible = visible; }, _createScrollArea: function() { this._table.add_style_class_name('multi-line-notification'); this._scrollArea = new St.ScrollView({ name: 'notification-scrollview', vscrollbar_policy: Gtk.PolicyType.NEVER, hscrollbar_policy: Gtk.PolicyType.NEVER, style_class: 'vfade' }); this._table.add(this._scrollArea, { row: 1, col: 2 }); this._updateLastColumnSettings(); this._contentArea = new St.BoxLayout({ name: 'notification-body', vertical: true }); this._scrollArea.add_actor(this._contentArea); // If we know the notification will be expandable, we need to add // the banner text to the body as the first element. this._addBannerBody(); }, // addActor: // @actor: actor to add to the body of the notification // // Appends @actor to the notification's body addActor: function(actor, style) { if (!this._scrollArea) { this._createScrollArea(); } this._contentArea.add(actor, style ? style : {}); this._updated(); }, // addBody: // @text: the text // @markup: %true if @text contains pango markup // @style: style to use when adding the actor containing the text // // Adds a multi-line label containing @text to the notification. // // Return value: the newly-added label addBody: function(text, markup, style) { let label = new URLHighlighter(text, true, markup); this.addActor(label.actor, style); return label.actor; }, _addBannerBody: function() { if (this._bannerBodyText) { let text = this._bannerBodyText; this._bannerBodyText = null; this.addBody(text, this._bannerBodyMarkup); } }, // scrollTo: // @side: St.Side.TOP or St.Side.BOTTOM // // Scrolls the content area (if scrollable) to the indicated edge scrollTo: function(side) { let adjustment = this._scrollArea.vscroll.adjustment; if (side == St.Side.TOP) adjustment.value = adjustment.lower; else if (side == St.Side.BOTTOM) adjustment.value = adjustment.upper; }, // setActionArea: // @actor: the actor // @props: (option) St.Table child properties // // Puts @actor into the action area of the notification, replacing // the previous contents setActionArea: function(actor, props) { if (this._actionArea) { this._actionArea.destroy(); this._actionArea = null; if (this._buttonBox) this._buttonBox = null; } else { this._addBannerBody(); } this._actionArea = actor; if (!props) props = {}; props.row = 2; props.col = 2; this._table.add_style_class_name('multi-line-notification'); this._table.add(this._actionArea, props); this._updateLastColumnSettings(); this._updated(); }, _updateLastColumnSettings: function() { if (this._scrollArea) this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1, col_span: this._imageBin ? 1 : 2 }); if (this._actionArea) this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1, col_span: this._imageBin ? 1 : 2 }); }, setImage: function(image) { if (this._imageBin) this.unsetImage(); this._imageBin = new St.Bin(); this._imageBin.child = image; this._imageBin.opacity = 230; this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('notification-with-image'); this._addBannerBody(); this._updateLastColumnSettings(); this._table.add(this._imageBin, { row: 1, col: 1, row_span: 2, x_expand: false, y_expand: false, x_fill: false, y_fill: false }); }, unsetImage: function() { if (this._imageBin) { this._table.remove_style_class_name('notification-with-image'); this._table.remove_actor(this._imageBin); this._imageBin = null; this._updateLastColumnSettings(); if (!this._scrollArea && !this._actionArea) this._table.remove_style_class_name('multi-line-notification'); } }, // addButton: // @id: the action ID // @label: the label for the action's button // // Adds a button with the given @label to the notification. All // action buttons will appear in a single row at the bottom of // the notification. // // If the button is clicked, the notification will emit the // %action-invoked signal with @id as a parameter addButton: function(id, label) { if (!this._buttonBox) { let box = new St.BoxLayout({ name: 'notification-actions' }); this.setActionArea(box, { x_expand: true, y_expand: false, x_fill: true, y_fill: false, x_align: St.Align.START }); this._buttonBox = box; } let button = new St.Button({ can_focus: true }); if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) { button.add_style_class_name('notification-icon-button'); button.child = new St.Icon({ icon_name: id }); } else { button.add_style_class_name('notification-button'); button.label = label; } if (this._buttonBox.get_children().length > 0) this._buttonFocusManager.remove_group(this._buttonBox); this._buttonBox.add(button); this._buttonFocusManager.add_group(this._buttonBox); button.connect('clicked', Lang.bind(this, this._onActionInvoked, id)); this._updated(); }, setUrgency: function(urgency) { this.urgency = urgency; }, setResident: function(resident) { this.resident = resident; }, setTransient: function(isTransient) { this.isTransient = isTransient; }, setUseActionIcons: function(useIcons) { this._useActionIcons = useIcons; }, _styleChanged: function() { this._spacing = this._table.get_theme_node().get_length('spacing-columns'); }, _bannerBoxGetPreferredWidth: function(actor, forHeight, alloc) { let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight); let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight); let [timeMin, timeNat] = this._timeLabel.get_preferred_width(forHeight); if (this._inNotificationBin) { alloc.min_size = Math.max(titleMin, timeMin); alloc.natural_size = Math.max(titleNat, timeNat) + this._spacing + bannerNat; } else { alloc.min_size = titleMin; alloc.natural_size = titleNat + this._spacing + bannerNat; } }, _bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) { if (this._inNotificationBin) { let [titleMin, titleNat] = this._titleLabel.get_preferred_height(forWidth); let [timeMin, timeNat] = this._timeLabel.get_preferred_height(forWidth); alloc.min_size = titleMin + timeMin; alloc.natural_size = titleNat + timeNat; } else { [alloc.min_size, alloc.natural_size] = this._titleLabel.get_preferred_height(forWidth); } }, _bannerBoxAllocate: function(actor, box, flags) { let availWidth = box.x2 - box.x1; let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1); let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth); let [timeMinW, timeNatW] = this._timeLabel.get_preferred_width(-1); let [timeMinH, timeNatH] = this._timeLabel.get_preferred_height(availWidth); let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth); let titleBox = new Clutter.ActorBox(); let timeBox = new Clutter.ActorBox(); let titleBoxW = Math.min(titleNatW, availWidth); let timeBoxW = Math.min(timeNatW, availWidth); if (this._titleDirection == St.TextDirection.RTL) { titleBox.x1 = availWidth - titleBoxW; titleBox.x2 = availWidth; timeBox.x1 = availWidth - timeBoxW; timeBox.x2 = availWidth; } else { titleBox.x1 = 0; timeBox.x1 = 0; titleBox.x2 = titleBoxW; timeBox.x2 = timeBoxW; } if (this._inNotificationBin) { timeBox.y1 = 0; timeBox.y2 = timeNatH; titleBox.y1 = timeNatH; titleBox.y2 = timeNatH + titleNatH; } else { titleBox.y1 = 0; titleBox.y2 = titleNatH; } this._titleLabel.allocate(titleBox, flags); if (this._inNotificationBin) { this._timeLabel.allocate(timeBox, flags); } this._titleFitsInBannerMode = (titleNatW <= availWidth); let bannerFits = true; if (titleBoxW + this._spacing > availWidth) { this._bannerLabel.opacity = 0; bannerFits = false; } else { let bannerBox = new Clutter.ActorBox(); if (this._titleDirection == St.TextDirection.RTL) { bannerBox.x1 = 0; bannerBox.x2 = titleBox.x1 - this._spacing; bannerFits = (bannerBox.x2 - bannerNatW >= 0); } else { bannerBox.x1 = titleBox.x2 + this._spacing; bannerBox.x2 = availWidth; bannerFits = (bannerBox.x1 + bannerNatW <= availWidth); } if (this._inNotificationBin) { bannerBox.y1 = timeNatH; bannerBox.y2 = timeNatH + titleNatH; } else { bannerBox.y1 = 0; bannerBox.y2 = titleNatH; } this._bannerLabel.allocate(bannerBox, flags); // Make _bannerLabel visible if the entire notification // fits on one line, or if the notification is currently // unexpanded and only showing one line anyway. if (!this.expanded || (bannerFits && this._table.row_count == 1)) this._bannerLabel.opacity = 255; } // If the banner doesn't fully fit in the banner box, we possibly need to add the // banner to the body. We can't do that from here though since that will force a // relayout, so we add it to the main loop. if (!bannerFits && this._canExpandContent()) Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { if (this._canExpandContent()) { this._addBannerBody(); this._table.add_style_class_name('multi-line-notification'); this._updated(); } return false; })); }, _canExpandContent: function() { return this._bannerBodyText || (!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification')); }, _updated: function() { if (this.expanded) this.expand(false); }, expand: function(animate) { this.expanded = true; // The banner is never shown when the title did not fit, so this // can be an if-else statement. if (!this._titleFitsInBannerMode) { // Remove ellipsization from the title label and make it wrap so that // we show the full title when the notification is expanded. this._titleLabel.clutter_text.line_wrap = true; this._titleLabel.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; } else if (this._table.row_count > 1 && this._bannerLabel.opacity != 0) { // We always hide the banner if the notification has additional content. // // We don't need to wrap the banner that doesn't fit the way we wrap the // title that doesn't fit because we won't have a notification with // row_count=1 that has a banner that doesn't fully fit. We'll either add // that banner to the content of the notification in _bannerBoxAllocate() // or the notification will have custom content. if (animate) Tweener.addTween(this._bannerLabel, { opacity: 0, time: ANIMATION_TIME, transition: 'easeOutQuad' }); else this._bannerLabel.opacity = 0; } this.emit('expanded'); }, collapseCompleted: function() { if (this._destroyed) return; this.expanded = false; // Make sure we don't line wrap the title, and ellipsize it instead. this._titleLabel.clutter_text.line_wrap = false; this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END; // Restore banner opacity in case the notification is shown in the // banner mode again on update. this._bannerLabel.opacity = 255; this.emit('collapsed'); }, _onActionInvoked: function(actor, mouseButtonClicked, id) { this.emit('action-invoked', id); if (!this.resident) { // We don't hide a resident notification when the user invokes one of its actions, // because it is common for such notifications to update themselves with new // information based on the action. We'd like to display the updated information // in place, rather than pop-up a new notification. this.emit('done-displaying'); this.destroy(); } }, _onClicked: function() { this.emit('clicked'); // We hide all types of notifications once the user clicks on them because the common // outcome of clicking should be the relevant window being brought forward and the user's // attention switching to the window. this.emit('done-displaying'); if (!this.resident) this.destroy(); }, _onDestroy: function() { if (this._destroyed) return; this._destroyed = true; if (!this._destroyedReason) this._destroyedReason = NotificationDestroyedReason.DISMISSED; this.emit('destroy', this._destroyedReason); }, destroy: function(reason) { this._destroyedReason = reason; this.actor.destroy(); this.actor._delegate = null; } }; Signals.addSignalMethods(Notification.prototype); function Source(title) { this._init(title); } Source.prototype = { ICON_SIZE: 24, _init: function(title) { this.title = title; this.actor = new Cinnamon.GenericContainer(); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('destroy', Lang.bind(this, function() { this._actorDestroyed = true; })); this._actorDestroyed = false; this._counterLabel = new St.Label(); this._counterBin = new St.Bin({ style_class: 'summary-source-counter', child: this._counterLabel }); this._counterBin.hide(); this._iconBin = new St.Bin({ x_fill: true, y_fill: true }); this.actor.add_actor(this._iconBin); this.actor.add_actor(this._counterBin); this.isTransient = false; this.isChat = false; this.notifications = []; }, _getPreferredWidth: function (actor, forHeight, alloc) { let [min, nat] = this._iconBin.get_preferred_width(forHeight); alloc.min_size = min; alloc.nat_size = nat; }, _getPreferredHeight: function (actor, forWidth, alloc) { let [min, nat] = this._iconBin.get_preferred_height(forWidth); alloc.min_size = min; alloc.nat_size = nat; }, _allocate: function(actor, box, flags) { // the iconBin should fill our entire box this._iconBin.allocate(box, flags); let childBox = new Clutter.ActorBox(); let [minWidth, minHeight, naturalWidth, naturalHeight] = this._counterBin.get_preferred_size(); let direction = this.actor.get_direction(); if (direction == St.TextDirection.LTR) { // allocate on the right in LTR childBox.x1 = box.x2 - naturalWidth; childBox.x2 = box.x2; } else { // allocate on the left in RTL childBox.x1 = 0; childBox.x2 = naturalWidth; } childBox.y1 = box.y2 - naturalHeight; childBox.y2 = box.y2; this._counterBin.allocate(childBox, flags); }, _setCount: function(count, visible) { if (isNaN(parseInt(count))) throw new Error("Invalid notification count: " + count); if (this._actorDestroyed) return; this._counterBin.visible = visible; this._counterLabel.set_text(count.toString()); }, _updateCount: function() { let count = this.notifications.length; this._setCount(count, count > 1); }, setTransient: function(isTransient) { this.isTransient = isTransient; }, setTitle: function(newTitle) { this.title = newTitle; this.emit('title-changed'); }, // Called to create a new icon actor (of size this.ICON_SIZE). // Must be overridden by the subclass if you do not pass icons // explicitly to the Notification() constructor. createNotificationIcon: function() { throw new Error('no implementation of createNotificationIcon in ' + this); }, // Unlike createNotificationIcon, this always returns the same actor; // there is only one summary icon actor for a Source. getSummaryIcon: function() { return this.actor; }, pushNotification: function(notification) { if (this.notifications.indexOf(notification) < 0) { this.notifications.push(notification); this.emit('notification-added', notification); } notification.connect('clicked', Lang.bind(this, this.open)); notification.connect('destroy', Lang.bind(this, function () { let index = this.notifications.indexOf(notification); if (index < 0) return; this.notifications.splice(index, 1); if (this.notifications.length == 0) this._lastNotificationRemoved(); this._updateCount(); })); this._updateCount(); }, notify: function(notification) { this.pushNotification(notification); this.emit('notify', notification); }, destroy: function(reason) { this.emit('destroy', reason); }, // A subclass can redefine this to "steal" clicks from the // summaryitem; Use Clutter.get_current_event() to get the // details, return true to prevent the default handling from // ocurring. handleSummaryClick: function() { return false; }, //// Protected methods //// // The subclass must call this at least once to set the summary icon. _setSummaryIcon: function(icon) { if (this._iconBin.child) this._iconBin.child.destroy(); this._iconBin.child = icon; }, // Default implementation is to do nothing, but subclasses can override open: function(notification) { }, destroyNonResidentNotifications: function() { for (let i = this.notifications.length - 1; i >= 0; i--) if (!this.notifications[i].resident) this.notifications[i].destroy(); this._updateCount(); }, // Default implementation is to destroy this source, but subclasses can override _lastNotificationRemoved: function() { this.destroy(); } }; Signals.addSignalMethods(Source.prototype); function SummaryItem(source) { this._init(source); } SummaryItem.prototype = { _init: function(source) { this.source = source; this.source.connect('notification-added', Lang.bind(this, this._notificationAddedToSource)); this.actor = new St.Button({ style_class: 'summary-source-button', y_fill: true, reactive: true, button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO | St.ButtonMask.THREE, track_hover: true }); this._sourceBox = new St.BoxLayout({ style_class: 'summary-source' }); this._sourceIcon = source.getSummaryIcon(); this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true, clip_to_allocation: true }); this._sourceTitle = new St.Label({ style_class: 'source-title', text: source.title }); this._sourceTitle.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._sourceTitleBin.child = this._sourceTitle; this._sourceTitleBin.width = 0; this.source.connect('title-changed', Lang.bind(this, function() { this._sourceTitle.text = source.title; })); this._sourceBox.add(this._sourceIcon, { y_fill: false }); this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false }); this.actor.child = this._sourceBox; this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview', vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC, hscrollbar_policy: Gtk.PolicyType.NEVER, style_class: 'vfade' }); this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack', vertical: true }); this.notificationStackView.add_actor(this.notificationStack); this._stackedNotifications = []; this._oldMaxScrollAdjustment = 0; this.notificationStackView.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { let currentValue = adjustment.value + adjustment.page_size; if (currentValue == this._oldMaxScrollAdjustment) this.scrollTo(St.Side.BOTTOM); this._oldMaxScrollAdjustment = adjustment.upper; })); this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu', vertical: true }); let item; item = new PopupMenu.PopupMenuItem(_("Open")); item.connect('activate', Lang.bind(this, function() { source.open(); this.emit('done-displaying-content'); })); this.rightClickMenu.add(item.actor); item = new PopupMenu.PopupMenuItem(_("Remove")); item.connect('activate', Lang.bind(this, function() { source.destroy(); this.emit('done-displaying-content'); })); this.rightClickMenu.add(item.actor); let focusManager = St.FocusManager.get_for_stage(global.stage); focusManager.add_group(this.rightClickMenu); }, // getTitleNaturalWidth, getTitleWidth, and setTitleWidth include // the spacing between the icon and title (which is actually // _sourceTitle's padding-left) as part of the width. getTitleNaturalWidth: function() { let [minWidth, naturalWidth] = this._sourceTitle.get_preferred_width(-1); return Math.min(naturalWidth, MAX_SOURCE_TITLE_WIDTH); }, getTitleWidth: function() { return this._sourceTitleBin.width; }, setTitleWidth: function(width) { width = Math.round(width); if (width != this._sourceTitleBin.width) this._sourceTitleBin.width = width; }, setEllipsization: function(mode) { this._sourceTitle.clutter_text.ellipsize = mode; }, prepareNotificationStackForShowing: function() { if (this.notificationStack.get_children().length > 0) return; for (let i = 0; i < this.source.notifications.length; i++) { this._appendNotificationToStack(this.source.notifications[i]); } }, doneShowingNotificationStack: function() { let notificationActors = this.notificationStack.get_children(); for (let i = 0; i < this._stackedNotifications.length; i++) { let stackedNotification = this._stackedNotifications[i]; let notification = stackedNotification.notification; notification.collapseCompleted(); notification.disconnect(stackedNotification.notificationExpandedId); notification.disconnect(stackedNotification.notificationDoneDisplayingId); notification.disconnect(stackedNotification.notificationDestroyedId); if (notification.actor.get_parent() == this.notificationStack) this.notificationStack.remove_actor(notification.actor); notification.setIconVisible(true); } this._stackedNotifications = []; }, _notificationAddedToSource: function(source, notification) { if (this.notificationStack.mapped) this._appendNotificationToStack(notification); }, _appendNotificationToStack: function(notification) { let stackedNotification = {}; stackedNotification.notification = notification; stackedNotification.notificationExpandedId = notification.connect('expanded', Lang.bind(this, this._contentUpdated)); stackedNotification.notificationDoneDisplayingId = notification.connect('done-displaying', Lang.bind(this, this._notificationDoneDisplaying)); stackedNotification.notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyed)); this._stackedNotifications.push(stackedNotification); if (this.notificationStack.get_children().length > 0) notification.setIconVisible(false); this.notificationStack.add(notification.actor); notification.expand(false); }, // scrollTo: // @side: St.Side.TOP or St.Side.BOTTOM // // Scrolls the notifiction stack to the indicated edge scrollTo: function(side) { let adjustment = this.notificationStackView.vscroll.adjustment; if (side == St.Side.TOP) adjustment.value = adjustment.lower; else if (side == St.Side.BOTTOM) adjustment.value = adjustment.upper; }, _contentUpdated: function() { this.emit('content-updated'); }, _notificationDoneDisplaying: function() { this.emit('done-displaying-content'); }, _notificationDestroyed: function(notification) { for (let i = 0; i < this._stackedNotifications.length; i++) { if (this._stackedNotifications[i].notification == notification) { let stackedNotification = this._stackedNotifications[i]; notification.disconnect(stackedNotification.notificationExpandedId); notification.disconnect(stackedNotification.notificationDoneDisplayingId); notification.disconnect(stackedNotification.notificationDestroyedId); this._stackedNotifications.splice(i, 1); this._contentUpdated(); break; } } if (this.notificationStack.get_children().length > 0) this.notificationStack.get_children()[0]._delegate.setIconVisible(true); } }; Signals.addSignalMethods(SummaryItem.prototype); function MessageTray() { this._init(); } MessageTray.prototype = { _init: function() { this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { this._onStatusChanged(proxy.status); })); this._userStatus = GnomeSession.PresenceStatus.AVAILABLE; this._busy = false; this._backFromAway = false; this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) { this._onStatusChanged(status); })); this._notificationBin = new St.Bin(); this._notificationBin.hide(); this._notificationQueue = []; this._notification = null; this._notificationClickedId = 0; this._pointerBarrier = 0; this._focusGrabber = new FocusGrabber(); this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock)); this._focusGrabber.connect('button-pressed', Lang.bind(this, function(focusGrabber, source) { this._focusGrabber.ungrabFocus(); })); this._focusGrabber.connect('escape-pressed', Lang.bind(this, this._escapeTray)); this._trayState = State.HIDDEN; this._locked = false; this._traySummoned = false; this._useLongerTrayLeftTimeout = false; this._trayLeftTimeoutId = 0; this._pointerInTray = false; this._pointerInKeyboard = false; this._notificationState = State.HIDDEN; this._notificationTimeoutId = 0; this._notificationExpandedId = 0; this._notificationRemoved = false; this._reNotifyAfterHideNotification = null; this._sources = []; Main.layoutManager.addChrome(this._notificationBin); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition)); // Settings this.settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.notifications" }) function setting(self, source, camelCase, dashed) { function updater() { self[camelCase] = source.get_boolean(dashed); } source.connect('changed::'+dashed, updater); updater(); } setting(this, this.settings, "_notificationsEnabled", "display-notifications"); setting(this, this.settings, "fadeOnMouseover", "fade-on-mouseover"); this.fadeOpacity = this.settings.get_int("fade-opacity"); this.settings.connect("changed::fade-opacity", Lang.bind(this, function() { this.fadeOpacity = this.settings.get_int("fade-opacity"); })) this._setSizePosition(); let updateLockState = Lang.bind(this, function() { if (this._locked) { this._unlock(); } else { this._updateState(); } }); Main.overview.connect('showing', updateLockState); Main.overview.connect('hiding', updateLockState); Main.expo.connect('showing', updateLockState); Main.expo.connect('hiding', updateLockState); }, _setSizePosition: function() { //let monitor = Main.layoutManager.primaryMonitor; //this._notificationBin.x = monitor.width - 500; //this._notificationBin.width = monitor.width; }, contains: function(source) { return this._getSourceIndex(source) >= 0; }, _getSourceIndex: function(source) { return this._sources.indexOf(source); }, add: function(source) { if (this.contains(source)) { log('Trying to re-add source ' + source.title); return; } source.connect('notify', Lang.bind(this, this._onNotify)); source.connect('destroy', Lang.bind(this, this._onSourceDestroy)); }, _onSourceDestroy: function(source) { let index = this._getSourceIndex(source); if (index == -1) return; this._sources.splice(index, 1); let needUpdate = false; if (this._notification && this._notification.source == source) { this._updateNotificationTimeout(0); this._notificationRemoved = true; needUpdate = true; } if (needUpdate) this._updateState(); }, _onNotificationDestroy: function(notification) { if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) { this._updateNotificationTimeout(0); this._notificationRemoved = true; this._updateState(); return; } let index = this._notificationQueue.indexOf(notification); notification.destroy(); if (index != -1) this._notificationQueue.splice(index, 1); }, _lock: function() { this._locked = true; }, _unlock: function() { if (!this._locked) return; this._locked = false; this._updateState(); }, toggle: function() { this._traySummoned = !this._traySummoned; this._updateState(); }, hide: function() { this._traySummoned = false; this._updateState(); }, _onNotify: function(source, notification) { if (this._notification == notification) { // If a notification that is being shown is updated, we update // how it is shown and extend the time until it auto-hides. // If a new notification is updated while it is being hidden, // we stop hiding it and show it again. this._updateShowingNotification(); } else if (this._notificationQueue.indexOf(notification) < 0) { notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy)); this._notificationQueue.push(notification); this._notificationQueue.sort(function(notification1, notification2) { return (notification2.urgency - notification1.urgency); }); } this._updateState(); }, _onStatusChanged: function(status) { this._backFromAway = (this._userStatus == GnomeSession.PresenceStatus.IDLE && this._userStatus != status); this._userStatus = status; if (status == GnomeSession.PresenceStatus.BUSY) { // remove notification and allow the summary to be closed now this._updateNotificationTimeout(0); this._busy = true; } else if (status != GnomeSession.PresenceStatus.IDLE) { // We preserve the previous value of this._busy if the status turns to IDLE // so that we don't start showing notifications queued during the BUSY state // as the screensaver gets activated. this._busy = false; } this._updateState(); }, _escapeTray: function() { this._unlock(); this._pointerInTray = false; this._updateNotificationTimeout(0); this._updateState(); }, // All of the logic for what happens when occurs here; the various // event handlers merely update variables such as // 'this._pointerInTray', 'this._summaryState', etc, and // _updateState() figures out what (if anything) needs to be done // at the present time. _updateState: function() { // Notifications let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL; let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent); let notificationExpanded = this._notificationBin.y < 0; let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded) ) || this._notificationRemoved; let canShowNotification = notificationsPending && this._notificationsEnabled; if (this._notificationState == State.HIDDEN) { if (canShowNotification) { this._showNotification(); } else if (!this._notificationsEnabled) { if (notificationsPending) { this._notification = this._notificationQueue.shift(); if (AppletManager.get_role_provider_exists(AppletManager.Roles.NOTIFICATIONS)) { this.emit('notify-applet-update', this._notification); } } } } else if (this._notificationState == State.SHOWN) { if (notificationExpired) this._hideNotification(); } }, _tween: function(actor, statevar, value, params) { let onComplete = params.onComplete; let onCompleteScope = params.onCompleteScope; let onCompleteParams = params.onCompleteParams; params.onComplete = this._tweenComplete; params.onCompleteScope = this; params.onCompleteParams = [statevar, value, onComplete, onCompleteScope, onCompleteParams]; Tweener.addTween(actor, params); let valuing = (value == State.SHOWN) ? State.SHOWING : State.HIDING; this[statevar] = valuing; }, _tweenComplete: function(statevar, value, onComplete, onCompleteScope, onCompleteParams) { this[statevar] = value; if (onComplete) onComplete.apply(onCompleteScope, onCompleteParams); this._updateState(); }, _showNotification: function() { this._notificationTimeoutId = 1; // this prevents a race condition with the messagetray wanting // to hide a notification before it's done showing it, when updating from applet this._notification = this._notificationQueue.shift(); if (this._notification.actor._parent_container) { this._notification.collapseCompleted(); this._notification.actor._parent_container.remove_actor(this._notification.actor); } this._notificationClickedId = this._notification.connect('done-displaying', Lang.bind(this, this._escapeTray)); this._notificationBin.child = this._notification.actor; this._notificationBin.opacity = 0; let monitor = Main.layoutManager.primaryMonitor; let panel = Main.panelManager.getPanel(0, false); // We only want the top panel in monitor 0 let height = 5; if (panel) height += panel.actor.get_height(); this._notificationBin.y = monitor.y + height * 2; // Notifications appear from here (for the animation) let margin = this._notification._table.get_theme_node().get_length('margin-from-right-edge-of-screen'); this._notificationBin.x = monitor.x + monitor.width - this._notification._table.width - margin; this._notificationBin.show(); this._updateShowingNotification(); let [x, y, mods] = global.get_pointer(); // We save the position of the mouse at the time when we started showing the notification // in order to determine if the notification popped up under it. We make that check if // the user starts moving the mouse and _onTrayHoverChanged() gets called. We don't // expand the notification if it just happened to pop up under the mouse unless the user // explicitly mouses away from it and then mouses back in. this._showNotificationMouseX = x; this._showNotificationMouseY = y; // We save the y coordinate of the mouse at the time when we started showing the notification // and then we update it in _notifiationTimeout() if the mouse is moving towards the // notification. We don't pop down the notification if the mouse is moving towards it. this._lastSeenMouseY = y; }, _updateShowingNotification: function() { Tweener.removeTweens(this._notificationBin); this._expandNotification(true); // We tween all notifications to full opacity. This ensures that both new notifications and // notifications that might have been in the process of hiding get full opacity. // // We tween any notification showing in the banner mode to banner height (this._notificationBin.y = 0). // This ensures that both new notifications and notifications in the banner mode that might // have been in the process of hiding are shown with the banner height. // // We use this._showNotificationCompleted() onComplete callback to extend the time the updated // notification is being shown. // // We don't set the y parameter for the tween for expanded notifications because // this._expandNotification() will result in getting this._notificationBin.y set to the appropriate // fully expanded value. let tweenParams = { opacity: 255, time: ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._showNotificationCompleted, onCompleteScope: this }; let monitor = Main.layoutManager.primaryMonitor; let panel = Main.panelManager.getPanel(0, false); // We only want the top panel in monitor 0 let height = 5; if (panel) height += panel.actor.get_height(); if (!this._notification.expanded) tweenParams.y = monitor.y + height; this._tween(this._notificationBin, '_notificationState', State.SHOWN, tweenParams); }, _showNotificationCompleted: function() { this._notificationTimeoutId = 0; if (this._notification.urgency != Urgency.CRITICAL) { this._updateNotificationTimeout(NOTIFICATION_TIMEOUT * 1000); } else if (AppletManager.get_role_provider_exists(AppletManager.Roles.NOTIFICATIONS)) { this._updateNotificationTimeout(NOTIFICATION_CRITICAL_TIMEOUT_WITH_APPLET * 1000); } }, _updateNotificationTimeout: function(timeout) { if (this._notificationTimeoutId) { Mainloop.source_remove(this._notificationTimeoutId); this._notificationTimeoutId = 0; } if (timeout > 0) this._notificationTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._notificationTimeout)); }, _notificationTimeout: function() { let [x, y, mods] = global.get_pointer(); if (y > this._lastSeenMouseY + 10) { // The mouse is moving towards the notification, so don't // hide it yet. (We just create a new timeout (and destroy // the old one) each time because the bookkeeping is // simpler.) this._lastSeenMouseY = y; this._updateNotificationTimeout(1000); } else { this._notificationTimeoutId = 0; this._updateState(); } return false; }, _hideNotification: function() { this._focusGrabber.ungrabFocus(); if (this._notificationExpandedId) { this._notification.disconnect(this._notificationExpandedId); this._notificationExpandedId = 0; } this._tween(this._notificationBin, '_notificationState', State.HIDDEN, { y: Main.layoutManager.primaryMonitor.y, opacity: 0, time: ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._hideNotificationCompleted, onCompleteScope: this }); }, _hideNotificationCompleted: function() { this._notificationBin.hide(); this._notificationBin.child = null; this._notification.collapseCompleted(); this._notification.disconnect(this._notificationClickedId); this._notificationClickedId = 0; let notification = this._notification; if (AppletManager.get_role_provider_exists(AppletManager.Roles.NOTIFICATIONS) && !this._notificationRemoved) { this.emit('notify-applet-update', notification); } else { if (notification.isTransient) notification.destroy(NotificationDestroyedReason.EXPIRED); } this._notification = null; this._notificationRemoved = false; }, _expandNotification: function(autoExpanding) { // Don't grab focus in notifications that are auto-expanded. if (!autoExpanding) this._focusGrabber.grabFocus(this._notification.actor); if (!this._notificationExpandedId) this._notificationExpandedId = this._notification.connect('expanded', Lang.bind(this, this._onNotificationExpanded)); // Don't animate changes in notifications that are auto-expanding. this._notification.expand(!autoExpanding); }, _onNotificationExpanded: function() { let expandedY = this._notification.actor.height - this._notificationBin.height; // Don't animate the notification to its new position if it has shrunk: // there will be a very visible "gap" that breaks the illusion. // This isn't really working at the moment, but it was just crashing before // if it encountered a critical notification. expandedY is always 0. For now // just make sure it's not covering the top panel if there is one. let monitor = Main.layoutManager.primaryMonitor; let panel = Main.panelManager.getPanel(0, false); // We only want the top panel in monitor 0 let height = 5; if (panel) height += panel.actor.get_height(); let newY = monitor.y + height; if (this._notificationBin.y < expandedY) this._notificationBin.y = expandedY; else if (this._notification.y != expandedY) this._tween(this._notificationBin, '_notificationState', State.SHOWN, { y: newY, time: ANIMATION_TIME, transition: 'easeOutQuad' }); }, // We use this function to grab focus when the user moves the pointer // to a notification with CRITICAL urgency that was already auto-expanded. _ensureNotificationFocused: function() { this._focusGrabber.grabFocus(this._notification.actor); } }; Signals.addSignalMethods(MessageTray.prototype); function SystemNotificationSource() { this._init(); } SystemNotificationSource.prototype = { __proto__: Source.prototype, _init: function() { Source.prototype._init.call(this, _("System Information")); this._setSummaryIcon(this.createNotificationIcon()); }, createNotificationIcon: function() { return new St.Icon({ icon_name: 'dialog-information', icon_type: St.IconType.SYMBOLIC, icon_size: this.ICON_SIZE }); }, open: function() { this.destroy(); } }; Cinnamon-2.8.6/js/ui/extensionSystem.js0000664000175000017500000000511012625666430017034 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Extension = imports.ui.extension; // Maps uuid -> importer object (extension directory tree) var extensions; // Maps uuid -> metadata object var extensionMeta; // Lists extension uuid's that are currently active; const runningExtensions = {}; // Arrays of uuids var enabledExtensions; const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; // Deprecated, kept for compatibility reasons var ExtensionState; // Deprecated, kept for compatibility reasons function disableExtension(uuid) { Extension.unloadExtension(uuid, Extension.Type.EXTENSION); } // Deprecated, kept for compatibility reasons function enableExtension(uuid) { Extension.loadExtension(uuid, Extension.Type.EXTENSION); } // Callback for extension.js function prepareExtensionUnload(extension) { try { extension.module.disable(); } catch (e) { extension.logError('Failed to evaluate \'disable\' function on extension: ' + extension.uuid, e); } delete runningExtensions[extension.uuid]; } // Callback for extension.js function finishExtensionLoad(extension) { if(!extension.lockRole(extension.module)) { return false; } try { extension.module.init(extension.meta); } catch (e) { extension.logError('Failed to evaluate \'init\' function on extension: ' + extension.uuid, e); return false; } try { extension.module.enable(); } catch (e) { extension.logError('Failed to evaluate \'enable\' function on extension: ' + extension.uuid, e); return false; } runningExtensions[extension.uuid] = true; return true; } function onEnabledExtensionsChanged() { enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); for(let uuid in Extension.Type.EXTENSION.maps.objects) { if(enabledExtensions.indexOf(uuid) == -1) Extension.unloadExtension(uuid, Extension.Type.EXTENSION); } for(let i=0; i 0) { this.killUrgencyTimeout(); this.urgencyTimeout = Mainloop.timeout_add(750, Lang.bind(this, function() { this.showUrgencyState({showUrgent:!force, reps: params.reps - (force ? 0 : 1)}); this.urgencyTimeout = 0; })); } }, demandAttention: function() { this.demanding_attention = true; this.showUrgencyState({showUrgent:true, reps: 50}); this.emit('demanding-attention'); }, onWindowDemandsAttention: function(display, metaWindow) { if (metaWindow != this.metaWindow) {return;} this.demandAttention(); }, setStackAbove: function (actor) { if (actor.get_parent() !== this.actor.get_parent()) { return; } this.stackAbove = actor; if (this.stackAbove == null) this.actor.lower_bottom(); else this.actor.raise(this.stackAbove); }, destroy: function () { this.killUrgencyTimeout(); this.disconnectAttentionSignals(); this.actor.destroy(); this.icon.destroy(); }, onPositionChanged: function() { this.actor.set_position(this.origX = this.realWindow.x, this.origY = this.realWindow.y); this.actor.set_size(this.realWindow.width, this.realWindow.height); }, onSizeChanged: function() { this.actor.set_size(this.realWindow.width, this.realWindow.height); }, onDestroy: function() { this._signalManager.disconnectAllSignals(); this.actor._delegate = null; if (this.inDrag) { this.inDrag = false; this.emit('drag-end'); } this.disconnectAll(); }, onButtonRelease : function (actor, event) { if ((Cinnamon.get_event_state(event) & Clutter.ModifierType.BUTTON1_MASK) || (Cinnamon.get_event_state(event) & Clutter.ModifierType.BUTTON3_MASK)) { this.emit('selected', event.get_time()); } if ((Cinnamon.get_event_state(event) & Clutter.ModifierType.BUTTON2_MASK)) { this.emit('middle-button-release', event.get_time()); } return true; }, onDragBegin : function (draggable, time) { this.inDrag = true; this.dragCancelled = false; this.emit('drag-begin'); }, onDragCancelled : function (draggable, time) { this.dragCancelled = true; this.emit('drag-cancelled'); }, onDragEnd : function (draggable, time, snapback) { this.inDrag = false; this.emit('drag-end'); } }; Signals.addSignalMethods(ExpoWindowClone.prototype); const ThumbnailState = { NEW : 0, ANIMATING_IN : 1, NORMAL: 2, REMOVING : 3, ANIMATING_OUT : 4, ANIMATED_OUT : 5, COLLAPSING : 6, DESTROYED : 7 }; /** * @metaWorkspace: a #Meta.Workspace */ function ExpoWorkspaceThumbnail(metaWorkspace, box) { this._init(metaWorkspace, box); } ExpoWorkspaceThumbnail.prototype = { _init : function(metaWorkspace, box) { this.box = box; this.metaWorkspace = metaWorkspace; this.frame = new St.Group({ clip_to_allocation: true, style_class: 'expo-workspace-thumbnail-frame' }); this.actor = new St.Group({ reactive: true, clip_to_allocation: true, style_class: 'workspace-thumbnail' }); this.actor._delegate = this; this.actor.set_size(global.screen_width, global.screen_height); this.contents = new Clutter.Group(); this.actor.add_actor(this.contents); this.actor.connect('destroy', Lang.bind(this, this.onDestroy)); let lastButtonPressTimeStamp = 0; let lastButtonPressActor = null; this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { lastButtonPressTimeStamp = event.get_time(); lastButtonPressActor = actor; return true; })); this.actor.connect('button-release-event', Lang.bind(this, function(actor, event) { if (lastButtonPressActor !== actor) { return true; } let timeElapsed = event.get_time() - lastButtonPressTimeStamp; // A long time elapsed is probably due to a failed dnd attempt, // or some other mishap, so we'll ignore those. if (timeElapsed > 500) { return true; } let evstate = Cinnamon.get_event_state(event); if ((evstate & Clutter.ModifierType.BUTTON1_MASK) || (evstate & Clutter.ModifierType.BUTTON3_MASK)) { this.activate(null, event.get_time()); return true; } else if (evstate & Clutter.ModifierType.BUTTON2_MASK) { this.remove(); return true; } return false; })); this.actor.connect('scroll-event', Lang.bind(this, this.onScrollEvent)); this.title = new St.Entry({ style_class: 'expo-workspaces-name-entry', track_hover: true, can_focus: true }); this.title._spacing = 0; this.titleText = this.title.clutter_text; this.titleText.connect('key-press-event', Lang.bind(this, this.onTitleKeyPressEvent)); this.titleText.connect('key-focus-in', Lang.bind(this, function() { this.origTitle = Main.getWorkspaceName(this.metaWorkspace.index()); })); this.titleText.connect('key-focus-out', Lang.bind(this, function() { if (this.doomed) { // user probably deleted workspace while editing global.stage.set_key_focus(this.box.actor); return; } if (!this.undoTitleEdit) { let newName = this.title.get_text().trim(); if (newName != this.origTitle) { Main.setWorkspaceName(this.metaWorkspace.index(), newName); } } this.title.set_text(Main.getWorkspaceName(this.metaWorkspace.index())); })); this.title.set_text(Main.getWorkspaceName(this.metaWorkspace.index())); this.background = new Clutter.Group(); this.contents.add_actor(this.background); let desktopBackground = Meta.BackgroundActor.new_for_screen(global.screen); this.background.add_actor(desktopBackground); let backgroundShade = new St.Bin({style_class: 'workspace-overview-background-shade'}); this.background.add_actor(backgroundShade); backgroundShade.set_size(global.screen_width, global.screen_height); this.shader = new St.Bin(); this.shader.set_style('background-color: black;'); this.actor.add_actor(this.shader); this.shader.set_size(global.screen_width, global.screen_height); this.shader.opacity = INACTIVE_OPACITY; if (metaWorkspace == global.screen.get_active_workspace()) this.shader.opacity = 0; let windows = global.get_window_actors().filter(this.isMyWindow, this); // Create clones for windows that should be visible in the Expo this.count = 0; this.windows = []; for (let i = 0; i < windows.length; i++) { if (this.isExpoWindow(windows[i])) { this.addWindowClone(windows[i]); } } let windowAddedId = this.metaWorkspace.connect('window-added', Lang.bind(this, this.windowAdded)); let windowRemovedId = this.metaWorkspace.connect('window-removed', Lang.bind(this, this.windowRemoved)); let windowEnteredMonitorId = global.screen.connect('window-entered-monitor', Lang.bind(this, this.windowEnteredMonitor)); let windowLeftMonitorId = global.screen.connect('window-left-monitor', Lang.bind(this, this.windowLeftMonitor)); let setOverviewModeId = box.connect('set-overview-mode', Lang.bind(this, function(box, turnOn) { this.setOverviewMode(turnOn); this.hovering = false; })); let stickyAddedId = box.connect('sticky-detected', Lang.bind(this, function(box, metaWindow) { this.doAddWindow(metaWindow); })); let restackedNotifyId = global.screen.connect('restacked', Lang.bind(this, this.onRestack)); this.disconnectOtherSignals = function() { global.screen.disconnect(restackedNotifyId); this.box.disconnect(setOverviewModeId); this.box.disconnect(stickyAddedId); this.metaWorkspace.disconnect(windowAddedId); this.metaWorkspace.disconnect(windowRemovedId); global.screen.disconnect(windowEnteredMonitorId); global.screen.disconnect(windowLeftMonitorId); }; this.isActive = false; this.state = ThumbnailState.NORMAL; this.restack(); this._slidePosition = 0; // Fully slid in this.setOverviewMode(forceOverviewMode); }, setOverviewMode: function(turnOn) { if (turnOn) {this.overviewModeOn();} else {this.overviewModeOff();} }, refresh: function() { this.refreshTitle(); this.resetCloneHover(); this.setOverviewMode(this.overviewMode); }, onRestack: function() { this.restack(); this.refresh(); }, restack: function(force) { if (this.state > ThumbnailState.NORMAL) { return; } if (this.isActive || !this.stackIndices || force) { let stack = global.get_window_actors().filter(this.isMyWindow, this); this.stackIndices = {}; for (let i = 0; i < stack.length; i++) { // Use the stable sequence for an integer to use as a hash key this.stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; } } this.syncStacking(this.stackIndices); }, setActive: function(isActive) { this.isActive = isActive; this.frame.name = isActive ? 'active' : ''; }, refreshTitle: function() { if (!this.doomed) { // better safe than sorry this.title.set_text(Main.getWorkspaceName(this.metaWorkspace.index())); } }, onTitleKeyPressEvent: function(actor, event) { this.undoTitleEdit = false; let symbol = event.get_key_symbol(); if (symbol === Clutter.Return || symbol === Clutter.Escape) { if (symbol === Clutter.Escape) { this.undoTitleEdit = true; } global.stage.set_key_focus(this.actor); return true; } return false; }, activateWorkspace: function() { if (this.metaWorkspace != global.screen.get_active_workspace()) this.metaWorkspace.activate(global.get_current_time()); Main.expo.hide(); }, showKeyboardSelectedState: function(selected) { this.isSelected = selected; this.title.name = selected ? "selected" : ""; if (selected) { this.highlight(); this.overviewModeOn(); } else { this.hovering = false; this.overviewModeOff(); this.shade(); } }, lookupIndex: function (metaWindow) { for (let i = 0; i < this.windows.length; i++) { if (this.windows[i].metaWindow == metaWindow) { return i; } } return -1; }, syncStacking: function(stackIndices) { this.windows.sort(Lang.bind(this, function (a, b) { let minimizedDiff = function(a, b) { let minimizedA = a.metaWindow.minimized ? -1 : 0; let minimizedB = b.metaWindow.minimized ? -1 : 0; return minimizedA - minimizedB; }; let noOverviewDiff = Lang.bind(this, function(a, b) { let noOverviewA = !this.isOverviewWindow(a.metaWindow) ? -1 : 0; let noOverviewB = !this.isOverviewWindow(b.metaWindow) ? -1 : 0; return noOverviewA - noOverviewB; }); let transientRelation = function(a, b) { let overviewDifference = noOverviewDiff(a,b); if (overviewDifference) { let transientA = a.metaWindow.get_transient_for() === b.metaWindow ? 1 : 0; let transientB = !transientA && b.metaWindow.get_transient_for() === a.metaWindow ? 1 : 0; return transientA - transientB || overviewDifference; } return 0; }; return transientRelation(a,b) || minimizedDiff(a,b) || stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; })); for (let i = 0; i < this.windows.length; i++) { let clone = this.windows[i]; let metaWindow = clone.metaWindow; if (i == 0) { clone.setStackAbove(this.background); } else { let previousClone = this.windows[i - 1]; clone.setStackAbove(previousClone.actor); } } }, set slidePosition(slidePosition) { this._slidePosition = slidePosition; this.actor.queue_relayout(); }, get slidePosition() { return this._slidePosition; }, doRemoveWindow : function(metaWin) { let win = metaWin.get_compositor_private(); // find the position of the window in our list let index = this.lookupIndex (metaWin); if (index == -1) return; // Check if window still should be here if (win && this.isMyWindow(win) && this.isExpoWindow(win)) return; let clone = this.windows[index]; this.windows.splice(index, 1); clone.destroy(); if (this.overviewMode) this.overviewModeOn(); }, doAddWindow : function(metaWin) { let win = metaWin.get_compositor_private(); if (!win) { // Newly-created windows are added to a workspace before // the compositor finds out about them... Mainloop.idle_add(Lang.bind(this, function () { if (this.windows /*will be null if we're closing down*/ && metaWin.get_compositor_private()) { this.doAddWindow(metaWin); } return false; })); return; } // We might have the window in our list already if it was on all workspaces and // now was moved to this workspace if (this.lookupIndex (metaWin) != -1) return; if (!this.isMyWindow(win) || !this.isExpoWindow(win)) return; let clone = this.addWindowClone(win); this.overviewModeOn(); }, windowAdded : function(metaWorkspace, metaWin) { this.doAddWindow(metaWin); this.restack(); }, windowRemoved : function(metaWorkspace, metaWin) { this.doRemoveWindow(metaWin); }, windowEnteredMonitor : function(metaScreen, monitorIndex, metaWin) { // important if workspaces-only-on-primary is in effect this.doAddWindow(metaWin); }, windowLeftMonitor : function(metaScreen, monitorIndex, metaWin) { // important if workspaces-only-on-primary is in effect this.doRemoveWindow(metaWin); }, destroy : function() { this.actor.destroy(); this.frame.destroy(); }, onDestroy: function(actor) { this.disconnectOtherSignals(); this.resetCloneHover(); for (let i = 0; i < this.windows.length; i++) { this.windows[i].destroy(); } this.windows = null; }, // Tests if @win belongs to this workspace and monitor isMyWindow : function (win) { return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index()); }, // Tests if @win should be shown in the Expo isExpoWindow : function (win) { let metaWindow = win.get_meta_window(); if (metaWindow.is_override_redirect()) { return false; } let type = metaWindow.get_window_type(); return type !== Meta.WindowType.DESKTOP && type !== Meta.WindowType.DOCK; }, // Tests if @win should be shown in overview mode isOverviewWindow : function (metaWindow) { return Main.isInteresting(metaWindow); }, // Create a clone of a (non-desktop) window and add it to the window list addWindowClone : function(win) { let clone = new ExpoWindowClone(win); clone.connect('workspace-changed', Lang.bind(this, function() { this.doRemoveWindow(clone.metaWindow); if (clone.metaWindow.is_on_all_workspaces()) { // Muffin appears not to broadcast when a window turns sticky this.box.emit('sticky-detected', clone.metaWindow); } })); clone.connect('middle-button-release', Lang.bind(this, function(sender, time) { clone.metaWindow.delete(time); })); clone.connect('hovering', Lang.bind(this, this.onCloneHover)); clone.connect('demanding-attention', Lang.bind(this, function() {this.overviewModeOn();})); clone.connect('selected', Lang.bind(this, this.activate)); clone.connect('remove-workspace', Lang.bind(this, this.remove)); clone.connect('drag-begin', Lang.bind(this, function(clone) { this.box.emit('drag-begin'); this.resetCloneHover(); })); clone.connect('drag-end', Lang.bind(this, function(clone) { this.box.emit('drag-end'); if (clone.dragCancelled) { // stacking order may have been disturbed this.restack(); } this.overviewModeOn(); })); this.contents.add_actor(clone.actor); if (this.windows.length == 0) clone.setStackAbove(this.background); else clone.setStackAbove(this.windows[this.windows.length - 1].actor); this.windows.push(clone); return clone; }, resetCloneHover : function () { this.lastHoveredClone = null; if (this.tooltip) { this.tooltip.destroy(); this.tooltip = null; } }, onCloneHover : function (clone, hovering) { if (!this.overviewMode) { this.resetCloneHover(); return; } if (hovering && clone !== this.lastHoveredClone) { if (this.buttonTimeoutId) {Mainloop.source_remove(this.buttonTimeoutId);} this.buttonTimeoutId = Mainloop.idle_add(Lang.bind(this,function() { this.buttonTimeoutId = null; if (!this.windows) {return;} /* being destroyed */ let [x, y, mask] = global.get_pointer(); let target = this.contents.get_stage().get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); if (target !== clone.actor) { this.resetCloneHover(); return; } if (this.tooltip) { this.tooltip.destroy(); } this.tooltip = new Tooltips.Tooltip(clone.actor, clone.metaWindow.title); })); this.lastHoveredClone = clone; } }, overviewModeOn : function () { if (!this.box.scale) {return;} this.overviewMode = true; this.resetCloneHover(); let windows = []; this.windows.forEach(function(window) { if (this.isOverviewWindow(window.metaWindow)) { windows.push(window); } else { Tweener.addTween(window.actor, {scale_x: 0, scale_y: 0, time: REARRANGE_TIME_ON, transition: 'easeOutQuad', onComplete: window.actor.hide}); } }, this); windows.reverse(); // top-to-bottom order Main.layoutManager.monitors.forEach(function(monitor, monitorIndex) { let monitorWindows = windows.filter(function(window) { return window.metaWindow.get_monitor() === monitorIndex; }, this); let spacing = 14; let nWindows = monitorWindows.length; let [nCols, nRows] = [Math.ceil(Math.sqrt(nWindows)), Math.round(Math.sqrt(nWindows))] let maxWindowWidth = (monitor.width - (spacing * (nCols+1))) / nCols; let maxWindowHeight = (monitor.height - (spacing * (nRows+1))) / nRows; let [col, row] = [1, 1]; let lastRowCols = nWindows - ((nRows - 1) * nCols); let lastRowOffset = (monitor.width - (maxWindowWidth * lastRowCols) - (spacing * (lastRowCols+1))) / 2; let offset = 0; monitorWindows.forEach(function(window, i) { if (window.inDrag) {return;} window.refreshClone(true); window.showUrgencyState(); if (row == nRows) offset = lastRowOffset; let [wWidth, wHeight] = [window.realWindow.width, window.realWindow.height]; let scale = Math.min(1, maxWindowWidth / wWidth, maxWindowHeight / wHeight); let x = monitor.x + offset + (spacing * col) + (maxWindowWidth * (col - 1)) + ((maxWindowWidth - (wWidth * scale)) / 2); let y = monitor.y + (spacing * row) + (maxWindowHeight * (row - 1)) + ((maxWindowHeight - (wHeight * scale)) / 2); window.icon.raise_top(); // all icons should be the same size! let iconScale = (0.25/this.box.scale/scale); window.icon.set_scale(iconScale, iconScale); let [iconX, iconY] = [ICON_OFFSET / this.box.scale/scale, ICON_OFFSET / this.box.scale/scale]; window.icon.set_position(iconX, iconY); Tweener.addTween(window.actor, { x: x, y: y, scale_x: scale, scale_y: scale, opacity: 255, time: REARRANGE_TIME_ON, transition: 'easeOutQuad', onComplete: function() { window.actor.show(); window.icon.show(); } }); col++; if (col > nCols){ row ++; col = 1; } }, this); }, this); }, overviewModeOff : function(force, override) { if (!this.box.scale) {return;} this.resetCloneHover(); if (this.overviewMode === false && !force) {return;} if (forceOverviewMode && !override) {return;} this.overviewMode = false; const iconSpacing = ICON_SIZE/4; let rearrangeTime = force ? REARRANGE_TIME_OFF/2 : REARRANGE_TIME_OFF; Main.layoutManager.monitors.forEach(function(monitor, monitorIndex) { let iconCount = 0; this.windows.filter(function(window) { return monitorIndex === window.metaWindow.get_monitor(); },this).forEach(function(window) { if (window.inDrag) {return;} window.refreshClone(false); window.showUrgencyState(); window.icon.hide(); window.actor.show(); Tweener.addTween(window.actor, { x: window.origX, y: window.origY, scale_x: 1, scale_y: 1, opacity: window.metaWindow.showing_on_its_workspace() ? 255 : 127, time: rearrangeTime, transition: 'easeOutQuad' }); }, this); }, this); }, onScrollEvent: function (actor, event) { if (Main.expo.animationInProgress) return; switch ( event.get_scroll_direction() ) { case Clutter.ScrollDirection.UP: Main.wm.actionMoveWorkspaceLeft(); break; case Clutter.ScrollDirection.DOWN: Main.wm.actionMoveWorkspaceRight(); break; } }, activate : function (clone, time) { if (this.state > ThumbnailState.NORMAL) return; if (clone && clone.metaWindow != null){ Main.activateWindow(clone.metaWindow, time, this.metaWorkspace.index()); } if (this.metaWorkspace != global.screen.get_active_workspace()) this.metaWorkspace.activate(time); Main.expo.hide(); }, shade : function (force){ if (!this.isSelected || force) Tweener.addTween(this.shader, {opacity: INACTIVE_OPACITY, time: SLIDE_ANIMATION_TIME, transition: 'easeOutQuad'}); }, highlight : function (){ Tweener.addTween(this.shader, {opacity: 0, time: SLIDE_ANIMATION_TIME, transition: 'easeOutQuad'}); }, remove : function (){ if (this.doomed) { // this workspace is already being removed return; } if (global.screen.n_workspaces <= 1) { return; } let removeAction = Lang.bind(this, function() { this.doomed = true; Main._removeWorkspace(this.metaWorkspace); }); if (!Main.hasDefaultWorkspaceName(this.metaWorkspace.index())) { this.overviewModeOn(); this.highlight(); let prompt = _("Are you sure you want to remove workspace \"%s\"?\n\n").format( Main.getWorkspaceName(this.metaWorkspace.index())); let confirm = new ModalDialog.ConfirmDialog(prompt, removeAction); confirm.open(); } else { removeAction(); } }, coordinateToMonitor : function(x, y) { let indexOne = 0; Main.layoutManager.monitors.forEach(function(monitor, mindex) { let [xX, yY] = [x - monitor.x, y - monitor.y]; indexOne = indexOne || (xX >= 0 && xX < monitor.width && yY > 0 && yY < monitor.height ? mindex + 1 : 0); }, this); return indexOne - 1; }, // Draggable target interface handleDragOver : function(source, actor, x, y, time) { this.emit('drag-over'); if (!this.overviewMode) { this.overviewModeOn(); } return this.handleDragOverOrDrop(false, source, actor, x, y, time); }, handleDragOverOrDrop : function(dropping, source, actor, x, y, time) { this.hovering = false; // normal hover logic is off during dnd if (dropping) { let draggable = source._draggable; actor.opacity = draggable._dragOrigOpacity; global.reparentActor(actor, draggable._dragOrigParent); } if (source == Main.xdndHandler) { return DND.DragMotionResult.CONTINUE; } if (this.state > ThumbnailState.NORMAL) return DND.DragMotionResult.CONTINUE; if (!source.metaWindow) return DND.DragMotionResult.CONTINUE; let win = source.realWindow; let metaWindow = source.metaWindow; let targetMonitor = this.coordinateToMonitor(x, y); let fromMonitor = metaWindow.get_monitor(); let movingMonitors = targetMonitor >= 0 && fromMonitor !== targetMonitor; let movingWorkspaces = !Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index()); let canDrop = false; if (movingMonitors && Main.wm.workspacesOnlyOnPrimary && (fromMonitor === Main.layoutManager.primaryIndex || targetMonitor === Main.layoutManager.primaryIndex)) { canDrop = true; if (dropping) { metaWindow.move_to_monitor(targetMonitor); if (targetMonitor === Main.layoutManager.primaryIndex) { metaWindow.change_workspace(this.metaWorkspace, false, time); } } } else { if (movingWorkspaces || (metaWindow.is_on_all_workspaces() && !movingMonitors && (Main.layoutManager.monitors.length === 1 || !(Main.wm.workspacesOnlyOnPrimary && fromMonitor !== Main.layoutManager.primaryIndex) ))) { canDrop = true; if (dropping) { metaWindow.change_workspace(this.metaWorkspace, false, time); } } if (movingMonitors) { canDrop = true; if (dropping) { metaWindow.move_to_monitor(targetMonitor); } } } return canDrop ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; }, acceptDrop : function(source, actor, x, y, time) { if (this.handleDragOverOrDrop(false, source, actor, x, y, time) != DND.DragMotionResult.CONTINUE) { if (this.handleDragOverOrDrop(true, source, actor, x, y, time) != DND.DragMotionResult.CONTINUE) { this.restack(true); this.overviewModeOn(); return true; } } return false; } }; Signals.addSignalMethods(ExpoWorkspaceThumbnail.prototype); function ExpoThumbnailsBox() { this._init(); } ExpoThumbnailsBox.prototype = { _init: function() { this.actor = new Cinnamon.GenericContainer({ style_class: 'workspace-thumbnails', reactive: true, request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT }); this.actor.connect('get-preferred-width', Lang.bind(this, this.getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this.getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this.allocate)); // When we animate the scale, we don't animate the requested size of the thumbnails, rather // we ask for our final size and then animate within that size. This slightly simplifies the // interaction with the main workspace windows (instead of constantly reallocating them // to a new size, they get a new size once, then use the standard window animation code // allocate the windows to their new positions), however it causes problems for drawing // the background and border wrapped around the thumbnail as we animate - we can't just pack // the container into a box and set style properties on the box since that box would wrap // around the final size not the animating size. So instead we fake the background with // an actor underneath the content and adjust the allocation of our children to leave space // for the border and padding of the background actor. this.background = new St.Bin({reactive:true}); this.actor.add_actor(this.background); this.background.handleDragOver = function(source, actor, x, y, time) { return source.metaWindow && !source.metaWindow.is_on_all_workspaces() ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; }; this.background.acceptDrop = Lang.bind(this, function(source, actor, x, y, time) { if (this.background.handleDragOver.apply(this, arguments) === DND.DragMotionResult.MOVE_DROP) { let draggable = source._draggable; actor.get_parent().remove_actor(actor); draggable._dragOrigParent.add_actor(actor); actor.opacity = draggable._dragOrigOpacity; source.metaWindow.stick(); return true; } return false; }); this.background._delegate = this.background; this.button = new St.Button({ style_class: 'workspace-close-button' }); this.actor.add_actor(this.button); this.button.connect('enter-event', Lang.bind(this, function () {this.button.show();})); this.button.connect('leave-event', Lang.bind(this, function () {this.button.hide();})); this.button.connect('clicked', Lang.bind(this, function () { this.lastHovered.remove(); this.button.hide();})); this.button.hide(); this.actor.connect('scroll-event', this.onScrollEvent); this.targetScale = 0; this._scale = 0; this.pendingScaleUpdate = false; this.stateUpdateQueued = false; this.stateCounts = {}; for (let key in ThumbnailState) this.stateCounts[ThumbnailState[key]] = 0; this.thumbnails = []; // The "porthole" is the portion of the screen that we show in the workspaces this.porthole = { x: 0, y: 0, width: global.screen_width, height: global.screen_height }; this.kbThumbnailIndex = global.screen.get_active_workspace_index(); // apparently we get no direct call to show the initial // view, so we must force an explicit overviewMode On/Off display // after it has been allocated let allocId = this.connect('allocated', Lang.bind(this, function() { this.disconnect(allocId); Mainloop.timeout_add(0, Lang.bind(this, function() { this.emit('set-overview-mode', forceOverviewMode === 1); this.thumbnails[this.kbThumbnailIndex].showKeyboardSelectedState(true); })); })); this.toggleGlobalOverviewMode = function() { forceOverviewMode = (forceOverviewMode + 1) % 2; this.emit('set-overview-mode', forceOverviewMode === 1); }; this.actor.connect('button-release-event', Lang.bind(this, function(actor, event) { if (Cinnamon.get_event_state(event) & Clutter.ModifierType.BUTTON2_MASK) { this.toggleGlobalOverviewMode(); } })); }, show: function() { this.switchWorkspaceNotifyId = global.window_manager.connect('switch-workspace', Lang.bind(this, this.activeWorkspaceChanged)); this.workspaceAddedId = global.screen.connect('workspace-added', Lang.bind(this, function(screen, index) { this.addThumbnails(index, 1); })); this.workspaceRemovedId = global.screen.connect('workspace-removed', Lang.bind(this, function() { this.button.hide(); // just handling the single workspace removed is not enough let removedCount = 0; this.thumbnails.forEach(function(thumbnail, i) { let metaWorkspace = global.screen.get_workspace_by_index(i-removedCount); if (thumbnail.metaWorkspace != metaWorkspace) { ++removedCount; if (thumbnail.state <= ThumbnailState.NORMAL) { this.setThumbnailState(thumbnail, ThumbnailState.REMOVING); } } }, this); this.updateStates(); })); this.stateCounts = {}; for (let key in ThumbnailState) this.stateCounts[ThumbnailState[key]] = 0; this.addThumbnails(0, global.screen.n_workspaces); this.button.raise_top(); global.stage.set_key_focus(this.actor); }, handleKeyPressEvent: function(actor, event) { let modifiers = Cinnamon.get_event_state(event); let ctrlAltMask = Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.MOD1_MASK; let symbol = event.get_key_symbol(); if (symbol === Clutter.Return || symbol === Clutter.KEY_space || symbol === Clutter.KP_Enter) { this.activateSelectedWorkspace(); return true; } if (symbol === Clutter.F2) { this.editWorkspaceTitle(); return true; } if ((symbol === Clutter.o || symbol === Clutter.O) && modifiers & Clutter.ModifierType.CONTROL_MASK) { this.toggleGlobalOverviewMode(); return true; } if (modifiers & ctrlAltMask) { return false; } return this.selectNextWorkspace(symbol); }, editWorkspaceTitle: function() { this.thumbnails[this.kbThumbnailIndex].title.grab_key_focus(); }, activateSelectedWorkspace: function() { this.thumbnails[this.kbThumbnailIndex].activateWorkspace(); }, removeSelectedWorkspace: function() { this.thumbnails[this.kbThumbnailIndex].remove(); }, // returns true if symbol was understood, false otherwise selectNextWorkspace: function(symbol) { let prevIndex = this.kbThumbnailIndex; let lastIndex = this.thumbnails.length - 1; let [nColumns, nRows] = this.getNumberOfColumnsAndRows(this.thumbnails.length); let nextIndex = GridNavigator.nextIndex(this.thumbnails.length, nColumns, prevIndex, symbol); if (nextIndex >= 0) { this.kbThumbnailIndex = nextIndex; } else { let index = symbol - 48 - 1; // convert '1' to index 0, etc if (index >= 0 && index < 10) { // OK } else { index = symbol - Clutter.KP_1; // convert Num-pad '1' to index 0, etc if (index < 0 || index > 9) { return false; // not handled } } if (index > lastIndex) { return true; // handled, but out of range } this.kbThumbnailIndex = index; this.activateSelectedWorkspace(); Main.wm.showWorkspaceOSD(); return true; // handled } if (prevIndex != this.kbThumbnailIndex) { this.thumbnails[prevIndex].showKeyboardSelectedState(false); this.thumbnails[this.kbThumbnailIndex].showKeyboardSelectedState(true); } return true; // handled }, hide: function() { global.window_manager.disconnect(this.switchWorkspaceNotifyId); global.screen.disconnect(this.workspaceAddedId); global.screen.disconnect(this.workspaceRemovedId); for (let w = 0; w < this.thumbnails.length; w++) { this.thumbnails[w].destroy(); } this.thumbnails = []; }, showButton: function(){ if (global.screen.n_workspaces <= 1) return false; this.actor.queue_relayout(); this.button.raise_top(); this.button.show(); return true; }, addThumbnails: function(start, count) { function isInternalEvent(thumbnail, actor, event) { return actor === event.get_related() || thumbnail.actor.contains(event.get_related()); } for (let k = start; k < start + count; k++) { let metaWorkspace = global.screen.get_workspace_by_index(k); let thumbnail = new ExpoWorkspaceThumbnail(metaWorkspace, this); this.thumbnails.push(thumbnail); if (metaWorkspace == global.screen.get_active_workspace()) { this.lastActiveWorkspace = thumbnail; thumbnail.setActive(true); } let overviewTimeoutId = null; let setOverviewTimeout = function(timeout, func) { if (overviewTimeoutId) Mainloop.source_remove(overviewTimeoutId); overviewTimeoutId = null; if (timeout && func) { overviewTimeoutId = Mainloop.timeout_add(timeout, func); } }; thumbnail.actor.connect('destroy', Lang.bind(this, function(actor) { setOverviewTimeout(0, function() { overviewTimeoutId = 0; }); this.actor.remove_actor(thumbnail.frame); this.actor.remove_actor(actor); this.actor.remove_actor(thumbnail.title); thumbnail.title.destroy(); })); this.actor.add_actor(thumbnail.frame); this.actor.add_actor(thumbnail.actor); this.actor.add_actor(thumbnail.title); // We use this as a flag to minimize the number of enter and leave events we really // have to deal with, since we get many spurious events when the mouse moves // over the windows in the thumbnails. Handling each and every event leads to // jumping icons if there are minimized windows in a thumbnail. thumbnail.hovering = false; thumbnail.connect('drag-over', Lang.bind(this, function () { thumbnail.highlight(); if (this.lastHovered && this.lastHovered != thumbnail) { this.lastHovered.shade(); } this.lastHovered = thumbnail; })); // We want to ignore spurious events caused by animations // (when the contents are moving and not the pointer). let pointerTracker = new PointerTracker.PointerTracker(); thumbnail.actor.connect('motion-event', Lang.bind(this, function (actor, event) { if (!pointerTracker.hasMoved()) {return;} if (!thumbnail.hovering) { thumbnail.hovering = true; this.lastHovered = thumbnail; this.showButton(); thumbnail.highlight(); setOverviewTimeout(POINTER_ENTER_MILLISECONDS_GRACE, function() { if (thumbnail.hovering) { thumbnail.overviewModeOn(); } overviewTimeoutId = 0; }); } })); thumbnail.actor.connect('leave-event', Lang.bind(this, function (actor, event) { if (!pointerTracker.hasMoved()) {return;} if (this.isShowingModalDialog()) {return;} if (thumbnail.hovering && !isInternalEvent(thumbnail, actor, event)) { thumbnail.hovering = false; this.button.hide(); thumbnail.shade(); setOverviewTimeout(POINTER_LEAVE_MILLISECONDS_GRACE, function() { if (!thumbnail.hovering) { thumbnail.overviewModeOff(); } overviewTimeoutId = 0; }); } })); if (start > 0) { // not the initial fill thumbnail.state = ThumbnailState.NEW; thumbnail.slidePosition = 1; // start slid out } else { thumbnail.state = ThumbnailState.NORMAL; } this.stateCounts[thumbnail.state]++; } if (start > 0) { this.updateStates(); } else { this.queueUpdateStates(); } }, set scale(scale) { this._scale = scale; this.actor.queue_relayout(); }, get scale() { return this._scale; }, setThumbnailState: function(thumbnail, state) { this.stateCounts[thumbnail.state]--; thumbnail.state = state; this.stateCounts[thumbnail.state]++; }, iterateStateThumbnails: function(state, callback) { if (this.stateCounts[state] == 0) return; for (let i = 0; i < this.thumbnails.length; i++) { if (this.thumbnails[i].state == state) callback.call(this, this.thumbnails[i]); } }, tweenScale: function() { Tweener.addTween(this, { scale: this.targetScale, time: RESCALE_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this.queueUpdateStates, onCompleteScope: this }); }, updateStates: function() { this.stateUpdateQueued = false; // Then slide out any thumbnails that have been destroyed this.iterateStateThumbnails(ThumbnailState.REMOVING, function(thumbnail) { thumbnail.title.hide(); this.setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT); Tweener.addTween(thumbnail, { slidePosition: 1, time: SLIDE_ANIMATION_TIME, transition: 'linear', onComplete: function() { this.setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT); this.queueUpdateStates(); }, onCompleteScope: this }); }); // As long as things are sliding out, don't proceed if (this.stateCounts[ThumbnailState.ANIMATING_OUT] > 0) return; // Once that's complete, we can start scaling to the new size and collapse any removed thumbnails this.iterateStateThumbnails(ThumbnailState.ANIMATED_OUT, function(thumbnail) { this.actor.set_skip_paint(thumbnail.actor, true); //this.title.set_skip_paint(thumbnail.title, true); this.setThumbnailState(thumbnail, ThumbnailState.COLLAPSING); Tweener.addTween(thumbnail, { time: RESCALE_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: function() { this.stateCounts[thumbnail.state]--; thumbnail.state = ThumbnailState.DESTROYED; let index = this.thumbnails.indexOf(thumbnail); this.thumbnails.splice(index, 1); thumbnail.destroy(); if (index < this.kbThumbnailIndex || (index === this.kbThumbnailIndex && index === this.thumbnails.length)) { --this.kbThumbnailIndex; } this.queueUpdateStates(); }, onCompleteScope: this }); }); if (this.pendingScaleUpdate) { this.tweenScale(); this.pendingScaleUpdate = false; } // Wait until that's done if (this._scale != this.targetScale || this.stateCounts[ThumbnailState.COLLAPSING] > 0) return; // And then slide in any new thumbnails this.iterateStateThumbnails(ThumbnailState.NEW, function(thumbnail) { this.setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN); Tweener.addTween(thumbnail, { slidePosition: 0, time: SLIDE_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: function() { this.setThumbnailState(thumbnail, ThumbnailState.NORMAL); }, onCompleteScope: this }); }); this.iterateStateThumbnails(ThumbnailState.NORMAL, function(thumbnail) { thumbnail.refresh(); }); this.thumbnails[this.kbThumbnailIndex].showKeyboardSelectedState(true); if (!this.isShowingModalDialog()) { // we may inadvertently have lost keyboard focus during the reshuffling global.stage.set_key_focus(this.actor); } }, isShowingModalDialog: function() { // the normal value is 1 while Expo is active return Main.modalCount > 1; }, queueUpdateStates: function() { if (this.stateUpdateQueued) return; Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this.updateStates)); this.stateUpdateQueued = true; }, getNumberOfColumnsAndRows: function(nWorkspaces) { let asGrid = global.settings.get_boolean("workspace-expo-view-as-grid"); let nColumns = asGrid ? Math.ceil(Math.sqrt(nWorkspaces)) : nWorkspaces; let nRows = Math.ceil(nWorkspaces/nColumns); // in case of a very wide screen, we can try and optimize the screen // utilization by switching the columns and rows, but only if there's a // big difference. If the user doesn't want a grid we are even more conservative. let divisor = 1.25; let screenRatio = global.screen_width / global.screen_height; let boxRatio = this.box ? (this.box.x2 - this.box.x1) / (this.box.y2 - this.box.y1) : 1.6; if (nWorkspaces <= Math.floor(screenRatio)) { return [1, nWorkspaces]; } else if (!asGrid || (screenRatio / divisor) <= boxRatio) { return [nColumns, nRows]; } else { return [nRows, nColumns]; } }, getPreferredHeight: function(actor, forWidth, alloc) { // See comment about this.background in _init() let themeNode = this.background.get_theme_node(); forWidth = themeNode.adjust_for_width(forWidth); // Note that for getPreferredWidth/Height we cheat a bit and skip propagating // the size request to our children because we know how big they are and know // that the actors aren't depending on the virtual functions being called. if (this.thumbnails.length == 0) return; let spacing = this.actor.get_theme_node().get_length('spacing'); let nWorkspaces = global.screen.n_workspaces; let totalSpacing = (nWorkspaces - 1) * spacing; let avail = Main.layoutManager.primaryMonitor.width - totalSpacing; let [nColumns, nRows] = this.getNumberOfColumnsAndRows(nWorkspaces); let scale = (avail / nColumns) / this.porthole.width; let height = Math.round(this.porthole.height * scale); [alloc.min_size, alloc.natural_size] = themeNode.adjust_preferred_height(400, Main.layoutManager.primaryMonitor.height); }, getPreferredWidth: function(actor, forHeight, alloc) { // See comment about this.background in _init() let themeNode = this.background.get_theme_node(); if (this.thumbnails.length == 0) return; // We don't animate our preferred width, which is always reported according // to the actual number of current workspaces, we just animate within that let spacing = this.actor.get_theme_node().get_length('spacing'); let nWorkspaces = global.screen.n_workspaces; let totalSpacing = (nWorkspaces - 1) * spacing; let avail = Main.layoutManager.primaryMonitor.width - totalSpacing; let [nColumns, nRows] = this.getNumberOfColumnsAndRows(nWorkspaces); let scale = (avail / nColumns) / this.porthole.width; let width = Math.round(this.porthole.width * scale); let maxWidth = (width) * nWorkspaces; [alloc.min_size, alloc.natural_size] = themeNode.adjust_preferred_width(totalSpacing, Main.layoutManager.primaryMonitor.width); }, allocate: function(actor, box, flags) { this.box = box; let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); if (this.thumbnails.length == 0) // not visible return; let portholeWidth = this.porthole.width; let portholeHeight = this.porthole.height; let spacing = this.actor.get_theme_node().get_length('spacing'); // We must find out every setting that may affect the height of // the workspace title: let firstThumbnailTitleThemeNode = this.thumbnails[0].title.get_theme_node(); let thTitleHeight = firstThumbnailTitleThemeNode.get_length('height'); let thTitleTopPadding = firstThumbnailTitleThemeNode.get_padding(St.Side.TOP); let thTitleBottomPadding = firstThumbnailTitleThemeNode.get_padding(St.Side.BOTTOM); let thTitleMargin = thTitleBottomPadding; let thTitleBorderHeight = firstThumbnailTitleThemeNode.get_border_width(St.Side.BOTTOM) * 2; let extraHeight = thTitleHeight + thTitleTopPadding + thTitleBottomPadding + thTitleMargin + thTitleBorderHeight; // Compute the scale we'll need once everything is updated let nWorkspaces = this.thumbnails.length; let [nColumns, nRows] = this.getNumberOfColumnsAndRows(nWorkspaces); let totalSpacingX = (nColumns - 1) * spacing; let availX = (box.x2 - box.x1) - totalSpacingX - (spacing * 2) ; let availY = (box.y2 - box.y1) - 2 * spacing - nRows * extraHeight - (nRows - 1) * thTitleMargin; let screen = (box.x2 - box.x1); let newScaleX = (availX / nColumns) / portholeWidth; let newScaleY = (availY / nRows) / portholeHeight; let newScale = Math.min(newScaleX, newScaleY, MAX_THUMBNAIL_SCALE); if (newScale != this.targetScale) { if (this.targetScale > 0) { // We don't do the tween immediately because we need to observe the ordering // in queueUpdateStates - if workspaces have been removed we need to slide them // out as the first thing. this.targetScale = newScale; this.pendingScaleUpdate = true; } else { this.targetScale = this._scale = newScale; } this.queueUpdateStates(); } let thumbnailHeight = Math.round(portholeHeight * this._scale); let thumbnailWidth = Math.round(portholeWidth * this._scale); let childBox = new Clutter.ActorBox(); let calcPaddingX = function(nCols) { let neededX = (thumbnailWidth * nCols) + totalSpacingX + (spacing * 2); let extraSpaceX = (box.x2 - box.x1) - neededX; return spacing + extraSpaceX/2; }; // The background is horizontally restricted to correspond to the current thumbnail size // but otherwise covers the entire allocation childBox.x1 = box.x1; childBox.x2 = box.x2; childBox.y1 = box.y1; childBox.y2 = box.y2 + this.thumbnails[0].title.height; this.background.allocate(childBox, flags); let x; let y = spacing + Math.floor((availY - nRows * thumbnailHeight) / 2); for (let i = 0; i < this.thumbnails.length; i++) { let column = i % nColumns; let row = Math.floor(i / nColumns); let cItemsInRow = Math.min(this.thumbnails.length - (row * nColumns), nColumns); x = column > 0 ? x : calcPaddingX(cItemsInRow); let rowMultiplier = row + 1; let thumbnail = this.thumbnails[i]; // We might end up with thumbnailHeight being something like 99.33 // pixels. To make this work and not end up with a gap at the bottom, // we need some thumbnails to be 99 pixels and some 100 pixels height; // we compute an actual scale separately for each thumbnail. let x1 = Math.round(x + (thumbnailWidth * thumbnail.slidePosition / 2)); let x2 = Math.round(x + thumbnailWidth); let y1, y2; y1 = y; y2 = y1 + thumbnailHeight; // Allocating a scaled actor is funny - x1/y1 correspond to the origin // of the actor, but x2/y2 are increased by the *unscaled* size. childBox.x1 = x1; childBox.x2 = x1 + portholeWidth; childBox.y1 = y1; childBox.y2 = y1 + portholeHeight; let scale = this._scale * (1 - thumbnail.slidePosition); thumbnail.actor.set_scale(scale, scale); thumbnail.actor.allocate(childBox, flags); let framethemeNode = thumbnail.frame.get_theme_node(); let borderWidth = framethemeNode.get_border_width(St.Side.BOTTOM); childBox.x1 = x1 - borderWidth; childBox.x2 = x2 + borderWidth; childBox.y1 = y1 - borderWidth; childBox.y2 = y2 + borderWidth; thumbnail.frame.set_scale((1 - thumbnail.slidePosition), (1 - thumbnail.slidePosition)); thumbnail.frame.allocate(childBox, flags); let thumbnailx = Math.round(x + (thumbnailWidth * thumbnail.slidePosition / 2)); childBox.x1 = Math.max(thumbnailx, thumbnailx + Math.round(thumbnailWidth/2) - Math.round(thumbnail.title.width/2)); childBox.x2 = Math.min(thumbnailx + thumbnailWidth, childBox.x1 + thumbnail.title.width); childBox.y1 = y + thumbnailHeight + thTitleMargin; childBox.y2 = childBox.y1 + thumbnail.title.height; thumbnail.title.allocate(childBox, flags); x += thumbnailWidth + spacing; y += (i + 1) % nColumns > 0 ? 0 : thumbnailHeight + extraHeight + thTitleMargin; } let x = 0; let y = 0; let buttonWidth = this.button.get_theme_node().get_length('width'); let buttonHeight = this.button.get_theme_node().get_length('height'); let buttonOverlap = this.button.get_theme_node().get_length('-cinnamon-close-overlap'); if (this.lastHovered && this.lastHovered.actor != null && !this.lastHovered.doomed){ x = this.lastHovered.actor.allocation.x1 + ((this.lastHovered.actor.allocation.x2 - this.lastHovered.actor.allocation.x1) * this.lastHovered.actor.get_scale()[0]) - buttonOverlap; y = this.lastHovered.actor.allocation.y1 - (buttonHeight - buttonOverlap); } else { this.button.hide(); } childBox.x1 = x; childBox.x2 = childBox.x1 + buttonWidth; childBox.y1 = y; childBox.y2 = childBox.y1 + buttonHeight; this.button.allocate(childBox, flags); this.emit('allocated'); }, activeWorkspaceChanged: function(wm, from, to, direction) { this.thumbnails[this.kbThumbnailIndex].showKeyboardSelectedState(false); this.kbThumbnailIndex = global.screen.get_active_workspace_index(); this.thumbnails[this.kbThumbnailIndex].showKeyboardSelectedState(true); let thumbnail; let activeWorkspace = global.screen.get_active_workspace(); for (let i = 0; i < this.thumbnails.length; i++) { if (this.thumbnails[i].metaWorkspace == activeWorkspace) { thumbnail = this.thumbnails[i]; break; } } if (this.lastActiveWorkspace) { this.lastActiveWorkspace.setActive(false); } thumbnail.setActive(true); this.lastActiveWorkspace = thumbnail; }, onScrollEvent: function (actor, event) { if (Main.expo.animationInProgress) return; switch ( event.get_scroll_direction() ) { case Clutter.ScrollDirection.UP: Main.wm.actionMoveWorkspaceUp(); break; case Clutter.ScrollDirection.DOWN: Main.wm.actionMoveWorkspaceDown(); break; } } }; Signals.addSignalMethods(ExpoThumbnailsBox.prototype); Cinnamon-2.8.6/js/ui/environment.js0000664000175000017500000000623212625666430016165 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- imports.gi.versions.Clutter = '1.0'; imports.gi.versions.Gio = '2.0'; imports.gi.versions.Gdk = '3.0'; imports.gi.versions.GdkPixbuf = '2.0'; imports.gi.versions.Gtk = '3.0'; const Clutter = imports.gi.Clutter;; const Gettext = imports.gettext; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Overrides = imports.ui.overrides; // We can't import cinnamon JS modules yet, because they may have // variable initializations, etc, that depend on init() already having // been run. // "monkey patch" in some varargs ClutterContainer methods; we need // to do this per-container class since there is no representation // of interfaces in Javascript function _patchContainerClass(containerClass) { // This one is a straightforward mapping of the C method containerClass.prototype.child_set = function(actor, props) { let meta = this.get_child_meta(actor); for (let prop in props) meta[prop] = props[prop]; }; // clutter_container_add() actually is a an add-many-actors // method. We conveniently, but somewhat dubiously, take the // this opportunity to make it do something more useful. containerClass.prototype.add = function(actor, props) { this.add_actor(actor); if (props) this.child_set(actor, props); }; } function init() { // Add some bindings to the global JS namespace; (gjs keeps the web // browser convention of having that namespace be called 'window'.) window.global = Cinnamon.Global.get(); window._ = Gettext.gettext; window.C_ = Gettext.pgettext; window.ngettext = Gettext.ngettext; // Set the default direction for St widgets (this needs to be done before any use of St) if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) { St.Widget.set_default_direction(St.TextDirection.RTL); } // Miscellaneous monkeypatching _patchContainerClass(St.BoxLayout); _patchContainerClass(St.Table); Clutter.Actor.prototype.toString = function() { return St.describe_actor(this); }; let origToString = Object.prototype.toString; Object.prototype.toString = function() { let base = origToString.call(this); if ('actor' in this && this.actor instanceof Clutter.Actor) return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1)); else return base; }; // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783 Date.prototype.toLocaleFormat = function(format) { return Cinnamon.util_format_date(format, this.getTime()); }; let slowdownEnv = GLib.getenv('CINNAMON_SLOWDOWN_FACTOR'); if (slowdownEnv) { let factor = parseFloat(slowdownEnv); if (!isNaN(factor) && factor > 0.0) St.set_slow_down_factor(factor); } // OK, now things are initialized enough that we can import cinnamon JS const Format = imports.misc.format; const Tweener = imports.ui.tweener; Tweener.init(); String.prototype.format = Format.format; Overrides.init(); } Cinnamon-2.8.6/js/ui/cinnamonEntry.js0000664000175000017500000001126712625666430016451 0ustar fabiofabioconst Clutter = imports.gi.Clutter; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const St = imports.gi.St; const Main = imports.ui.main; const Params = imports.misc.params; const PopupMenu = imports.ui.popupMenu; function _EntryMenu(entry, params) { this._init(entry, params); }; _EntryMenu.prototype = { __proto__: PopupMenu.PopupMenu.prototype, _init: function(entry, params) { params = Params.parse (params, { isPassword: false }); PopupMenu.PopupMenu.prototype._init.call(this, entry, 0, St.Side.TOP); this.actor.add_style_class_name('entry-context-menu'); this._entry = entry; this._clipboard = St.Clipboard.get_default(); // Populate menu let item; item = new PopupMenu.PopupMenuItem(_("Copy")); item.connect('activate', Lang.bind(this, this._onCopyActivated)); this.addMenuItem(item); this._copyItem = item; item = new PopupMenu.PopupMenuItem(_("Paste")); item.connect('activate', Lang.bind(this, this._onPasteActivated)); this.addMenuItem(item); this._pasteItem = item; this._passwordItem = null; if (params.isPassword) { item = new PopupMenu.PopupMenuItem(''); item.connect('activate', Lang.bind(this, this._onPasswordActivated)); this.addMenuItem(item); this._passwordItem = item; } Main.uiGroup.add_actor(this.actor); this.actor.hide(); }, open: function() { this._updatePasteItem(); this._updateCopyItem(); if (this._passwordItem) this._updatePasswordItem(); let direction = Gtk.DirectionType.TAB_FORWARD; if (!this.actor.navigate_focus(null, direction, false)) this.actor.grab_key_focus(); PopupMenu.PopupMenu.prototype.open.call(this); }, _updateCopyItem: function() { let selection = this._entry.clutter_text.get_selection(); this._copyItem.setSensitive(selection && selection != ''); }, _updatePasteItem: function() { this._clipboard.get_text(Lang.bind(this, function(clipboard, text) { this._pasteItem.setSensitive(text && text != ''); })); }, _updatePasswordItem: function() { let textHidden = (this._entry.clutter_text.password_char); if (textHidden) this._passwordItem.label.set_text(_("Show Text")); else this._passwordItem.label.set_text(_("Hide Text")); }, _onCopyActivated: function() { let selection = this._entry.clutter_text.get_selection(); this._clipboard.set_text(selection); }, _onPasteActivated: function() { this._clipboard.get_text(Lang.bind(this, function(clipboard, text) { if (!text) return; this._entry.clutter_text.delete_selection(); let pos = this._entry.clutter_text.get_cursor_position(); this._entry.clutter_text.insert_text(text, pos); })); }, _onPasswordActivated: function() { let visible = !!(this._entry.clutter_text.password_char); this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf'); } }; function _setMenuAlignment(entry, stageX) { let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0); if (success) entry._menu.setSourceAlignment(entryX / entry.width); }; function _onClicked(actor, action) { let entry = actor._menu ? actor : actor.get_parent(); if (entry._menu.isOpen) { entry._menu.close(); } else if (action.get_button() == 3) { let [stageX, stageY] = action.get_coords(); _setMenuAlignment(entry, stageX); entry._menu.open(); } }; function _onPopup(actor) { let entry = actor._menu ? actor : actor.get_parent(); let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1); if (success) entry._menu.setSourceAlignment(textX / entry.width); entry._menu.open(); }; function addContextMenu(entry, params) { if (entry._menu) return; entry._menu = new _EntryMenu(entry, params); entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }); entry._menuManager.addMenu(entry._menu); // Add a click action to both the entry and its clutter_text; the former // so padding is included in the clickable area, the latter because the // event processing of ClutterText prevents event-bubbling. entry.connect('button-press-event', _onClicked); entry.clutter_text.connect('button-press-event', _onClicked); entry.connect('popup-menu', _onPopup); } Cinnamon-2.8.6/js/ui/popupMenu.js0000664000175000017500000033027012625666430015613 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cairo = imports.cairo; const Mainloop = imports.mainloop; const Clutter = imports.gi.Clutter; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const Signals = imports.signals; const St = imports.gi.St; const Atk = imports.gi.Atk; const BoxPointer = imports.ui.boxpointer; const DND = imports.ui.dnd; const Main = imports.ui.main; const SignalManager = imports.misc.signalManager; const Tweener = imports.ui.tweener; const CheckBox = imports.ui.checkBox; const RadioButton = imports.ui.radioButton; const Params = imports.misc.params; const Util = imports.misc.util; const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */ const OrnamentType = { NONE: 0, CHECK: 1, DOT: 2, ICON: 3 }; const FactoryClassTypes = { 'RootMenuClass' : "RootMenuClass", 'MenuItemClass' : "MenuItemClass", 'SubMenuMenuItemClass' : "SubMenuMenuItemClass", 'MenuSectionMenuItemClass' : "MenuSectionMenuItemClass", 'SeparatorMenuItemClass' : "SeparatorMenuItemClass" }; const FactoryEventTypes = { 'opened' : "opened", 'closed' : "closed", 'clicked' : "clicked" }; function _ensureStyle(actor) { if (actor.get_children) { let children = actor.get_children(); for (let i = 0; i < children.length; i++) _ensureStyle(children[i]); } if (actor instanceof St.Widget) actor.ensure_style(); } /** * @side Side to which the arrow points. */ function arrowIcon(side) { let iconName; switch (side) { case St.Side.TOP: iconName = 'pan-up'; break; case St.Side.RIGHT: iconName = 'pan-end'; break; case St.Side.BOTTOM: iconName = 'pan-down'; break; case St.Side.LEFT: iconName = 'pan-start'; break; } let arrow = new St.Icon({ style_class: 'popup-menu-arrow', icon_name: iconName, icon_type: St.IconType.SYMBOLIC, y_expand: true, y_align: Clutter.ActorAlign.CENTER, important: true }); return arrow; } function PopupBaseMenuItem(params) { this._init(params); } PopupBaseMenuItem.prototype = { _init: function (params) { params = Params.parse (params, { reactive: true, activate: true, hover: true, sensitive: true, style_class: null, focusOnHover: true }); this.actor = new Cinnamon.GenericContainer({ style_class: 'popup-menu-item', reactive: params.reactive, track_hover: params.reactive, can_focus: params.reactive, accessible_role: Atk.Role.MENU_ITEM }); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); this.actor._delegate = this; this._children = []; this._dot = null; this._columnWidths = null; this._spacing = 0; this.active = false; this._activatable = params.reactive && params.activate; this.sensitive = true; this.focusOnHover = params.focusOnHover; this.setSensitive(this._activatable && params.sensitive); if (params.style_class) this.actor.add_style_class_name(params.style_class); if (this._activatable) { this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent)); this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); } if (params.reactive && params.hover) this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged)); if (params.reactive) { this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut)); } }, _onStyleChanged: function (actor) { this._spacing = Math.round(actor.get_theme_node().get_length('spacing')); }, _onButtonReleaseEvent: function (actor, event) { this.activate(event, false); return true; }, _onKeyPressEvent: function (actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { this.activate(event); return true; } return false; }, _onKeyFocusIn: function (actor) { this.setActive(true); }, _onKeyFocusOut: function (actor) { this.setActive(false); }, _onHoverChanged: function (actor) { this.setActive(actor.hover); }, activate: function (event, keepMenu) { this.emit('activate', event, keepMenu); }, setActive: function (active) { let activeChanged = active != this.active; if (activeChanged) { this.active = active; this.actor.change_style_pseudo_class('active', active); if (this.focusOnHover && this.active) this.actor.grab_key_focus(); this.emit('active-changed', active); } }, setSensitive: function(sensitive) { if (!this._activatable) return; if (this.sensitive == sensitive) return; this.sensitive = sensitive; this.actor.reactive = sensitive; this.actor.can_focus = sensitive; this.actor.change_style_pseudo_class('insensitive', !sensitive); this.emit('sensitive-changed', sensitive); }, destroy: function() { this.actor.destroy(); this.emit('destroy'); }, // adds an actor to the menu item; @params can contain %span // (column span; defaults to 1, -1 means "all the remaining width", 0 means "no new column after this actor"), // %expand (defaults to #false), and %align (defaults to // #St.Align.START) addActor: function(child, params) { params = Params.parse(params, { span: 1, expand: false, align: St.Align.START }); params.actor = child; this._children.push(params); this.actor.connect('destroy', Lang.bind(this, function () { this._removeChild(child); })); this.actor.add_actor(child); }, _removeChild: function(child) { for (let i = 0; i < this._children.length; i++) { if (this._children[i].actor == child) { this._children.splice(i, 1); return; } } }, removeActor: function(child) { this.actor.remove_actor(child); this._removeChild(child); }, setShowDot: function(show) { if (show) { if (this._dot) return; this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' }); this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot)); this.actor.add_actor(this._dot); this.actor.add_accessible_state (Atk.StateType.CHECKED); } else { if (!this._dot) return; this._dot.destroy(); this._dot = null; this.actor.remove_accessible_state (Atk.StateType.CHECKED); } }, _onRepaintDot: function(area) { let cr = area.get_context(); let [width, height] = area.get_surface_size(); let color = area.get_theme_node().get_foreground_color(); cr.setSourceRGBA ( color.red / 255, color.green / 255, color.blue / 255, color.alpha / 255); cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI); cr.fill(); cr.$dispose(); }, // This returns column widths in logical order (i.e. from the dot // to the image), not in visual order (left to right) getColumnWidths: function() { let widths = []; for (let i = 0, col = 0; i < this._children.length; i++) { let child = this._children[i]; let [min, natural] = child.actor.get_preferred_width(-1); if (widths[col]) widths[col] += this._spacing + natural; else widths[col] = natural; if (child.span > 0) { col++; for (let j = 1; j < child.span; j++) widths[col++] = 0; } } return widths; }, setColumnWidths: function(widths) { this._columnWidths = widths; }, _getPreferredWidth: function(actor, forHeight, alloc) { let width = 0; if (this._columnWidths) { for (let i = 0; i < this._columnWidths.length; i++) { if (i > 0) width += this._spacing; width += this._columnWidths[i]; } } else { for (let i = 0; i < this._children.length; i++) { let child = this._children[i]; if (i > 0) width += this._spacing; let [min, natural] = child.actor.get_preferred_width(-1); width += natural; } } alloc.min_size = alloc.natural_size = width; }, _getPreferredHeight: function(actor, forWidth, alloc) { let height = 0, x = 0, minWidth, childWidth; for (let i = 0; i < this._children.length; i++) { let child = this._children[i]; if (this._columnWidths) { if (child.span == -1) { childWidth = 0; for (let j = i; j < this._columnWidths.length; j++) childWidth += this._columnWidths[j] } else childWidth = this._columnWidths[i]; } else { if (child.span == -1) childWidth = forWidth - x; else [minWidth, childWidth] = child.actor.get_preferred_width(-1); } x += childWidth; let [min, natural] = child.actor.get_preferred_height(childWidth); if (natural > height) height = natural; } alloc.min_size = alloc.natural_size = height; }, _allocate: function(actor, box, flags) { let height = box.y2 - box.y1; let direction = this.actor.get_direction(); if (this._dot) { // The dot is placed outside box // one quarter of padding from the border of the container // (so 3/4 from the inner border) // (padding is box.x1) let dotBox = new Clutter.ActorBox(); let dotWidth = Math.round(box.x1 / 2); if (direction == St.TextDirection.LTR) { dotBox.x1 = Math.round(box.x1 / 4); dotBox.x2 = dotBox.x1 + dotWidth; } else { dotBox.x2 = box.x2 + 3 * Math.round(box.x1 / 4); dotBox.x1 = dotBox.x2 - dotWidth; } dotBox.y1 = Math.round(box.y1 + (height - dotWidth) / 2); dotBox.y2 = dotBox.y1 + dotWidth; this._dot.allocate(dotBox, flags); } let x; if (direction == St.TextDirection.LTR) x = box.x1; else x = box.x2; let cols; //clone _columnWidths, if it exists, to be able to modify it without any impact if (this._columnWidths instanceof Array) cols = this._columnWidths.slice(0); // if direction is ltr, x is the right edge of the last added // actor, and it's constantly increasing, whereas if rtl, x is // the left edge and it decreases for (let i = 0, col = 0; i < this._children.length; i++) { let child = this._children[i]; let childBox = new Clutter.ActorBox(); let [minWidth, naturalWidth] = child.actor.get_preferred_width(-1); let availWidth, extraWidth; if (cols) { if (child.span == -1) { if (direction == St.TextDirection.LTR) availWidth = box.x2 - x; else availWidth = x - box.x1; } else if (child.span == 0) { availWidth = naturalWidth; cols[col] -= naturalWidth + this._spacing; } else { availWidth = 0; for (let j = 0; j < child.span; j++) availWidth += cols[col++]; } extraWidth = availWidth - naturalWidth; } else { if (child.span == -1) { if (direction == St.TextDirection.LTR) availWidth = box.x2 - x; else availWidth = x - box.x1; } else { availWidth = naturalWidth; } extraWidth = 0; } if (direction == St.TextDirection.LTR) { if (child.expand) { childBox.x1 = x; childBox.x2 = x + availWidth; } else if (child.align === St.Align.MIDDLE) { childBox.x1 = x + Math.round(extraWidth / 2); childBox.x2 = childBox.x1 + naturalWidth; } else if (child.align === St.Align.END) { childBox.x2 = x + availWidth; childBox.x1 = childBox.x2 - naturalWidth; } else { childBox.x1 = x; childBox.x2 = x + naturalWidth; } //when somehow the actor is wider than the box, cut it off if(childBox.x2 > box.x2) childBox.x2 = box.x2; } else { if (child.expand) { childBox.x1 = x - availWidth; childBox.x2 = x; } else if (child.align === St.Align.MIDDLE) { childBox.x1 = x - Math.round(extraWidth / 2); childBox.x2 = childBox.x1 + naturalWidth; } else if (child.align === St.Align.END) { // align to the left childBox.x1 = x - availWidth; childBox.x2 = childBox.x1 + naturalWidth; } else { // align to the right childBox.x2 = x; childBox.x1 = x - naturalWidth; } //when somehow the actor is wider than the box, cut it off if(childBox.x1 < box.x1) childBox.x1 = box.x1; } let [minHeight, naturalHeight] = child.actor.get_preferred_height(childBox.x2 - childBox.x1); childBox.y1 = Math.round(box.y1 + (height - naturalHeight) / 2); childBox.y2 = childBox.y1 + naturalHeight; child.actor.allocate(childBox, flags); if (direction == St.TextDirection.LTR) x += availWidth + this._spacing; else x -= availWidth + this._spacing; } } }; Signals.addSignalMethods(PopupBaseMenuItem.prototype); function PopupMenuItem() { this._init.apply(this, arguments); } PopupMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function (text, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({ text: text }); this.addActor(this.label); this.actor.label_actor = this.label; } }; function PopupSeparatorMenuItem() { this._init(); } PopupSeparatorMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function () { PopupBaseMenuItem.prototype._init.call(this, { reactive: false }); this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' }); this.addActor(this._drawingArea, { span: -1, expand: true }); this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint)); }, _onRepaint: function(area) { let cr = area.get_context(); let themeNode = area.get_theme_node(); let [width, height] = area.get_surface_size(); let margin = themeNode.get_length('-margin-horizontal'); let gradientHeight = themeNode.get_length('-gradient-height'); let startColor = themeNode.get_color('-gradient-start'); let endColor = themeNode.get_color('-gradient-end'); let gradientWidth = (width - margin * 2); let gradientOffset = (height - gradientHeight) / 2; let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight); pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255); pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255); pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255); cr.setSource(pattern); cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight); cr.fill(); cr.$dispose(); } }; const PopupAlternatingMenuItemState = { DEFAULT: 0, ALTERNATIVE: 1 } function PopupAlternatingMenuItem() { this._init.apply(this, arguments); } PopupAlternatingMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function(text, alternateText, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.actor.add_style_class_name('popup-alternating-menu-item'); this._text = text; this._alternateText = alternateText; this.label = new St.Label({ text: text }); this.state = PopupAlternatingMenuItemState.DEFAULT; this.addActor(this.label); this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped)); }, _onMapped: function() { if (this.actor.mapped) { this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); this._updateStateFromModifiers(); } else { if (this._capturedEventId != 0) { global.stage.disconnect(this._capturedEventId); this._capturedEventId = 0; } } }, _setState: function(state) { if (this.state != state) { if (state == PopupAlternatingMenuItemState.ALTERNATIVE && !this._canAlternate()) return; this.state = state; this._updateLabel(); } }, _updateStateFromModifiers: function() { let [x, y, mods] = global.get_pointer(); let state; if ((mods & Clutter.ModifierType.MOD1_MASK) == 0) { state = PopupAlternatingMenuItemState.DEFAULT; } else { state = PopupAlternatingMenuItemState.ALTERNATIVE; } this._setState(state); }, _onCapturedEvent: function(actor, event) { if (event.type() != Clutter.EventType.KEY_PRESS && event.type() != Clutter.EventType.KEY_RELEASE) return false; let key = event.get_key_symbol(); if (key == Clutter.KEY_Alt_L || key == Clutter.KEY_Alt_R) this._updateStateFromModifiers(); return false; }, _updateLabel: function() { if (this.state == PopupAlternatingMenuItemState.ALTERNATIVE) { this.actor.add_style_pseudo_class('alternate'); this.label.set_text(this._alternateText); } else { this.actor.remove_style_pseudo_class('alternate'); this.label.set_text(this._text); } }, _canAlternate: function() { if (this.state == PopupAlternatingMenuItemState.DEFAULT && !this._alternateText) return false; return true; }, updateText: function(text, alternateText) { this._text = text; this._alternateText = alternateText; if (!this._canAlternate()) this._setState(PopupAlternatingMenuItemState.DEFAULT); this._updateLabel(); } }; function PopupSliderMenuItem() { this._init.apply(this, arguments); } PopupSliderMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function(value) { PopupBaseMenuItem.prototype._init.call(this, { activate: false }); this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); if (isNaN(value)) // Avoid spreading NaNs around throw TypeError('The slider value must be a number'); this._value = Math.max(Math.min(value, 1), 0); this._slider = new St.DrawingArea({ style_class: 'popup-slider-menu-item', reactive: true }); this.addActor(this._slider, { span: -1, expand: true }); this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint)); this.actor.connect('button-press-event', Lang.bind(this, this._startDragging)); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this._releaseId = this._motionId = 0; this._dragging = false; }, setValue: function(value) { if (isNaN(value)) throw TypeError('The slider value must be a number'); this._value = Math.max(Math.min(value, 1), 0); this._slider.queue_repaint(); }, _sliderRepaint: function(area) { let cr = area.get_context(); let themeNode = area.get_theme_node(); let [width, height] = area.get_surface_size(); let handleRadius = themeNode.get_length('-slider-handle-radius'); let sliderWidth = width - 2 * handleRadius; let sliderHeight = themeNode.get_length('-slider-height'); let sliderBorderWidth = themeNode.get_length('-slider-border-width'); let sliderBorderRadius = Math.min(width, sliderHeight) / 2; let sliderBorderColor = themeNode.get_color('-slider-border-color'); let sliderColor = themeNode.get_color('-slider-background-color'); let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color'); let sliderActiveColor = themeNode.get_color('-slider-active-background-color'); const TAU = Math.PI * 2; let handleX = handleRadius + (width - 2 * handleRadius) * this._value; cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4); cr.lineTo(handleX, (height - sliderHeight) / 2); cr.lineTo(handleX, (height + sliderHeight) / 2); cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2); Clutter.cairo_set_source_color(cr, sliderActiveColor); cr.fillPreserve(); Clutter.cairo_set_source_color(cr, sliderActiveBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4); cr.lineTo(handleX, (height + sliderHeight) / 2); cr.lineTo(handleX, (height - sliderHeight) / 2); cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2); Clutter.cairo_set_source_color(cr, sliderColor); cr.fillPreserve(); Clutter.cairo_set_source_color(cr, sliderBorderColor); cr.setLineWidth(sliderBorderWidth); cr.stroke(); let handleY = height / 2; let color = themeNode.get_foreground_color(); Clutter.cairo_set_source_color(cr, color); cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI); cr.fill(); cr.$dispose(); }, _startDragging: function(actor, event) { if (this._dragging) // don't allow two drags at the same time return; this.emit('drag-begin'); this._dragging = true; // FIXME: we should only grab the specific device that originated // the event, but for some weird reason events are still delivered // outside the slider if using clutter_grab_pointer_for_device Clutter.grab_pointer(this._slider); this._releaseId = this._slider.connect('button-release-event', Lang.bind(this, this._endDragging)); this._motionId = this._slider.connect('motion-event', Lang.bind(this, this._motionEvent)); let absX, absY; [absX, absY] = event.get_coords(); this._moveHandle(absX, absY); }, _endDragging: function() { if (this._dragging) { this._slider.disconnect(this._releaseId); this._slider.disconnect(this._motionId); Clutter.ungrab_pointer(); this._dragging = false; this.emit('drag-end'); } return true; }, _onScrollEvent: function (actor, event) { let direction = event.get_scroll_direction(); if (direction == Clutter.ScrollDirection.DOWN) { this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP); } else if (direction == Clutter.ScrollDirection.UP) { this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP); } this._slider.queue_repaint(); this.emit('value-changed', this._value); }, _motionEvent: function(actor, event) { let absX, absY; [absX, absY] = event.get_coords(); this._moveHandle(absX, absY); return true; }, _moveHandle: function(absX, absY) { let relX, relY, sliderX, sliderY; [sliderX, sliderY] = this._slider.get_transformed_position(); relX = absX - sliderX; relY = absY - sliderY; let width = this._slider.width; let handleRadius = this._slider.get_theme_node().get_length('-slider-handle-radius'); let newvalue; if (relX < handleRadius) newvalue = 0; else if (relX > width - handleRadius) newvalue = 1; else newvalue = (relX - handleRadius) / (width - 2 * handleRadius); this._value = newvalue; this._slider.queue_repaint(); this.emit('value-changed', this._value); }, get value() { return this._value; }, _onKeyPressEvent: function (actor, event) { let key = event.get_key_symbol(); if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) { let delta = key == Clutter.KEY_Right ? 0.1 : -0.1; this._value = Math.max(0, Math.min(this._value + delta, 1)); this._slider.queue_repaint(); this.emit('value-changed', this._value); this.emit('drag-end'); return true; } return false; } }; function Switch() { this._init.apply(this, arguments); } Switch.prototype = { _init: function(state) { this.actor = new St.Bin({ style_class: 'toggle-switch' , accessible_role: Atk.Role.CHECK_BOX}); // Translators: this MUST be either "toggle-switch-us" // (for toggle switches containing the English words // "ON" and "OFF") or "toggle-switch-intl" (for toggle // switches containing "O" and "|"). Other values will // simply result in invisible toggle switches. this.actor.add_style_class_name("toggle-switch-us"); this.setToggleState(state); }, setToggleState: function(state) { this.actor.change_style_pseudo_class('checked', state); this.state = state; }, toggle: function() { this.setToggleState(!this.state); } }; function PopupSwitchMenuItem() { this._init.apply(this, arguments); } PopupSwitchMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function(text, active, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({ text: text }); this._statusLabel = new St.Label({ text: '', style_class: 'popup-inactive-menu-item' }); this._switch = new Switch(active); this.addActor(this.label); this.addActor(this._statusLabel); this._statusBin = new St.Bin({ x_align: St.Align.END }); this.addActor(this._statusBin, { expand: true, span: -1, align: St.Align.END }); this._statusBin.child = this._switch.actor; }, setStatus: function(text) { if (text != null) { this._statusLabel.set_text(text); } else { this._statusLabel.set_text(''); } }, activate: function(event) { if (this._switch.actor.mapped) { this.toggle(); } PopupBaseMenuItem.prototype.activate.call(this, event, true); }, toggle: function() { this._switch.toggle(); this.emit('toggled', this._switch.state); }, get state() { return this._switch.state; }, setToggleState: function(state) { this._switch.setToggleState(state); } }; /** * #PopupIconMenuItem: * @short_description: A menu item with an icon and a text. * * This is a popup menu item displaying an icon and a text. The icon is * displayed to the left of the text. #PopupImageMenuItem is a similar, * deprecated item, that displays the icon to the right of the text, which is * ugly in most cases. Do not use it. If you think you need to display the icon * on the right, make your own menu item (by copy and pasting the code found * below) because PopupImageMenuItem is deprecated and may disappear any time. */ function PopupIconMenuItem() { this._init.apply(this, arguments); } PopupIconMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, /** * _init: * @text (string): text to display in the label * @iconName (string): name of the icon used * @iconType (St.IconType): the type of icon (usually #St.IconType.SYMBOLIC * or #St.IconType.FULLCOLOR) * @params (JSON): parameters to pass to %PopupMenu.PopupBaseMenuItem._init */ _init: function (text, iconName, iconType, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({text: text}); this._icon = new St.Icon({ style_class: 'popup-menu-icon', icon_name: iconName, icon_type: iconType}); this.addActor(this._icon, {span: 0}); this.addActor(this.label); }, /** * setIconSymbolicName: * @iconName (string): name of the icon * * Changes the icon to a symbolic icon with name @iconName. */ setIconSymbolicName: function (iconName) { this._icon.set_icon_name(iconName); this._icon.set_icon_type(St.IconType.SYMBOLIC); }, /** * setIconName: * @iconName (string): name of the icon * * Changes the icon to a full color icon with name @iconName. */ setIconName: function (iconName) { this._icon.set_icon_name(iconName); this._icon.set_icon_type(St.IconType.FULLCOLOR); } } // Deprecated. Do not use function PopupImageMenuItem() { this._init.apply(this, arguments); } PopupImageMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function (text, iconName, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({ text: text }); this.addActor(this.label); this._icon = new St.Icon({ style_class: 'popup-menu-icon' }); this.addActor(this._icon, { align: St.Align.END }); this.setIcon(iconName); }, setIcon: function(name) { this._icon.icon_name = name; } }; /** * #PopupIndicatorMenuItem: * @short_description: A menu item with text, ornaments and accel. * * This is a popup menu item displaying an text, a accel, and a ornament. The ornament * is displayed to the left of the text and the accel will be displayed at the end of * the item. The default ornament is an icon, but can be replace for a check button, * a radio button or empty. */ function PopupIndicatorMenuItem() { this._init.apply(this, arguments); } PopupIndicatorMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function(text, params) { PopupBaseMenuItem.prototype._init.call(this, params); this.actor._delegate = this; this._displayIcon = false; this.label = new St.Label({ text: text }); this._accel = new St.Label({ x_align: St.Align.END }); this._ornament = new St.Bin(); this._icon = new St.Icon({ style_class: 'popup-menu-icon', icon_type: St.IconType.FULLCOLOR }); this._ornament.child = this._icon; this.addActor(this._ornament, {span: 0}); this.addActor(this.label); this.addActor(this._accel, { align: St.Align.END }); }, setAccel: function(accel) { this._accel.set_text(accel); }, haveIcon: function() { return ((this._icon)&&((this._icon.icon_name && this._icon.icon_name != "") || (this._icon.gicon))); }, setIconName: function(name) { if (this._icon) this._icon.icon_name = name; }, setGIcon: function(gicon) { if (this._icon) this._icon.gicon = gicon; }, setOrnament: function(ornamentType, state) { switch (ornamentType) { case OrnamentType.CHECK: if ((this._ornament.child)&&(!(this._ornament.child._delegate instanceof CheckBox.CheckButton))) { this._ornament.child.destroy(); this._ornament.child = null; } if (!this._ornament.child) { let switchOrn = new CheckBox.CheckButton(state); this._ornament.child = switchOrn.actor; } else { this._ornament.child._delegate.setToggleState(state); } this._icon = null; break; case OrnamentType.DOT: if ((this._ornament.child)&&(!(this._ornament.child._delegate instanceof RadioButton.RadioBox))) { this._ornament.child.destroy(); this._ornament.child = null; } if (!this._ornament.child) { let radioOrn = new RadioButton.RadioBox(state); this._ornament.child = radioOrn.actor; } else { this._ornament.child._delegate.setToggleState(state); } this._icon = null; break; } }, destroy: function() { if (this.actor) { PopupMenuItem.prototype.destroy.call(this); this.actor = null; } } }; /** * #PopupMenuAbstractItem: * @short_description: A class to represent any abstract menu item. * * This is an abstract class for create a binding between the PopupMenuItem class , * and an abstract representation of a menu item. If you want to create a cinnamon * menu structure, you need to inherit from this class and implement the functions * getItemById and handleEvent. All instances of this class need to have a unique * id to represent a menu item. */ function PopupMenuAbstractItem() { throw new TypeError('Trying to instantiate abstract class PopupMenuAbstractItem'); } PopupMenuAbstractItem.prototype = { _init: function(id, childrenIds, params) { this._id = id; this._childrenIds = childrenIds; if (!this._childrenIds) this._childrenIds = new Array(); /*this._shellMenuSignalsHandlers = null; this._internalSignalsHandlers = new SignalManager.SignalManager(this); this._externalSignalsHandlers = new SignalManager.SignalManager(this);*/ this._internalSignalsHandlers = new Array(); this._externalSignalsHandlers = new Array(); this._shellItemSignalsHandlers = null; this._shellMenuSignalsHandlers = null; this.shellItem = null; this.parent = null; // Properties params = Params.parse (params, { label: "", accel: "", sensitive: true, visible: true, toggleType: "", toggleState: false, iconName: "", iconData: null, action:"", paramType: "", // This is a variant for GTK, better remove it? type: FactoryClassTypes.MenuItemClass }); this._label = params.label; this._accel = params.accel; this._sensitive = params.sensitive; this._visible = params.visible; this._toggleType = params.toggleType; this._toggleState = params.toggleState; this._iconName = params.iconName; this._iconData = params.iconData; this._type = params.type; this._action = params.action; this._paramType = params.paramType; }, getItemById: function(id) {throw new Error('Trying to use abstract function getItemById');}, handleEvent: function(event, params) {throw new Error('Trying to use abstract function handleEvent');}, //FIXME: Will be intresting this function? We don't use it anyway... //is_root: function() {throw new Error('Trying to use abstract function is_root');}, isVisible: function() { return this._visible; }, setVisible: function(visible) { if (this._visible != visible) { this._visible = visible; this._updateVisible(); } }, isSensitive: function() { return this._sensitive; }, setSensitive: function(sensitive) { if (this._sensitive != sensitive) { this._sensitive = sensitive; this._updateSensitive(); } }, getLabel: function() { return this._label; }, setLabel: function(label) { if (this._label != label) { this._label = label; this._updateLabel(); } }, getAction: function() { return this._action; }, setAction: function(action) { if (this._action != action) { this._action = action; } }, getParamType: function() { return this._paramType; }, setParamType: function(paramType) { if (this._paramType != paramType) { this._paramType = paramType; } }, getFactoryType: function() { return this._type; }, setFactoryType: function(type) { if ((type) && (this._type != type)) { this._type = type; this._updateType(); } }, getIconName: function() { return this._iconName; }, setIconName: function(iconName) { if (this._iconName != iconName) { this._iconName = iconName; this._updateImage(); } }, getGdkIcon: function() { return this._iconData; }, setGdkIcon: function(iconData) { if (this._iconData != iconData) { this._iconData = iconData; this._updateImage(); } }, getToggleType: function() { return this._toggleType; }, setToggleType: function(toggleType) { if (this._toggleType != toggleType) { this._toggleType = toggleType; this._updateOrnament(); } }, getToggleState: function() { return this._toggleState; }, setToggleState: function(toggleState) { if (this._toggleState != toggleState) { this._toggleState = toggleState; this._updateOrnament(); } }, getAccel: function() { return this._accel; }, setAccel: function(accel) { if (this._accel != accel) { this._accel = accel; this._updateAccel(); } }, setShellItem: function(shellItem, handlers) { if (this.shellItem != shellItem) { if (this.shellItem) { // FIXME: This create problems, why? //this.shellItem.destroy(); global.logWarning("Attempt to override a shellItem, so we automatically destroy our original shellItem."); } this.shellItem = shellItem; if (this.shellItem) { // Initialize our state this._updateLabel(); this._updateOrnament(); this._updateAccel(); this._updateImage(); this._updateVisible(); this._updateSensitive(); /*for (let signal in handlers) { this._internalSignalsHandlers.connect(this, signal, handlers[signal]); }*/ this._connectAndSaveId(this, handlers, this._internalSignalsHandlers); this._shellItemSignalsHandlers = this._connectAndSaveId(this.shellItem, { 'activate': Lang.bind(this, this._onActivate), 'destroy' : Lang.bind(this, this._onShellItemDestroyed) }); /*this._internalSignalsHandlers.connect(this.shellItem, 'activate', this._onActivate); this._internalSignalsHandlers.connect(this.shellItem, 'destroy', this._onShellItemDestroyed);*/ if (this.shellItem.menu) { /*this._shellMenuSignalsHandlers = new SignalManager.SignalManager(this); this._shellMenuSignalsHandlers.connect(this.shellItem.menu, 'open-state-changed', this._onOpenStateChanged); this._shellMenuSignalsHandlers.connect(this.shellItem.menu, 'destroy', this._onShellMenuDestroyed);*/ this._shellMenuSignalsHandlers = this._connectAndSaveId(this.shellItem.menu, { 'open-state-changed': Lang.bind(this, this._onOpenStateChanged), 'destroy' : Lang.bind(this, this._onShellMenuDestroyed) }); } else { //this._internalSignalsHandlers.connect(this.shellItem, 'open-state-changed', this._onOpenStateChanged); this._connectAndSaveId(this.shellItem, { 'open-state-changed': Lang.bind(this, this._onOpenStateChanged), }, this._shellItemSignalsHandlers); } } } }, _updateLabel: function() { if ((this.shellItem)&&(this.shellItem.label)) { let label = this.getLabel(); // The separator item might not even have a hidden label if (this.shellItem.label) this.shellItem.label.set_text(label); } }, _updateOrnament: function() { // Separators and alike might not have gotten the setOrnament function if ((this.shellItem)&&(this.shellItem.setOrnament)) { if (this.getToggleType() == "checkmark") { this.shellItem.setOrnament(OrnamentType.CHECK, this.getToggleState()); } else if (this.getToggleType() == "radio") { this.shellItem.setOrnament(OrnamentType.DOT, this.getToggleState()); } else { this.shellItem.setOrnament(OrnamentType.NONE); } } }, _updateAccel: function() { if ((this.shellItem)&&(this.shellItem._accel)) { let accel = this.getAccel(); if (accel) { this.shellItem._accel.set_text(accel); } } }, _updateImage: function() { // Might be missing on submenus / separators if ((this.shellItem)&&(this.shellItem._icon)) { let iconName = this.getIconName(); if (iconName) { if (this.shellItem.setIconName) this.shellItem.setIconName(iconName); else if (this.shellItem._icon) { this.shellItem._icon.icon_name = iconName; this.shellItem._icon.show(); } } else { let gicon = this.getGdkIcon(); if (gicon) { if (this.shellItem.setGIcon) this.shellItem.setGIcon(gicon); else if (this.shellItem._icon) { this.shellItem._icon.gicon = gicon; this.shellItem._icon.show(); } } } } }, _updateVisible: function() { if (this.shellItem) { this.shellItem.actor.visible = this.isVisible(); } }, _updateSensitive: function() { if ((this.shellItem)&&(this.shellItem.setSensitive)) { this.shellItem.setSensitive(this.isSensitive()); } }, _updateType: function() { this.emit('type-changed'); }, getShellItem: function() { return this.shellItem; }, getId: function() { return this._id; }, getChildrenIds: function() { // Clone it! return this._childrenIds.concat(); }, getChildren: function() { return this._childrenIds.map(function(child_id) { return this.getItemById(child_id); }, this); }, getParent: function() { return this.parent; }, setParent: function(parent) { this.parent = parent; }, addChild: function(pos, child_id) { let factoryItem = this.getItemById(child_id); if (factoryItem) { // If our item is previusly asigned, so destroy first the shell item. factoryItem.destroyShellItem(); factoryItem.setParent(this); this._childrenIds.splice(pos, 0, child_id); this.emit('child-added', factoryItem, pos); } }, removeChild: function(child_id) { // Find it let pos = -1; for (let i = 0; i < this._childrenIds.length; ++i) { if (this._childrenIds[i] == child_id) { pos = i; break; } } if (pos < 0) { global.logError("Trying to remove child which doesn't exist"); } else { this._childrenIds.splice(pos, 1); let factoryItem = this.getItemById(child_id); if (factoryItem) { let shellItem = factoryItem.getShellItem(); this._destroyShellItem(shellItem); factoryItem.setParent(null); this.emit('child-removed', factoryItem); } } if (this._childrenIds.length == 0) { this.emit('childs-empty'); } }, moveChild: function(child_id, newpos) { // Find the old position let oldpos = -1; for (let i = 0; i < this._childrenIds.length; ++i) { if (this._childrenIds[i] == child_id) { oldpos = i; break; } } if (oldpos < 0) { global.logError("Tried to move child which wasn't in the list"); return; } if (oldpos != newpos) { this._childrenIds.splice(oldpos, 1); this._childrenIds.splice(newpos, 0, child_id); this.emit('child-moved', this.getItemById(child_id), oldpos, newpos); } }, // handlers = { "signal": handler } connectAndRemoveOnDestroy: function(handlers) { /*for (let signal in handlers) { this._externalSignalsHandlers.connect(this, signal, handlers[signal]); }*/ this._connectAndSaveId(this, handlers, this._externalSignalsHandlers); }, destroyShellItem: function() { this._destroyShellItem(this.shellItem); }, // We try to not crash cinnamon if a shellItem will be destroyed and has the focus, // then we are moving the focus to the source actor. _destroyShellItem: function(shellItem) { if (shellItem) { let focus = global.stage.key_focus; if (shellItem.close) shellItem.close(); if (shellItem.menu) shellItem.menu.close(); if (focus && shellItem.actor && shellItem.actor.contains(focus)) { if (shellItem.sourceActor) shellItem.sourceActor.grab_key_focus(); else if ((shellItem.menu)&&(shellItem.menu.sourceActor)) shellItem.menu.sourceActor.grab_key_focus(); else global.stage.set_key_focus(null); } shellItem.destroy(); } }, // handlers = { "signal": handler } _connectAndSaveId: function(target, handlers , idArray) { idArray = typeof idArray != 'undefined' ? idArray : []; for (let signal in handlers) { idArray.push(target.connect(signal, handlers[signal])); } return idArray; }, _disconnectSignals: function(obj, signals_handlers) { if ((obj)&&(signals_handlers)) { for (let pos in signals_handlers) obj.disconnect(signals_handlers[pos]); } }, _onActivate: function(shellItem, event, keepMenu) { this.handleEvent("clicked"); }, _onOpenStateChanged: function(menu, open) { if (open) { this.handleEvent("opened"); } else { this.handleEvent("closed"); } }, _onShellItemDestroyed: function(shellItem) { if ((this.shellItem)&&(this.shellItem == shellItem)) { this.shellItem = null; /*if (this._internalSignalsHandlers) { this._internalSignalsHandlers.disconnectAllSignals(); }*/ if (this._internalSignalsHandlers) { this._disconnectSignals(this, this._internalSignalsHandlers); this._internalSignalsHandlers = []; } if (this._shellItemSignalsHandlers) { this._disconnectSignals(shellItem, this._shellItemSignalsHandlers); this._shellItemSignalsHandlers = null; } } else if (this.shellItem) { global.logError("We are not conected with " + shellItem); } else { global.logWarning("We are not conected with any shellItem"); } }, _onShellMenuDestroyed: function(shellMenu) { /*if (this._shellMenuSignalsHandlers) { this._shellMenuSignalsHandlers.disconnectAllSignals(); this._shellMenuSignalsHandlers = null; }*/ if (this._shellMenuSignalsHandlers) { this._disconnectSignals(shellMenu, this._shellMenuSignalsHandlers); this._shellMenuSignalsHandlers = null; } }, destroy: function() { if (this._externalSignalsHandlers) { // Emit the destroy signal first, to allow the external listener know about it, // then, disconnect the listener handler. this.emit("destroy"); this.destroyShellItem(); this.shellItem = null; //this._externalSignalsHandlers.disconnectAllSignals(); this._disconnectSignals(this, this._externalSignalsHandlers); this._externalSignalsHandlers = null; this._internalSignalsHandlers = null; } } }; Signals.addSignalMethods(PopupMenuAbstractItem.prototype); /** * #PopupMenuBase * @short_description: The base class of all popup menus * @sourceActor (St.Widget): The actor that owns the popup menu * @box (St.BoxLayout): The box containing the popup menu widgets. * @isOpen (boolean): Whether the popup menu is open. * @blockSourceEvents (boolean): If set, we don't send events (including * crossing events) to the source actor for the menu which causes its prelight * state to freeze * * @passEvents (boolean): Can be set while a menu is up to let all events * through without special menu handling useful for scrollbars in menus, and * probably not otherwise. * * @firstMenuItem (PopupMenu.PopupBaseMenuItem): The first item in the popup * menu * @numMenuItems (int): The number of items in the popup menu. * * This is a base popup menu class for more sophisticated popup menus to * inherit. This cannot be instantiated. */ function PopupMenuBase() { throw new TypeError('Trying to instantiate abstract class PopupMenuBase'); } PopupMenuBase.prototype = { /** * _init: * @sourceActor (St.Widget): the actor that owns the popup menu * @styleClass (string): (optional) the style class of the popup menu */ _init: function(sourceActor, styleClass) { this.sourceActor = sourceActor; if (styleClass !== undefined) { this.box = new St.BoxLayout({ style_class: styleClass, vertical: true }); } else { this.box = new St.BoxLayout({ vertical: true }); } this.box.connect_after('queue-relayout', Lang.bind(this, this._menuQueueRelayout)); this.length = 0; this.isOpen = false; this.blockSourceEvents = false; this.passEvents = false; this._activeMenuItem = null; this._childMenus = []; }, /** * addAction: * @title (string): the text to display on the item * @callback (function): the function to call when clicked * * Adds a #PopupMenuItem with label @title to the menu. When the item is * clicked, @callback will be called. * * Returns (PopupMenu.PopupMenuItem): the menu item created. */ addAction: function(title, callback) { let menuItem = new PopupMenuItem(title); this.addMenuItem(menuItem); menuItem.connect('activate', Lang.bind(this, function (menuItem, event) { callback(event); })); return menuItem; }, /** * addSettingsAction: * @title (string): the text to display on the item * @module (string): the module to launch * * Adds a #PopupMenuItem with label @title to the menu. When the item is * clicked, Cinnamon Settings will be launched with the module @module * activated. * * Returns (PopupMenu.PopupMenuItem): the menu item created. */ addSettingsAction: function(title, module) { let menuItem = this.addAction(title, function() { Util.spawnCommandLine("cinnamon-settings " + module); }); return menuItem; }, /** * addCommandlineAction: * @title (string): the text to display on the item * @cmd (string): the command to call * * Adds a #PopupMenuItem with label @title to the menu. When the item is * clicked, the command @cmd will be executed. * * Returns (PopupMenu.PopupMenuItem): the menu item created. */ addCommandlineAction: function(title, cmd) { let menuItem = this.addAction(title, function() { Util.spawnCommandLine(cmd); }); return menuItem }, /** * isChildMenu: * @menu (PopupMenu.PopupMenuBase): the menu of interest * * Returns: whether @menu is a submenu of this menu. */ isChildMenu: function(menu) { return this._childMenus.indexOf(menu) != -1; }, /** * addChildMenu: * @menu (PopupMenu.PopupMenuBase): the menu of interest * * Makes @menu a submenu of this menu. */ addChildMenu: function(menu) { if (this.isChildMenu(menu)) return; this._childMenus.push(menu); /** * SIGNAL:child-menu-added * @menu (PopupMenu.PopupMenuBase): The menu added * * Emitted when an menu is added as a submenu. */ this.emit('child-menu-added', menu); }, /** * removeChildMenu: * @menu (PopupMenuBase): the menu of interest * * Removes @menu from the current menu if it is a child. */ removeChildMenu: function(menu) { let index = this._childMenus.indexOf(menu); if (index == -1) return; this._childMenus.splice(index, 1); this.emit('child-menu-removed', menu); /** * SIGNAL:child-menu-removed * @menu (PopupMenu.PopupMenuBase): The menu removed * * Emitted when an submenu is removed. */ }, _connectSubMenuSignals: function(object, menu) { /** * SIGNAL:activate * @menuItem (PopupBaseMenuItem): the item activated * @keepMenu (boolean): whether the menu should remain opened * * Emitted when an item of the menu is activated. */ /** * SIGNAL:active-changed * @menuItem (PopupBaseMenuItem): the current active item (possibly null) * * Emitted when the active item of menu is changed. */ object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function(submenu, submenuItem, keepMenu) { this.emit('activate', submenuItem, keepMenu); if (!keepMenu){ this.close(true); } })); object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) { if (this._activeMenuItem && this._activeMenuItem != submenuItem) this._activeMenuItem.setActive(false); this._activeMenuItem = submenuItem; this.emit('active-changed', submenuItem); })); }, _connectItemSignals: function(menuItem) { menuItem._activeChangeId = menuItem.connect('active-changed', Lang.bind(this, function (menuItem, active) { if (active && this._activeMenuItem != menuItem) { if (this._activeMenuItem) this._activeMenuItem.setActive(false); this._activeMenuItem = menuItem; this.emit('active-changed', menuItem); } else if (!active && this._activeMenuItem == menuItem) { this._activeMenuItem = null; this.emit('active-changed', null); } })); menuItem._sensitiveChangeId = menuItem.connect('sensitive-changed', Lang.bind(this, function(menuItem, sensitive) { if (!sensitive && this._activeMenuItem == menuItem) { if (!this.actor.navigate_focus(menuItem.actor, Gtk.DirectionType.TAB_FORWARD, true)) this.actor.grab_key_focus(); } else if (sensitive && this._activeMenuItem == null) { if (global.stage.get_key_focus() == this.actor) menuItem.actor.grab_key_focus(); } })); menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event, keepMenu) { this.emit('activate', menuItem, keepMenu); if (!keepMenu){ this.close(true); } })); menuItem.connect('destroy', Lang.bind(this, function(emitter) { menuItem.disconnect(menuItem._activateId); menuItem.disconnect(menuItem._activeChangeId); menuItem.disconnect(menuItem._sensitiveChangeId); if (menuItem.menu) { menuItem.menu.disconnect(menuItem._subMenuActivateId); menuItem.menu.disconnect(menuItem._subMenuActiveChangeId); this.disconnect(menuItem._closingId); } if (menuItem == this._activeMenuItem) this._activeMenuItem = null; this.length--; })); }, _updateSeparatorVisibility: function(menuItem) { let children = this.box.get_children(); let index = children.indexOf(menuItem.actor); if (index < 0) return; let childBeforeIndex = index - 1; while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible) childBeforeIndex--; if (childBeforeIndex < 0 || children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) { menuItem.actor.hide(); return; } let childAfterIndex = index + 1; while (childAfterIndex < children.length && !children[childAfterIndex].visible) childAfterIndex++; if (childAfterIndex >= children.length || children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) { menuItem.actor.hide(); return; } menuItem.actor.show(); }, /** * addMenuItem: * @menuItem (PopupMenu.PopupBaseMenuItem): the item to include (can also * be a #PopupMenuSection) * @position (int): (optional) position to add the item at (empty for end * of menu) * * Adds the @menuItem to the menu. */ addMenuItem: function(menuItem, position) { let before_item = null; if (position == undefined) { this.box.add(menuItem.actor); } else { let items = this._getMenuItems(); if (position < items.length) { before_item = items[position].actor; this.box.insert_before(menuItem.actor, before_item); } else this.box.add(menuItem.actor); } if (menuItem instanceof PopupMenuSection) { this._connectSubMenuSignals(menuItem, menuItem); menuItem.connect('destroy', Lang.bind(this, function() { menuItem.disconnect(menuItem._subMenuActivateId); menuItem.disconnect(menuItem._subMenuActiveChangeId); this.length--; })); } else if (menuItem instanceof PopupSubMenuMenuItem) { if (before_item == null) this.box.add(menuItem.menu.actor); else this.box.insert_before(menuItem.menu.actor, before_item); this._connectSubMenuSignals(menuItem, menuItem.menu); this._connectItemSignals(menuItem); menuItem._closingId = this.connect('open-state-changed', function(self, open) { if (!open) menuItem.menu.close(false); }); } else if (menuItem instanceof PopupSeparatorMenuItem) { this._connectItemSignals(menuItem); // updateSeparatorVisibility needs to get called any time the // separator's adjacent siblings change visibility or position. // open-state-changed isn't exactly that, but doing it in more // precise ways would require a lot more bookkeeping. this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); })); this.box.connect('allocation-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); })); } else if (menuItem instanceof PopupBaseMenuItem) this._connectItemSignals(menuItem); else throw TypeError("Invalid argument to PopupMenuBase.addMenuItem()"); this.length++; }, /** * getColumnWidths: * * Gets the width of each column this thing has. In popup menus, everything * is put into columns, and the columns of all items align. This is used * internally and shouldn't be fiddled with unless you are implementing * other popup menu items. */ getColumnWidths: function() { let columnWidths = []; let items = this.box.get_children(); for (let i = 0; i < items.length; i++) { if (!items[i].visible) continue; if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) { let itemColumnWidths = items[i]._delegate.getColumnWidths(); for (let j = 0; j < itemColumnWidths.length; j++) { if (j >= columnWidths.length || itemColumnWidths[j] > columnWidths[j]) columnWidths[j] = itemColumnWidths[j]; } } } return columnWidths; }, /** * setColumnWidths: * @widths (array): the widths of each column * * Sets the widths of each column according to @widths so that things can * align. */ setColumnWidths: function(widths) { let items = this.box.get_children(); for (let i = 0; i < items.length; i++) { if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) items[i]._delegate.setColumnWidths(widths); } }, // Because of the above column-width funniness, we need to do a // queue-relayout on every item whenever the menu itself changes // size, to force clutter to drop its cached size requests. (The // menuitems will in turn call queue_relayout on their parent, the // menu, but that call will be a no-op since the menu already // has a relayout queued, so we won't get stuck in a loop. _menuQueueRelayout: function() { this.box.get_children().map(function (actor) { actor.queue_relayout(); }); }, addActor: function(actor) { this.box.add(actor); }, _getMenuItems: function() { return this.box.get_children().map(function (actor) { return actor._delegate; }).filter(function(item) { return item instanceof PopupBaseMenuItem || item instanceof PopupMenuSection; }); }, get firstMenuItem() { let items = this._getMenuItems(); if (items.length) return items[0]; else return null; }, get numMenuItems() { return this._getMenuItems().length; }, /** * removeAll: * * Clears everything inside the menu. */ removeAll: function() { let children = this._getMenuItems(); for (let i = 0; i < children.length; i++) { let item = children[i]; item.destroy(); } }, /** * toggle: * * Toggles the open/close state of the menu. */ toggle: function() { if (this.isOpen) this.close(true); else this.open(true); }, /** * toggle_with_options: * @animate (boolean): whether or not to animate the open/close. * @onComplete (function): the function to call when the toggle action * completes. * * Toggles the open/close state of the menu with extra parameters */ toggle_with_options: function (animate, onComplete) { if (this.isOpen) { this.close(animate, onComplete); } else { this.open(animate, onComplete); } }, /** * destroy: * * Destroys the popup menu completely. */ destroy: function() { this.removeAll(); this.actor.destroy(); /** * SIGNAL:destroy * * Emitted when the menu is destroyed. */ this.emit('destroy'); } }; Signals.addSignalMethods(PopupMenuBase.prototype); /** * #PopupMenu * @short_description: An actual popup menu * @_boxPointer (Boxpointer.BoxPointer): The box pointer object that actually * draws the popup menu. * @actor (St.Bin): The actor of the popup menu, stolen from the %_boxPointer. * @animating (boolean): Whether the popup menu is currently performing the * open/close animation. */ function PopupMenu() { this._init.apply(this, arguments); } PopupMenu.prototype = { __proto__: PopupMenuBase.prototype, /** * _init: * @sourceActor (St.Widget): the actor that owns the popup menu * @arrowAlignment (real): the position of the popup menu arrow relative to * the popup menu. If 0.0, the arrow will be at the left of the popup menu. * If 1.0, the arrow will be at the right of the popup menu, and numbers in * between will put the arrow somewhere between the left and the right (if * the popup menu opens sideways, 0.0 and 1.0 correspond to top and bottom * respectively). Providing a value outside the range [0, 1] will cause * unexpected behaviour. * @arrowSide (St.Side): the arrow side of the menu. See %setArrowSide() for details */ _init: function(sourceActor, arrowAlignment, arrowSide) { PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content'); this._arrowAlignment = arrowAlignment; this._arrowSide = arrowSide; this._boxPointer = new BoxPointer.BoxPointer(arrowSide, { x_fill: true, y_fill: true, x_align: St.Align.START }); this.actor = this._boxPointer.actor; this.actor._delegate = this; this.actor.style_class = 'popup-menu-boxpointer'; this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this._boxWrapper = new Cinnamon.GenericContainer(); this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth)); this._boxWrapper.connect('get-preferred-height', Lang.bind(this, this._boxGetPreferredHeight)); this._boxWrapper.connect('allocate', Lang.bind(this, this._boxAllocate)); this._boxPointer.bin.set_child(this._boxWrapper); this._boxWrapper.add_actor(this.box); this.actor.add_style_class_name('popup-menu'); this.paint_id = 0; this.paint_count = 0; this.animating = false; global.focus_manager.add_group(this.actor); this.actor.reactive = true; }, /** * setArrowSide: * @side (St.Side): The new side of the menu * * Sets the arrow side of the menu. Note that the side is the side * of the source actor, not the menu, e.g. If St.Side.TOP is set, * then the menu will appear below the source actor (the source * actor will be on top of the menu) */ setArrowSide: function(side) { this._arrowSide = side; this._boxPointer.setArrowSide(side); }, _boxGetPreferredWidth: function (actor, forHeight, alloc) { let columnWidths = this.getColumnWidths(); this.setColumnWidths(columnWidths); // Now they will request the right sizes [alloc.min_size, alloc.natural_size] = this.box.get_preferred_width(forHeight); }, _boxGetPreferredHeight: function (actor, forWidth, alloc) { [alloc.min_size, alloc.natural_size] = this.box.get_preferred_height(forWidth); }, _boxAllocate: function (actor, box, flags) { this.box.allocate(box, flags); }, _onKeyPressEvent: function(actor, event) { if (event.get_key_symbol() == Clutter.Escape) { this.close(true); return true; } return false; }, setArrowOrigin: function(origin) { this._boxPointer.setArrowOrigin(origin); }, /** * setSourceAlignment: * @alignment (real): the position of the arrow relative to the source * actor. * * If set to 0.0, the arrow will appear at the left end of the source * actor; 1.0 for right (0.0/1.0 for top/bottom for sideways arrows). The * default value is 0.5, which means the arrow appears at the center of the * source actor. */ setSourceAlignment: function(alignment) { this._boxPointer.setSourceAlignment(alignment); }, /** * open: * @animate (boolean): whether the animate the open effect * * Opens the popup menu */ open: function(animate) { if (this.isOpen) return; Main.popup_rendering_actor = this.actor; this.animating = animate; this.setMaxHeight(); this.isOpen = true; if (global.menuStackLength == undefined) global.menuStackLength = 0; global.menuStackLength += 1; this._boxPointer.setPosition(this.sourceActor, this._arrowAlignment); this.paint_id = this.actor.connect("paint", Lang.bind(this, this.on_paint)); this._boxPointer.show(animate, Lang.bind(this, function () { this.animating = false; })); this.actor.raise_top(); this.emit('open-state-changed', true); }, on_paint: function(actor) { if (this.paint_count < 2 || this.animating) { this.paint_count++; return; } if (this.paint_id > 0) { this.actor.disconnect(this.paint_id); this.paint_id = 0; } this.paint_count = 0; Main.popup_rendering_actor = null; }, /** * setMaxHeight: * * This function is called internally to set the max-height and max-width * properties of the popup menu such that it does not grow to a size larger * than the monitor. Individual popup menus can override this method to * change the max height/width if they really want to. * * Note that setting the max-height won't do any good if the minimum height * of the menu is higher then the screen; it's useful if part of the menu * is scrollable so the minimum height is smaller than the natural height. */ setMaxHeight: function() { let monitor = Main.layoutManager.findMonitorForActor(this.sourceActor) let maxHeight = monitor.height - this.actor.get_theme_node().get_length('-boxpointer-gap'); let panels = Main.panelManager.getPanelsInMonitor(Main.layoutManager.monitors.indexOf(monitor)); for (let panel of panels) maxHeight -= panel.actor.height; this.actor.style = 'max-height: ' + maxHeight / global.ui_scale + 'px; ' + 'max-width: ' + (monitor.width - 20)/ global.ui_scale + 'px;'; // PopupMenus have 10px margins ^ }, /** * close: * @animate (boolean): whether the animate the close effect * * Closes the popup menu. */ close: function(animate) { if (!this.isOpen) return; this.isOpen = false; global.menuStackLength -= 1; for (let i in Main.panelManager.panels) { if (Main.panelManager.panels[i]) Main.panelManager.updatePanelsVisibility(); } if (this._activeMenuItem) this._activeMenuItem.setActive(false); this._boxPointer.hide(animate); this.emit('open-state-changed', false); } }; /** * #PopupSubMenu * @short_description: A submenu that can show and hide * @actor (St.ScrollView): The actor of the submenu. * * A submenu to be included in #PopupMenus/#PopupMenuSections. You usually * don't want to create these manually. Instead you want to create a * #PopupSubMenuMenuItem, which creates a #PopupSubMenu, and shows/hides the * menu when clicked. * * Since submenus are usually used to hide long lists of things, they are * automatically put into a #St.ScrollView such that their height will be limited * by the css max-height property. * * Inherits: PopupMenu.PopupMenuBase */ function PopupSubMenu() { this._init.apply(this, arguments); } PopupSubMenu.prototype = { __proto__: PopupMenuBase.prototype, /** * _init: * @sourceActor (St.Widget): the actor that owns the popup menu * @sourceArrow (St.Icon): (optional) a little arrow object inside the * #PopupSubMenuMenuItem. When the submenu opens, the arrow is rotated by * pi/2 clockwise to denote the status of the submenu. */ _init: function(sourceActor, sourceArrow) { PopupMenuBase.prototype._init.call(this, sourceActor); this._arrow = sourceArrow; this.actor = new St.ScrollView({ style_class: 'popup-sub-menu', hscrollbar_policy: Gtk.PolicyType.NEVER, vscrollbar_policy: Gtk.PolicyType.NEVER }); // StScrollbar plays dirty tricks with events, calling // clutter_set_motion_events_enabled (FALSE) during the scroll; this // confuses our event tracking, so we just turn it off during the // scroll. let vscroll = this.actor.get_vscroll_bar(); vscroll.connect('scroll-start', Lang.bind(this, function() { let topMenu = this._getTopMenu(); if (topMenu) topMenu.passEvents = true; })); vscroll.connect('scroll-stop', Lang.bind(this, function() { let topMenu = this._getTopMenu(); if (topMenu) topMenu.passEvents = false; })); this.actor.add_actor(this.box); this.actor._delegate = this; this.actor.clip_to_allocation = true; this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this.actor.hide(); }, _getTopMenu: function() { let actor = this.actor.get_parent(); while (actor) { if (actor._delegate && actor._delegate instanceof PopupMenu) return actor._delegate; actor = actor.get_parent(); } return null; }, _needsScrollbar: function() { let topMenu = this._getTopMenu(); if(!topMenu) return false; let [topMinHeight, topNaturalHeight] = topMenu.actor.get_preferred_height(-1); let topThemeNode = topMenu.actor.get_theme_node(); let topMaxHeight = topThemeNode.get_max_height(); return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight; }, /** * open: * @animate (boolean): whether the animate the open effect * * Opens the submenu */ open: function(animate) { if (this.isOpen) return; this.isOpen = true; this.actor.show(); let needsScrollbar = this._needsScrollbar(); // St.ScrollView always requests space horizontally for a possible vertical // scrollbar if in AUTOMATIC mode. Doing better would require implementation // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad // when we *don't* need it, so turn off the scrollbar when that's true. // Dynamic changes in whether we need it aren't handled properly. this.actor.vscrollbar_policy = needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; // It looks funny if we animate with a scrollbar (at what point is // the scrollbar added?) so just skip that case animate = animate && !needsScrollbar let targetAngle = this.actor.text_direction == Clutter.TextDirection.RTL ? -90 : 90; if (animate) { let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1); this.actor.height = 0; if (this._arrow) this.actor._arrowRotation = this._arrow.rotation_angle_z; else this.actor._arrowRotation = targetAngle; Tweener.addTween(this.actor, { _arrowRotation: targetAngle, height: naturalHeight, time: 0.25, onUpdateScope: this, onUpdate: function() { if (this._arrow) this._arrow.rotation_angle_z = this.actor._arrowRotation; }, onCompleteScope: this, onComplete: function() { this.actor.set_height(-1); this.emit('open-state-changed', true); } }); } else { if (this._arrow) this._arrow.rotation_angle_z = targetAngle; this.emit('open-state-changed', true); } }, /** * close: * @animate (boolean): whether the animate the close effect * * Closes the submenu */ close: function(animate) { if (!this.isOpen) return; this.isOpen = false; if (this._activeMenuItem) this._activeMenuItem.setActive(false); animate = animate && !this._needsScrollbar(); if (animate) { if (this._arrow) this.actor._arrowRotation = this._arrow.rotation_angle_z; Tweener.addTween(this.actor, { _arrowRotation: 0, height: 0, time: 0.25, onCompleteScope: this, onComplete: function() { this.actor.hide(); this.actor.set_height(-1); this.emit('open-state-changed', false); }, onUpdateScope: this, onUpdate: function() { if (this._arrow) this._arrow.rotation_angle_z = this.actor._arrowRotation; } }); } else { if (this._arrow) this._arrow.rotation_angle_z = 0; this.actor.hide(); this.isOpen = false; this.emit('open-state-changed', false); } }, _onKeyPressEvent: function(actor, event) { // Move focus back to parent menu if the user types Left. if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) { this.close(true); this.sourceActor._delegate.setActive(true); return true; } return false; } }; /** * #PopupMenuSection: * @short_description: A section of a #PopupMenu that is transparent to user * * A section of a PopupMenu which is handled like a submenu (you can add and * remove items, you can destroy it, you can add it to another menu), but is * completely transparent to the user. This is helpful for grouping things * together so that you can manage them in bulk. A common use case might be to * let an object inherit a #PopupMenuSection and then add the whole object to a * popup menu. * * Note that you cannot close a #PopupMenuSection. * * Inherits: PopupMenu.PopupMenuBase */ function PopupMenuSection() { this._init.apply(this, arguments); } PopupMenuSection.prototype = { __proto__: PopupMenuBase.prototype, _init: function() { PopupMenuBase.prototype._init.call(this); this.actor = this.box; this.actor._delegate = this; this.isOpen = true; }, // deliberately ignore any attempt to open() or close() open: function(animate) { }, close: function() { }, } function PopupSubMenuMenuItem() { this._init.apply(this, arguments); } PopupSubMenuMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function(text) { PopupBaseMenuItem.prototype._init.call(this); this.actor.add_style_class_name('popup-submenu-menu-item'); this.label = new St.Label({ text: text, y_expand: true, y_align: Clutter.ActorAlign.CENTER }); this.addActor(this.label); this.actor.label_actor = this.label; this._triangleBin = new St.Bin({ x_align: St.Align.END }); this.addActor(this._triangleBin, { expand: true, span: -1, align: St.Align.END }); this._triangle = arrowIcon(St.Side.RIGHT); this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); this._triangleBin.child = this._triangle; this.menu = new PopupSubMenu(this.actor, this._triangle); this.menu.connect('open-state-changed', Lang.bind(this, this._subMenuOpenStateChanged)); }, _subMenuOpenStateChanged: function(menu, open) { this.actor.change_style_pseudo_class('open', open); }, destroy: function() { this.menu.destroy(); PopupBaseMenuItem.prototype.destroy.call(this); }, _onKeyPressEvent: function(actor, event) { let symbol = event.get_key_symbol(); if (symbol == Clutter.KEY_Right) { this.menu.open(true); this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false); return true; } else if (symbol == Clutter.KEY_Left && this.menu.isOpen) { this.menu.close(); return true; } return PopupBaseMenuItem.prototype._onKeyPressEvent.call(this, actor, event); }, activate: function(event) { this.menu.open(true); }, _onButtonReleaseEvent: function(actor) { this.menu.toggle(); } }; function PopupComboMenu() { this._init.apply(this, arguments); } PopupComboMenu.prototype = { __proto__: PopupMenuBase.prototype, _init: function(sourceActor) { PopupMenuBase.prototype._init.call(this, sourceActor, 'popup-combo-menu'); this.actor = this.box; this.actor._delegate = this; this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); this._activeItemPos = -1; global.focus_manager.add_group(this.actor); }, _onKeyPressEvent: function(actor, event) { if (event.get_key_symbol() == Clutter.Escape) { this.close(true); return true; } return false; }, _onKeyFocusIn: function(actor) { let items = this._getMenuItems(); let activeItem = items[this._activeItemPos]; activeItem.actor.grab_key_focus(); }, open: function() { if (this.isOpen) return; this.isOpen = true; let activeItem = this._getMenuItems()[this._activeItemPos]; let [sourceX, sourceY] = this.sourceActor.get_transformed_position(); this.actor.set_position(Math.round(sourceX), Math.round(sourceY - activeItem.actor.y)); this.actor.raise_top(); this.actor.opacity = 0; this.actor.show(); Tweener.addTween(this.actor, { opacity: 255, transition: 'linear', time: BoxPointer.POPUP_ANIMATION_TIME }); this.savedFocusActor = global.stage.get_key_focus(); global.stage.set_key_focus(this.actor); this.emit('open-state-changed', true); }, close: function() { if (!this.isOpen) return; this.isOpen = false; Tweener.addTween(this.actor, { opacity: 0, transition: 'linear', time: BoxPointer.POPUP_ANIMATION_TIME, onComplete: Lang.bind(this, function() { this.actor.hide(); }) }); this.emit('open-state-changed', false); global.stage.set_key_focus(this.savedFocusActor); }, setActiveItem: function(position) { this._activeItemPos = position; }, setItemVisible: function(position, visible) { if (!visible && position == this._activeItemPos) { log('Trying to hide the active menu item.'); return; } this._getMenuItems()[position].actor.visible = visible; }, getItemVisible: function(position) { return this._getMenuItems()[position].actor.visible; } }; function PopupComboBoxMenuItem() { this._init.apply(this, arguments); } PopupComboBoxMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, _init: function (params) { PopupBaseMenuItem.prototype._init.call(this, params); this._itemBox = new Cinnamon.Stack(); this.actor.accessible_role = Atk.Role.COMBO_BOX; this.addActor(this._itemBox); let expander = new St.Label({ text: '\u2304' }); this.addActor(expander, { align: St.Align.END, span: -1 }); this._menu = new PopupComboMenu(this.actor); Main.uiGroup.add_actor(this._menu.actor); this._menu.actor.hide(); if (params.style_class) this._menu.actor.add_style_class_name(params.style_class); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this._activeItemPos = -1; this._items = []; }, _getTopMenu: function() { let actor = this.actor.get_parent(); while (actor) { if (actor._delegate && (actor._delegate instanceof PopupMenu || actor._delegate instanceof PopupComboMenu)) return actor._delegate; actor = actor.get_parent(); } return null; }, _onScrollEvent: function(actor, event) { if (this._activeItemPos == -1) return; let position = this._activeItemPos; let direction = event.get_scroll_direction(); if (direction == Clutter.ScrollDirection.DOWN) { while (position < this._items.length - 1) { position++; if (this._menu.getItemVisible(position)) break; } } else if (direction == Clutter.ScrollDirection.UP) { while (position > 0) { position--; if (this._menu.getItemVisible(position)) break; } } if (position == this._activeItemPos) return; this.setActiveItem(position); this.emit('active-item-changed', position); }, activate: function(event) { let topMenu = this._getTopMenu(); if (!topMenu) return; topMenu.addChildMenu(this._menu); this._menu.toggle(); }, addMenuItem: function(menuItem, position) { if (position === undefined) position = this._menu.numMenuItems; this._menu.addMenuItem(menuItem, position); _ensureStyle(this._menu.actor); let item = new St.BoxLayout({ style_class: 'popup-combobox-item' }); let children = menuItem.actor.get_children(); for (let i = 0; i < children.length; i++) { let clone = new Clutter.Clone({ source: children[i] }); item.add(clone, { y_fill: false }); } let oldItem = this._items[position]; if (oldItem) this._itemBox.remove_actor(oldItem); this._items[position] = item; this._itemBox.add_actor(item); menuItem.connect('activate', Lang.bind(this, this._itemActivated, position)); }, checkAccessibleLabel: function() { let activeItem = this._menu.getActiveItem(); this.actor.label_actor = activeItem.label; }, setActiveItem: function(position) { let item = this._items[position]; if (!item) return; if (this._activeItemPos == position) return; this._menu.setActiveItem(position); this._activeItemPos = position; for (let i = 0; i < this._items.length; i++) this._items[i].visible = (i == this._activeItemPos); this.checkAccessibleLabel(); }, setItemVisible: function(position, visible) { this._menu.setItemVisible(position, visible); }, _itemActivated: function(menuItem, event, position) { this.setActiveItem(position); this.emit('active-item-changed', position); } }; /** * #PopupMenuFactory: * @short_description: A class to build a cinnamon menu using some abstract menu items. * * This class can build a cinnamon menu, using the instances of a heir of the * PopupMenuAbstractItem class. Please see the description of the PopupMenuAbstractItem * class to more details. To initialize the construction you need to provide the root * instance of your abstract menu items. */ function PopupMenuFactory() { this._init.apply(this, arguments); } PopupMenuFactory.prototype = { _init: function() { this._menuLikend = new Array(); }, _createShellItem: function(factoryItem, launcher, orientation) { // Decide whether it's a submenu or not let shellItem = null; let item_type = factoryItem.getFactoryType(); if (item_type == FactoryClassTypes.RootMenuClass) shellItem = new PopupMenu(launcher.actor, 0.0, orientation, 0); if (item_type == FactoryClassTypes.SubMenuMenuItemClass) shellItem = new PopupSubMenuMenuItem("FIXME"); else if (item_type == FactoryClassTypes.MenuSectionMenuItemClass) shellItem = new PopupMenuSection(); else if (item_type == FactoryClassTypes.SeparatorMenuItemClass) shellItem = new PopupSeparatorMenuItem(''); else if (item_type == FactoryClassTypes.MenuItemClass) shellItem = new PopupIndicatorMenuItem("FIXME"); return shellItem; }, getShellMenu: function(factoryMenu) { let index = this._menuLikend.indexOf(factoryMenu); if (index != -1) { return factoryMenu.getShellItem(); } return null; }, buildShellMenu: function(client, launcher, orientation) { let factoryMenu = client.getRoot(); if (!(factoryMenu instanceof PopupMenuAbstractItem)) { throw new Error("MenuFactory: can't construct an instance of \ PopupMenu using a non instance of the class PopupMenuAbstractItem"); } if (factoryMenu.shellItem) return factoryMenu.shellItem; // The shell menu let shellItem = this._createShellItem(factoryMenu, launcher, orientation); this._attachToMenu(shellItem, factoryMenu); return shellItem; }, // This will attach the root factoryItem to an already existing menu that will be used as the root menu. // it will also connect the factoryItem to be automatically destroyed when the menu dies. _attachToMenu: function(shellItem, factoryItem) { // Cleanup: remove existing childs (just in case) shellItem.removeAll(); // Fill the menu for the first time factoryItem.getChildren().forEach(function(child) { shellItem.addMenuItem(this._createItem(child)); }, this); factoryItem.setShellItem(shellItem, { 'child-added' : Lang.bind(this, this._onChildAdded), 'child-moved' : Lang.bind(this, this._onChildMoved) }); this._menuLikend.push(factoryItem); factoryItem.connectAndRemoveOnDestroy({ 'destroy' : Lang.bind(this, this._onDestroyMainMenu) }); }, _onDestroyMainMenu: function(factoryItem) { let index = this._menuLikend.indexOf(factoryItem); if (index != -1) { this._menuLikend.splice(index, 1); } }, _createItem: function(factoryItem) { // Don't allow to override previusly preasigned items, destroy the shell item first. factoryItem.destroyShellItem(); let shellItem = this._createShellItem(factoryItem); // Initially create children on idle, to not stop cinnamon mainloop. Mainloop.idle_add(Lang.bind(this, this._createChildrens, factoryItem)); // Now, connect various events factoryItem.setShellItem(shellItem, { 'type-changed': Lang.bind(this, this._onTypeChanged), 'child-added': Lang.bind(this, this._onChildAdded), 'child-moved': Lang.bind(this, this._onChildMoved) }); return shellItem; }, _createChildrens: function(factoryItem) { if (factoryItem) { let shellItem = factoryItem.getShellItem(); if (shellItem instanceof PopupSubMenuMenuItem) { let children = factoryItem.getChildren(); for (let i = 0; i < children.length; ++i) { let ch_item = this._createItem(children[i]); shellItem.menu.addMenuItem(ch_item); } } else if (shellItem instanceof PopupMenuSection) { let children = factoryItem.getChildren(); for (let i = 0; i < children.length; ++i) { let ch_item = this._createItem(children[i]); shellItem.addMenuItem(ch_item); } } } }, _onChildAdded: function(factoryItem, child, position) { let shellItem = factoryItem.getShellItem(); if (shellItem) { if (shellItem instanceof PopupSubMenuMenuItem) { shellItem.menu.addMenuItem(this._createItem(child), position, "factor"); } else if ((shellItem instanceof PopupMenuSection) || (shellItem instanceof PopupMenu)) { shellItem.addMenuItem(this._createItem(child), position); } else { global.logWarning("Tried to add a child to non-submenu item. Better recreate it as whole"); this._onTypeChanged(factoryItem); } } else { global.logWarning("Tried to add a child shell item to non existing shell item."); } }, _onChildMoved: function(factoryItem, child, oldpos, newpos) { let shellItem = factoryItem.getShellItem(); if (shellItem) { if (shellItem instanceof PopupSubMenuMenuItem) { this._moveItemInMenu(shellItem.menu, child, newpos); } else if ((shellItem instanceof PopupMenuSection) || (shellItem instanceof PopupMenu)) { this._moveItemInMenu(shellItem, child, newpos); } else { global.logWarning("Tried to move a child in non-submenu item. Better recreate it as whole"); this._onTypeChanged(factoryItem); } } else { global.logWarning("Tried to move a child shell item in non existing shell item."); } }, // FIXME: If this function it is applied, this mean that our old shell Item // is not valid right now, so we can destroy it with all the obsolete submenu // structure and then create again for the new factoryItems source. Anyway // there are a lot of possible scenarios when this was called, sure we are // missing some of them. _onTypeChanged: function(factoryItem) { let shellItem = factoryItem.getShellItem(); let factoryItemParent = factoryItem.getParent(); let parentMenu = null; if (factoryItemParent) { let shellItemParent = factoryItemParent.getShellItem(); if (shellItemParent instanceof PopupMenuSection) parentMenu = shellItemParent; else parentMenu = shellItemParent.menu; } // First, we need to find our old position let pos = -1; if ((parentMenu)&&(shellItem)) { let family = parentMenu._getMenuItems(); for (let i = 0; i < family.length; ++i) { if (family[i] == shellItem) pos = i; } } // if not insert the item in first position. if (pos < 0) pos = 0; // Now destroy our old self factoryItem.destroyShellItem(); if (parentMenu) { // Add our new self let newShellItem = this._createItem(factoryItem); parentMenu.addMenuItem(newShellItem, pos); } }, // FIXME: This is a HACK. We're really getting into the internals of the PopupMenu implementation. // First, find our wrapper. Children tend to lie. We do not trust the old positioning. // Will be better add this function inside the PopupMenuBase class? _moveItemInMenu: function(menu, factoryItem, newpos) { let shellItem = factoryItem.getShellItem(); if (shellItem) { let family = menu._getMenuItems(); for (let i = 0; i < family.length; ++i) { if (family[i] == shellItem) { // Now, remove it menu.box.remove_child(shellItem.actor); // Add it again somewhere else if (newpos < family.length && family[newpos] != shellItem) menu.box.insert_child_below(shellItem.actor, family[newpos].actor); else menu.box.add(shellItem.actor); // Skip the rest break; } } } } }; /* Basic implementation of a menu manager. * Call addMenu to add menus */ function PopupMenuManager(owner) { this._init(owner); } PopupMenuManager.prototype = { _init: function(owner) { this._owner = owner; this.grabbed = false; this._eventCaptureId = 0; this._enterEventId = 0; this._leaveEventId = 0; this._keyFocusNotifyId = 0; this._activeMenu = null; this._menus = []; this._menuStack = []; this._preGrabInputMode = null; this._grabbedFromKeynav = false; this._signals = new SignalManager.SignalManager(this); }, addMenu: function(menu, position) { this._signals.connect(menu, 'open-state-changed', this._onMenuOpenState); this._signals.connect(menu, 'child-menu-added', this._onChildMenuAdded); this._signals.connect(menu, 'child-menu-removed', this._onChildMenuRemoved); this._signals.connect(menu, 'destroy', this._onMenuDestroy); let source = menu.sourceActor; if (source) { this._signals.connect(source, 'enter-event', function() { this._onMenuSourceEnter(menu); }); this._signals.connect(source, 'key-focus-in', function() { this._onMenuSourceEnter(menu); }); } if (position == undefined) this._menus.push(menu); else this._menus.splice(position, 0, menu); }, removeMenu: function(menu) { if (menu == this._activeMenu) this._closeMenu(); let position = this._menus.indexOf(menu); if (position == -1) // not a menu we manage return; this._signals.disconnect(null, menu); if (menu.sourceActor) this._signals.disconnect(menu.sourceActor); this._menus.splice(position, 1); }, _grab: function() { if (!Main.pushModal(this._owner.actor)) { return; } this._signals.connect(global.stage, 'captured-event', this._onEventCapture); // captured-event doesn't see enter/leave events this._signals.connect(global.stage, 'enter-event', this._onEventCapture); this._signals.connect(global.stage, 'leave-event', this._onEventCapture); this._signals.connect(global.stage, 'notify::key-focus', this._onKeyFocusChanged); this.grabbed = true; }, _ungrab: function() { if (!this.grabbed) { return; } this._signals.disconnect(null, global.stage); this.grabbed = false; Main.popModal(this._owner.actor); }, _onMenuOpenState: function(menu, open) { if (open) { if (this._activeMenu && this._activeMenu.isChildMenu(menu)) { this._menuStack.push(this._activeMenu); menu.actor.grab_key_focus(); } this._activeMenu = menu; } else { if (this._menuStack.length > 0) { this._activeMenu = this._menuStack.pop(); if (menu.sourceActor) menu.sourceActor.grab_key_focus(); this._didPop = true; } } // Check what the focus was before calling pushModal/popModal let focus = global.stage.key_focus; let hadFocus = focus && this._activeMenuContains(focus); if (open) { if (!this.grabbed) { this._preGrabInputMode = global.stage_input_mode; this._grabbedFromKeynav = hadFocus; this._grab(); } if (hadFocus) focus.grab_key_focus(); else menu.actor.grab_key_focus(); } else if (menu == this._activeMenu) { if (this.grabbed) this._ungrab(); this._activeMenu = null; if (this._grabbedFromKeynav) { if (this._preGrabInputMode == Cinnamon.StageInputMode.FOCUSED) global.stage_input_mode = Cinnamon.StageInputMode.FOCUSED; if (hadFocus && menu.sourceActor) menu.sourceActor.grab_key_focus(); else if (focus) focus.grab_key_focus(); } } }, _onChildMenuAdded: function(menu, childMenu) { this.addMenu(childMenu); }, _onChildMenuRemoved: function(menu, childMenu) { this.removeMenu(childMenu); }, // change the currently-open menu without dropping grab _changeMenu: function(newMenu) { if (this._activeMenu) { // _onOpenMenuState will drop the grab if it sees // this._activeMenu being closed; so clear _activeMenu // before closing it to keep that from happening let oldMenu = this._activeMenu; this._activeMenu = null; for (let i = this._menuStack.length - 1; i >= 0; i--) this._menuStack[i].close(false); oldMenu.close(false); newMenu.open(false); } else newMenu.open(true); }, _onMenuSourceEnter: function(menu) { if (!this.grabbed || menu == this._activeMenu) return false; if (this._activeMenu && this._activeMenu.isChildMenu(menu)) return false; if (this._menuStack.indexOf(menu) != -1) return false; if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu)) return false; this._changeMenu(menu); return false; }, _onKeyFocusChanged: function() { if (!this.grabbed || !this._activeMenu || DND.isDragging()) return; let focus = global.stage.key_focus; if (focus) { if (this._activeMenuContains(focus)) return; if (this._menuStack.length > 0) return; if (focus._delegate && focus._delegate.menu && this._menus.indexOf(focus._delegate.menu) != -1) return; } this._closeMenu(); }, _onMenuDestroy: function(menu) { this.removeMenu(menu); }, _activeMenuContains: function(actor) { return this._activeMenu != null && (this._activeMenu.actor.contains(actor) || (this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(actor))); }, _eventIsOnActiveMenu: function(event) { return this._activeMenuContains(event.get_source()); }, _shouldBlockEvent: function(event) { let src = event.get_source(); if (this._activeMenu != null && this._activeMenu.actor.contains(src)) return false; return (this._menus.find(x => x.sourceActor && !x.blockSourceEvents && x.sourceActor.contains(src)) === undefined); }, _onEventCapture: function(actor, event) { if (!this.grabbed) return false; if (this._owner.menuEventFilter && this._owner.menuEventFilter(event)) return true; if (this._activeMenu != null && this._activeMenu.passEvents) return false; if (this._didPop) { this._didPop = false; return true; } let activeMenuContains = this._eventIsOnActiveMenu(event); let eventType = event.type(); if (eventType == Clutter.EventType.BUTTON_RELEASE) { if (activeMenuContains) { return false; } else { this._closeMenu(); return true; } } else if (eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains) { this._closeMenu(); return true; } else if (!this._shouldBlockEvent(event)) { return false; } return true; }, _closeMenu: function() { if (this._activeMenu != null) this._activeMenu.close(true); } }; Cinnamon-2.8.6/js/ui/applet.js0000664000175000017500000006044612625666430015115 0ustar fabiofabioconst St = imports.gi.St; const Lang = imports.lang; const Tooltips = imports.ui.tooltips; const PopupMenu = imports.ui.popupMenu; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Main = imports.ui.main; const DND = imports.ui.dnd; const Clutter = imports.gi.Clutter; const AppletManager = imports.ui.appletManager; const Gtk = imports.gi.Gtk; const Util = imports.misc.util; const Pango = imports.gi.Pango; const Mainloop = imports.mainloop; const Flashspot = imports.ui.flashspot; const ModalDialog = imports.ui.modalDialog; const Signals = imports.signals; const COLOR_ICON_HEIGHT_FACTOR = .875; // Panel height factor for normal color icons const PANEL_FONT_DEFAULT_HEIGHT = 11.5; // px const PANEL_SYMBOLIC_ICON_DEFAULT_HEIGHT = 1.14 * PANEL_FONT_DEFAULT_HEIGHT; // ems conversion const DEFAULT_PANEL_HEIGHT = 25; const FALLBACK_ICON_HEIGHT = 22; /** * #MenuItem * @short_description: Deprecated. Use #PopupMenu.PopupIconMenuItem instead. */ function MenuItem(label, icon, callback) { this.__proto__ = PopupMenu.PopupIconMenuItem.prototype; PopupMenu.PopupIconMenuItem.prototype._init.call(this, label, icon, St.IconType.SYMBOLIC); this.connect('activate', callback); } /** * #AppletContextMenu * @short_description: Applet right-click menu * * A context menu (right-click menu) to be used by an applet * * Inherits: PopupMenu.PopupMenu */ function AppletContextMenu(launcher, orientation) { this._init(launcher, orientation); } AppletContextMenu.prototype = { __proto__: PopupMenu.PopupMenu.prototype, /** * _init: * @launcher (Applet.Applet): The applet that contains the context menu * @orientation (St.Side): The orientation of the applet * * Constructor function */ _init: function(launcher, orientation) { PopupMenu.PopupMenu.prototype._init.call(this, launcher.actor, 0.0, orientation, 0); Main.uiGroup.add_actor(this.actor); this.actor.hide(); launcher.connect("orientation-changed", Lang.bind(this, function(a, orientation) { this.setArrowSide(orientation); })) } }; /** * #AppletPopupMenu: * @short_description: Applet left-click menu * * A popupmenu menu (left-click menu) to be used by an applet * * Inherits: PopupMenu.PopupMenu */ function AppletPopupMenu(launcher, orientation) { this._init(launcher, orientation); } AppletPopupMenu.prototype = { __proto__: PopupMenu.PopupMenu.prototype, /** * _init: * @launcher (Applet.Applet): The applet that contains the context menu * @orientation (St.Side): The orientation of the applet * * Constructor function */ _init: function(launcher, orientation) { PopupMenu.PopupMenu.prototype._init.call(this, launcher.actor, 0.0, orientation, 0); Main.uiGroup.add_actor(this.actor); this.actor.hide(); this.launcher = launcher; if (launcher instanceof Applet) launcher.connect("orientation-changed", Lang.bind(this, this._onOrientationChanged)); else if (launcher._applet) launcher._applet.connect("orientation-changed", Lang.bind(this, this._onOrientationChanged)); }, _onOrientationChanged: function(a, orientation) { this.setArrowSide(orientation); } } /** * #Applet * @short_description: Base applet class * * @actor (St.BoxLayout): Actor of the applet * @instance_id (int): Instance id of the applet * @_uuid (string): UUID of the applet. This is set by appletManager *after* * the applet is loaded. * @_panelLocation (St.BoxLayout): Panel sector containing the applet. This is * set by appletManager *after* the applet is loaded. * @panel (Panel.Panel): The panel object containing the applet. This is set by * appletManager *after* the applet is loaded. * @_meta (JSON): The metadata of the applet. This is set by appletManager * *after* the applet is loaded. * @_order (int): The order of the applet within a panel location This is set * by appletManager *after* the applet is loaded. * @_draggable (Dnd._Draggable): The draggable object of the applet * @_scaleMode (boolean): Whether the applet scales according to the panel size * @_applet_tooltip (Tooltips.PanelItemTooltip): The tooltip of the applet * @_menuManager (PopupMenu.PopupMenuManager): The menu manager of the applet * @_applet_context_menu (Applet.AppletContextMenu): The context menu of the applet * @_applet_tooltip_text (string): Text of the tooltip * * Base applet class that other applets can inherit */ function Applet(orientation, panelHeight, instance_id) { this._init(orientation, panelHeight, instance_id); } Applet.prototype = { /** * _init: * @orientation (St.Side): orientation of the applet; Orientation of panel containing the actor * @panelHeight (int): height of the panel containing the applet * @instance_id (int): instance id of the applet */ _init: function(orientation, panel_height, instance_id) { this.actor = new St.BoxLayout({ style_class: 'applet-box', reactive: true, track_hover: true }); this._applet_tooltip = new Tooltips.PanelItemTooltip(this, "", orientation); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent)); this._menuManager = new PopupMenu.PopupMenuManager(this); this._applet_context_menu = new AppletContextMenu(this, orientation); this._menuManager.addMenu(this._applet_context_menu); this.actor._applet = this; // Backlink to get the applet from its actor (handy when we want to know stuff about a particular applet within the panel) this.actor._delegate = this; this._order = 0; // Defined in gsettings, this is the order of the applet within a panel location. This value is set by Cinnamon when loading/listening_to gsettings. this._newOrder = null; // Used when moving an applet this._panelLocation = null; // Backlink to the panel location our applet is in, set by Cinnamon. this._newPanelLocation = null; // Used when moving an applet this._applet_enabled = true; // Whether the applet is enabled or not (if not it hides in the panel as if it wasn't there) this._panelHeight = panel_height ? panel_height : 25; this.instance_id = instance_id; // Needed by appletSettings this._uuid = null; // Defined in gsettings, set by Cinnamon. this._hook = null; // Defined in metadata.json, set by appletManager this._meta = null; // set by appletManager this._dragging = false; this._draggable = DND.makeDraggable(this.actor); this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); try { this._scaleMode = AppletManager.enabledAppletDefinitions.idMap[instance_id].panel.scaleMode; } catch (e) { // Sometimes applets are naughty and don't pass us our instance_id. In that case, we just find the first non-empty panel and pretend we are on it. for (let i in Main.panelManager.panels) { this._scaleMode = true; if (Main.panelManager.panels[i]) { this._scaleMode = Main.panelManager.panels[i].scaleMode; } } } this._applet_tooltip_text = ""; this.context_menu_item_remove = null; this.context_menu_separator = null; this._setAppletReactivity(); this._panelEditModeChangedId = global.settings.connect('changed::panel-edit-mode', Lang.bind(this, function() { this._setAppletReactivity(); })); }, /* FIXME: This makes no sense - inhibit flag should = panel edit mode, right? * Needs fixed in dnd.js also, it expects this backwards logic right now */ _setAppletReactivity: function() { this._draggable.inhibit = !global.settings.get_boolean('panel-edit-mode'); }, _onDragBegin: function() { this._dragging = true; this._applet_tooltip.hide(); this._applet_tooltip.preventShow = true; }, _onDragEnd: function() { this._dragging = false; this._applet_tooltip.preventShow = false; }, _onDragCancelled: function() { this._dragging = false; this._applet_tooltip.preventShow = false; }, getDragActor: function() { let clone = new Clutter.Clone({ source: this.actor }); clone.width = this.actor.width; clone.height = this.actor.height; return clone; }, // Returns the original actor that should align with the actor // we show as the item is being dragged. getDragActorSource: function() { return this.actor; }, _onButtonPressEvent: function (actor, event) { if (this._applet_enabled) { if (event.get_button() == 1) { if (!this._draggable.inhibit) { return false; } else { if (this._applet_context_menu.isOpen) { this._applet_context_menu.toggle(); } this.on_applet_clicked(event); } } if (event.get_button()==3){ if (this._applet_context_menu._getMenuItems().length > 0) { this._applet_context_menu.toggle(); } } } return true; }, /** * set_applet_tooltip: * @text (string): the tooltip text to be set * * Sets the tooltip of the applet */ set_applet_tooltip: function (text) { if (text != this._applet_tooltip_text) { this._applet_tooltip_text = text; this._applet_tooltip.set_text(text); } }, /** * set_applet_enabled: * @enabled (boolean): whether this applet is enabled or not * * Sets whether the applet is enabled or not. A disabled applet sets its * padding to 0px and doesn't react to clicks */ set_applet_enabled: function (enabled) { if (enabled != this._applet_enabled) { this._applet_enabled = enabled; this.actor.visible = enabled; } }, /** * on_applet_clicked: * @event (Clutter.Event): the event object * * This function is called when the applet is clicked. * * This is meant to be overriden in individual applets. */ on_applet_clicked: function(event) { // Implemented by Applets }, /** * on_applet_instances_changed: * * This function is called when an applet *of the same uuid* is added or * removed from the panels. It is intended to assist in delegation of * responsibilities between duplicate applet instances. * * This is meant to be overridden in individual applets */ on_applet_instances_changed: function() { }, on_applet_added_to_panel_internal: function(userEnabled) { if (userEnabled) { Mainloop.timeout_add(300, Lang.bind(this, function() { let [x, y] = this.actor.get_transformed_position(); let [w, h] = this.actor.get_transformed_size(); let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: w, height: h}); flashspot.fire(); return false; })); } this.on_applet_added_to_panel(userEnabled); Main.AppletManager.callAppletInstancesChanged(this._uuid); }, /** * on_applet_added_to_panel: * * This function is called by appletManager when the applet is added to the panel. * * This is meant to be overridden in individual applets. */ on_applet_added_to_panel: function(userEnabled) { }, /** * on_applet_removed_from_panel: * * This function is called by appletManager when the applet is removed from the panel. * * This is meant to be overridden in individual applets. */ on_applet_removed_from_panel: function() { }, // should only be called by appletManager _onAppletRemovedFromPanel: function() { global.settings.disconnect(this._panelEditModeChangedId); this.on_applet_removed_from_panel(); Main.AppletManager.callAppletInstancesChanged(this._uuid); }, /** * setOrientation: * @orientation (St.Side): the orientation * * Sets the orientation of the applet. * * This function should only be called by appletManager */ setOrientation: function (orientation) { this.on_orientation_changed(orientation); this.emit("orientation-changed", orientation); this.finalizeContextMenu(); }, /** * on_orientation_changed: * @orientation (St.Side): new orientation of the applet * * This function is called when the applet is changes orientation. * * This is meant to be overridden in individual applets. */ on_orientation_changed: function(orientation) { // Implemented by Applets }, /** * setPanelHeight: * @panelHeight (int): panelHeight * * Sets the panel height property of the applet. */ setPanelHeight: function (panel_height) { if (panel_height && panel_height > 0) { this._panelHeight = panel_height; } this._scaleMode = AppletManager.enabledAppletDefinitions.idMap[this.instance_id].panel.scaleMode; this.on_panel_height_changed(); }, /** * on_panel_height_changed: * * This function is called when the panel containing the applet changes height * * This is meant to be overridden in individual applets. */ on_panel_height_changed: function() { // Implemented byApplets }, finalizeContextMenu: function () { // Add default context menus if we're in panel edit mode, ensure their removal if we're not let items = this._applet_context_menu._getMenuItems(); if (this.context_menu_item_remove == null) { this.context_menu_item_remove = new PopupMenu.PopupIconMenuItem(_("Remove this applet"), "edit-delete", St.IconType.SYMBOLIC); this.context_menu_item_remove.connect('activate', Lang.bind(this, function() { AppletManager._removeAppletFromPanel(this._uuid, this.instance_id); })); } if (this.context_menu_item_about == null) { this.context_menu_item_about = new PopupMenu.PopupIconMenuItem(_("About..."), "dialog-question", St.IconType.SYMBOLIC); this.context_menu_item_about.connect('activate', Lang.bind(this, this.openAbout)); } if (this.context_menu_separator == null) { this.context_menu_separator = new PopupMenu.PopupSeparatorMenuItem(); } if (this._applet_context_menu._getMenuItems().length > 0) { this._applet_context_menu.addMenuItem(this.context_menu_separator); } if (items.indexOf(this.context_menu_item_about) == -1) { this._applet_context_menu.addMenuItem(this.context_menu_item_about); } if (!this._meta["hide-configuration"] && GLib.file_test(this._meta["path"] + "/settings-schema.json", GLib.FileTest.EXISTS)) { if (this.context_menu_item_configure == null) { this.context_menu_item_configure = new PopupMenu.PopupIconMenuItem(_("Configure..."), "system-run", St.IconType.SYMBOLIC); this.context_menu_item_configure.connect('activate', Lang.bind(this, function() { Util.spawnCommandLine("cinnamon-settings applets " + this._uuid + " " + this.instance_id); })); } if (items.indexOf(this.context_menu_item_configure) == -1) { this._applet_context_menu.addMenuItem(this.context_menu_item_configure); } } if (items.indexOf(this.context_menu_item_remove) == -1) { this._applet_context_menu.addMenuItem(this.context_menu_item_remove); } }, openAbout: function() { new ModalDialog.SpicesAboutDialog(this._meta, "applets"); }, }; Signals.addSignalMethods(Applet.prototype); /** * #IconApplet: * @short_description: Applet with icon * * @_applet_icon (St.Icon): Actor of the icon * * Applet that contains an icon * * Inherits: Applet.Applet */ function IconApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } IconApplet.prototype = { __proto__: Applet.prototype, /** * _init: * @orientation (St.Side): orientation of the applet; Orientation of panel containing the actor * @panelHeight (int): height of the panel containing the applet * @instance_id (int): instance id of the applet */ _init: function(orientation, panel_height, instance_id) { Applet.prototype._init.call(this, orientation, panel_height, instance_id); this._applet_icon_box = new St.Bin(); this.actor.add(this._applet_icon_box, {a_align: St.Align.MIDDLE, y_fill: false }); }, /** * set_applet_icon_name: * @icon_name (string): Name of the icon * * Sets the icon of the applet to @icon_name. * * The icon will be full color */ set_applet_icon_name: function (icon_name) { this._ensureIcon() this._applet_icon.set_icon_name(icon_name); this._applet_icon.set_icon_type(St.IconType.FULLCOLOR); this._setStyle(); }, /** * set_applet_icon_symbolic_name: * @icon_name (string): Name of the icon * * Sets the icon of the applet to @icon_name. * * The icon will be symbolic */ set_applet_icon_symbolic_name: function (icon_name) { this._ensureIcon() this._applet_icon.set_icon_name(icon_name); this._applet_icon.set_icon_type(St.IconType.SYMBOLIC); this._setStyle(); }, /** * set_applet_icon_path: * @icon_path (string): path of the icon * * Sets the icon of the applet to the image file at @icon_path * * The icon will be full color */ set_applet_icon_path: function (icon_path) { this._ensureIcon() try { let file = Gio.file_new_for_path(icon_path); this._applet_icon.set_gicon(new Gio.FileIcon({ file: file })); this._applet_icon.set_icon_type(St.IconType.FULLCOLOR); this._setStyle(); } catch (e) { global.log(e); } }, /** * set_applet_icon_symbolic_path: * @icon_path (string): path of the icon * * Sets the icon of the applet to the image file at @icon_path * * The icon will be symbolic */ set_applet_icon_symbolic_path: function(icon_path) { this._ensureIcon() try { let file = Gio.file_new_for_path(icon_path); this._applet_icon.set_gicon(new Gio.FileIcon({ file: file })); this._applet_icon.set_icon_type(St.IconType.SYMBOLIC); this._setStyle(); } catch (e) { global.log(e); } }, _ensureIcon: function() { if (!this._applet_icon) this._applet_icon = new St.Icon({ reactive: true, track_hover: true, style_class: 'applet-icon' }); this._applet_icon_box.set_child(this._applet_icon); }, _setStyle: function() { switch (this._applet_icon.get_icon_type()) { case St.IconType.FULLCOLOR: this._applet_icon.set_icon_size(this._scaleMode ? this._panelHeight * COLOR_ICON_HEIGHT_FACTOR / global.ui_scale : FALLBACK_ICON_HEIGHT); this._applet_icon.set_style_class_name('applet-icon'); break; case St.IconType.SYMBOLIC: this._applet_icon.set_icon_size(this._scaleMode ? (this._panelHeight / DEFAULT_PANEL_HEIGHT) * PANEL_SYMBOLIC_ICON_DEFAULT_HEIGHT / global.ui_scale : -1); this._applet_icon.set_style_class_name('system-status-icon'); } }, on_panel_height_changed: function() { this._scaleMode = AppletManager.enabledAppletDefinitions.idMap[this.instance_id].panel.scaleMode; if (this._applet_icon) this._setStyle(); } }; /** * #TextApplet: * @short_description: Applet with label * @_applet_label (St.Label): Label of the applet * * Applet that displays a text * * Inherits: Applet.Applet */ function TextApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } TextApplet.prototype = { __proto__: Applet.prototype, /** * _init: * @orientation (St.Side): orientation of the applet; Orientation of panel containing the actor * @panelHeight (int): height of the panel containing the applet * @instance_id (int): instance id of the applet */ _init: function(orientation, panel_height, instance_id) { Applet.prototype._init.call(this, orientation, panel_height, instance_id); this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); this._applet_label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.actor.add(this._applet_label, { y_align: St.Align.MIDDLE, y_fill: false }); this.actor.set_label_actor(this._applet_label); }, /** * set_applet_label: * @text (string): text to be displayed at the label * * Sets the text of the actor to @text */ set_applet_label: function (text) { this._applet_label.set_text(text); }, on_applet_added_to_panel: function() { } }; /** * #TextIconApplet: * @short_description: Applet with icon and label * @_applet_label (St.Label): Label of the applet * * Applet that displays an icon and a text. The icon is on the left of the text * * Inherits: Applet.IconApplet */ function TextIconApplet(orientation, panel_height, instance_id) { this._init(orientation, panel_height, instance_id); } TextIconApplet.prototype = { __proto__: IconApplet.prototype, /** * _init: * @orientation (St.Side): orientation of the applet; Orientation of panel containing the actor * @panelHeight (int): height of the panel containing the applet * @instance_id (int): instance id of the applet */ _init: function(orientation, panel_height, instance_id) { IconApplet.prototype._init.call(this, orientation, panel_height, instance_id); this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); this._applet_label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.actor.add(this._applet_label, { y_align: St.Align.MIDDLE, y_fill: false }); this.actor.set_label_actor(this._applet_label); }, /** * set_applet_label: * @text (string): text to be displayed at the label * * Sets the text of the actor to @text */ set_applet_label: function (text) { this._applet_label.set_text(text); if ((text && text != "") && this._applet_icon_box.child) { this._applet_label.set_margin_left(6.0); } else { this._applet_label.set_margin_left(0); } }, /** * set_applet_enabled: * @enabled (boolean): whether this applet is enabled or not * * Sets whether the applet is enabled or not. A disabled applet sets its * padding to 0px and doesn't react to clicks */ set_applet_enabled: function (enabled) { if (enabled != this._applet_enabled) { this._applet_enabled = enabled; this.actor.visible = enabled; if (this._applet_icon) { this._applet_icon.visible = enabled; } } }, /** * hide_applet_icon: * * Hides the icon of the applet */ hide_applet_icon: function () { this._applet_icon_box.child = null; }, on_applet_added_to_panel: function() { } }; Cinnamon-2.8.6/js/ui/edgeFlip.js0000664000175000017500000000445612625666430015346 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Main = imports.ui.main; const Clutter = imports.gi.Clutter; const St = imports.gi.St; const Mainloop = imports.mainloop; const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; function EdgeFlipper(side, func){ this._init(side, func); } EdgeFlipper.prototype = { _init: function(side, func){ this.side = side; this.func = func; this.enabled = true; this.delay = 1000; this.entered = false; this.activated = false; this._checkOver(); }, _checkOver: function(){ if (this.enabled) { let mask; [this.xMouse, this.yMouse, mask] = global.get_pointer(); if (!(mask & Clutter.ModifierType.BUTTON1_MASK)) { if (this.side == St.Side.RIGHT){ if (this.xMouse + 2 > global.screen_width){ this._onMouseEnter(); } else { this._onMouseLeave(); } } else if (this.side == St.Side.LEFT){ if (this.xMouse < 2 ){ this._onMouseEnter(); } else { this._onMouseLeave(); } } else if (this.side == St.Side.BOTTOM){ if (this.yMouse + 2 > global.screen_height) { this._onMouseEnter(); } else { this._onMouseLeave(); } } else if (this.side == St.Side.TOP){ if (this.yMouse < 2){ this._onMouseEnter(); } else { this._onMouseLeave(); } } } Mainloop.timeout_add(Math.max(this.delay, 200), Lang.bind(this, this._checkOver)); } }, _onMouseEnter: function(){ this.entered = true; Mainloop.timeout_add(this.delay, Lang.bind(this, this._check)); }, _check: function(){ if (this.entered && this.enabled && !this.activated){ this.func(); this.activated = true; } }, _onMouseLeave: function(){ this.entered = false; this.activated = false; } }; Cinnamon-2.8.6/js/ui/boxpointer.js0000664000175000017500000005225612625666430016021 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Cinnamon = imports.gi.Cinnamon; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Meta = imports.gi.Meta; const St = imports.gi.St; const Main = imports.ui.main; const Tweener = imports.ui.tweener; const POPUP_ANIMATION_TIME = 0.15; /** * BoxPointer: * @side: side to draw the arrow on * @binProperties: Properties to set on contained bin * * An actor which displays a triangle "arrow" pointing to a given * side. The .bin property is a container in which content can be * placed. The arrow position may be controlled via setArrowOrigin(). * */ function BoxPointer(side, binProperties) { this._init(side, binProperties); } BoxPointer.prototype = { _init: function(arrowSide, binProperties) { this._arrowSide = arrowSide; this._userArrowSide = arrowSide; this._arrowOrigin = 0; this.actor = new St.Bin({ x_fill: true, y_fill: true }); this._container = new Cinnamon.GenericContainer(); this.actor.set_child(this._container); this._container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this._container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this._container.connect('allocate', Lang.bind(this, this._allocate)); this.bin = new St.Bin(binProperties); this._container.add_actor(this.bin); this._border = new St.DrawingArea(); this._border.connect('repaint', Lang.bind(this, this._drawBorder)); this._container.add_actor(this._border); this.bin.raise(this._border); this._xOffset = 0; this._yOffset = 0; this._xPosition = 0; this._yPosition = 0; this._sourceAlignment = 0.5; }, show: function(animate, onComplete) { let themeNode = this.actor.get_theme_node(); let rise = themeNode.get_length('-arrow-rise'); if (animate) { this.opacity = 0; this.actor.show(); switch (this._arrowSide) { case St.Side.TOP: this.yOffset = -rise; break; case St.Side.BOTTOM: this.yOffset = rise; break; case St.Side.LEFT: this.xOffset = -rise; break; case St.Side.RIGHT: this.xOffset = rise; break; } Tweener.addTween(this, { opacity: 255, xOffset: 0, yOffset: 0, transition: 'linear', onComplete: onComplete, time: POPUP_ANIMATION_TIME }); } else { this.opacity = 255; this.actor.show(); } }, hide: function(animate, onComplete) { let xOffset = 0; let yOffset = 0; let themeNode = this.actor.get_theme_node(); let rise = themeNode.get_length('-arrow-rise'); if (animate) { switch (this._arrowSide) { case St.Side.TOP: yOffset = rise; break; case St.Side.BOTTOM: yOffset = -rise; break; case St.Side.LEFT: xOffset = rise; break; case St.Side.RIGHT: xOffset = -rise; break; } Tweener.addTween(this, { opacity: 0, xOffset: xOffset, yOffset: yOffset, transition: 'linear', time: POPUP_ANIMATION_TIME, onComplete: Lang.bind(this, function () { this.actor.hide(); this.xOffset = 0; this.yOffset = 0; if (onComplete) onComplete(); }) }); } else { this.actor.hide(); } }, /** * setArrowSide: * @side (St.Side): The new side of the menu * * Sets the arrow side of the menu. Note that the side is the side * of the source actor, not the menu, e.g. If St.Side.TOP is set, * then the menu will appear below the source actor (the source * actor will be on top of the menu) */ setArrowSide: function(side) { // Need not trigger any other function. Menu position is // recalculated every time it is shown this._arrowSide = side; }, _adjustAllocationForArrow: function(isWidth, alloc) { let themeNode = this.actor.get_theme_node(); let borderWidth = themeNode.get_length('-arrow-border-width'); alloc.min_size += borderWidth * 2; alloc.natural_size += borderWidth * 2; if ((!isWidth && (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM)) || (isWidth && (this._arrowSide == St.Side.LEFT || this._arrowSide == St.Side.RIGHT))) { let rise = themeNode.get_length('-arrow-rise'); alloc.min_size += rise; alloc.natural_size += rise; } }, _getPreferredWidth: function(actor, forHeight, alloc) { let [minInternalSize, natInternalSize] = this.bin.get_preferred_width(forHeight); alloc.min_size = minInternalSize; alloc.natural_size = natInternalSize; this._adjustAllocationForArrow(true, alloc); }, _getPreferredHeight: function(actor, forWidth, alloc) { let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth); alloc.min_size = minSize; alloc.natural_size = naturalSize; this._adjustAllocationForArrow(false, alloc); }, _allocate: function(actor, box, flags) { let themeNode = this.actor.get_theme_node(); let borderWidth = themeNode.get_length('-arrow-border-width'); let rise = themeNode.get_length('-arrow-rise'); let childBox = new Clutter.ActorBox(); let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; childBox.x1 = 0; childBox.y1 = 0; childBox.x2 = availWidth; childBox.y2 = availHeight; this._border.allocate(childBox, flags); childBox.x1 = borderWidth; childBox.y1 = borderWidth; childBox.x2 = availWidth - borderWidth; childBox.y2 = availHeight - borderWidth; switch (this._arrowSide) { case St.Side.TOP: childBox.y1 += rise; break; case St.Side.BOTTOM: childBox.y2 -= rise; break; case St.Side.LEFT: childBox.x1 += rise; break; case St.Side.RIGHT: childBox.x2 -= rise; break; } this.bin.allocate(childBox, flags); if (this._sourceActor && this._sourceActor.mapped) { this._reposition(); this._updateFlip(); } }, _drawBorder: function(area) { let themeNode = this.actor.get_theme_node(); let borderWidth = themeNode.get_length('-arrow-border-width'); let base = themeNode.get_length('-arrow-base'); let rise = themeNode.get_length('-arrow-rise'); let borderRadius = themeNode.get_length('-arrow-border-radius'); let halfBorder = borderWidth / 2; let halfBase = Math.floor(base/2); let backgroundColor = themeNode.get_color('-arrow-background-color'); let [width, height] = area.get_surface_size(); let [boxWidth, boxHeight] = [width, height]; if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) { boxHeight -= rise; } else { boxWidth -= rise; } let cr = area.get_context(); // Translate so that box goes from 0,0 to boxWidth,boxHeight, // with the arrow poking out of that if (this._arrowSide == St.Side.TOP) { cr.translate(0, rise); } else if (this._arrowSide == St.Side.LEFT) { cr.translate(rise, 0); } let [x1, y1] = [halfBorder, halfBorder]; let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder]; let skipTopLeft = false; let skipTopRight = false; let skipBottomLeft = false; let skipBottomRight = false; switch (this._arrowSide) { case St.Side.TOP: if (this._arrowOrigin == x1) skipTopLeft = true; else if (this._arrowOrigin == x2) skipTopRight = true; break; case St.Side.RIGHT: if (this._arrowOrigin == y1) skipTopRight = true; else if (this._arrowOrigin == y2) skipBottomRight = true; break; case St.Side.BOTTOM: if (this._arrowOrigin == x1) skipBottomLeft = true; else if (this._arrowOrigin == x2) skipBottomRight = true; break; case St.Side.LEFT: if (this._arrowOrigin == y1) skipTopLeft = true; else if (this._arrowOrigin == y2) skipBottomLeft = true; break; } cr.moveTo(x1 + borderRadius, y1); if (this._arrowSide == St.Side.TOP) { if (skipTopLeft) { cr.moveTo(x1, y2 - borderRadius); cr.lineTo(x1, y1 - rise); cr.lineTo(x1 + halfBase, y1); } else if (skipTopRight) { cr.lineTo(x2 - halfBase, y1); cr.lineTo(x2, y1 - rise); cr.lineTo(x2, y1 + borderRadius); } else { cr.lineTo(this._arrowOrigin - halfBase, y1); cr.lineTo(this._arrowOrigin, y1 - rise); cr.lineTo(this._arrowOrigin + halfBase, y1); } } if (!skipTopRight) { cr.lineTo(x2 - borderRadius, y1); cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius, 3*Math.PI/2, Math.PI*2); } if (this._arrowSide == St.Side.RIGHT) { if (skipTopRight) { cr.lineTo(x2 + rise, y1); cr.lineTo(x2 + rise, y1 + halfBase); } else if (skipBottomRight) { cr.lineTo(x2, y2 - halfBase); cr.lineTo(x2 + rise, y2); cr.lineTo(x2 - borderRadius, y2); } else { cr.lineTo(x2, this._arrowOrigin - halfBase); cr.lineTo(x2 + rise, this._arrowOrigin); cr.lineTo(x2, this._arrowOrigin + halfBase); } } if (!skipBottomRight) { cr.lineTo(x2, y2 - borderRadius); cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius, 0, Math.PI/2); } if (this._arrowSide == St.Side.BOTTOM) { if (skipBottomLeft) { cr.lineTo(x1 + halfBase, y2); cr.lineTo(x1, y2 + rise); cr.lineTo(x1, y2 - borderRadius); } else if (skipBottomRight) { cr.lineTo(x2, y2 + rise); cr.lineTo(x2 - halfBase, y2); } else { cr.lineTo(this._arrowOrigin + halfBase, y2); cr.lineTo(this._arrowOrigin, y2 + rise); cr.lineTo(this._arrowOrigin - halfBase, y2); } } if (!skipBottomLeft) { cr.lineTo(x1 + borderRadius, y2); cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius, Math.PI/2, Math.PI); } if (this._arrowSide == St.Side.LEFT) { if (skipTopLeft) { cr.lineTo(x1, y1 + halfBase); cr.lineTo(x1 - rise, y1); cr.lineTo(x1 + borderRadius, y1); } else if (skipBottomLeft) { cr.lineTo(x1 - rise, y2) cr.lineTo(x1 - rise, y2 - halfBase); } else { cr.lineTo(x1, this._arrowOrigin + halfBase); cr.lineTo(x1 - rise, this._arrowOrigin); cr.lineTo(x1, this._arrowOrigin - halfBase); } } if (!skipTopLeft) { cr.lineTo(x1, y1 + borderRadius); cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius, Math.PI, 3*Math.PI/2); } Clutter.cairo_set_source_color(cr, backgroundColor); cr.fillPreserve(); if (borderWidth > 0) { let borderColor = themeNode.get_color('-arrow-border-color'); Clutter.cairo_set_source_color(cr, borderColor); cr.setLineWidth(borderWidth); cr.stroke(); } cr.$dispose(); }, setPosition: function(sourceActor, alignment) { // We need to show it now to force an allocation, // so that we can query the correct size. this.actor.show(); this._sourceActor = sourceActor; this._arrowAlignment = alignment; this._reposition(); this._updateFlip(); }, setSourceAlignment: function(alignment) { this._sourceAlignment = alignment; if (!this._sourceActor) return; this.setPosition(this._sourceActor, this._arrowAlignment); }, _reposition: function() { let sourceActor = this._sourceActor; let alignment = this._arrowAlignment; // Position correctly relative to the sourceActor let sourceNode = sourceActor.get_theme_node(); let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box()); let sourceAllocation = Cinnamon.util_get_transformed_allocation(sourceActor); let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment; let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment; let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size(); // We also want to keep it onscreen, and separated from the // edge by the same distance as the main part of the box is // separated from its sourceActor let monitor = Main.layoutManager.findMonitorForActor(sourceActor); let themeNode = this.actor.get_theme_node(); let borderWidth = themeNode.get_length('-arrow-border-width'); let arrowBase = themeNode.get_length('-arrow-base'); let borderRadius = themeNode.get_length('-arrow-border-radius'); let margin = (4 * borderRadius + borderWidth + arrowBase); let gap = themeNode.get_length('-boxpointer-gap'); let padding = themeNode.get_length('-arrow-rise'); let resX, resY; switch (this._arrowSide) { case St.Side.TOP: resY = sourceAllocation.y2 + gap; break; case St.Side.BOTTOM: resY = sourceAllocation.y1 - natHeight - gap; break; case St.Side.LEFT: resX = sourceAllocation.x2 + gap; break; case St.Side.RIGHT: resX = sourceAllocation.x1 - natWidth - gap; break; } // Now align and position the pointing axis, making sure it fits on // screen. If the arrowOrigin is so close to the edge that the arrow // will not be isosceles, we try to compensate as follows: // - We skip the rounded corner and settle for a right angled arrow // as shown below. See _drawBorder for further details. // |\_____ // | // | // - If the arrow was going to be acute angled, we move the position // of the box to maintain the arrow's accuracy. let arrowOrigin; let halfBase = Math.floor(arrowBase/2); let halfBorder = borderWidth / 2; let halfMargin = margin / 2; let [x1, y1] = [halfBorder, halfBorder]; let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder]; switch (this._arrowSide) { case St.Side.TOP: case St.Side.BOTTOM: resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment); resX = Math.max(resX, monitor.x + padding); resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth)); arrowOrigin = sourceCenterX - resX; if (arrowOrigin <= (x1 + (borderRadius + halfBase))) { if (arrowOrigin > x1) resX += (arrowOrigin - x1); arrowOrigin = x1; } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) { if (arrowOrigin < x2) resX -= (x2 - arrowOrigin); arrowOrigin = x2; } break; case St.Side.LEFT: case St.Side.RIGHT: resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment); resY = Math.max(resY, monitor.y + padding); resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight)); arrowOrigin = sourceCenterY - resY; if (arrowOrigin <= (y1 + (borderRadius + halfBase))) { if (arrowOrigin > y1) resY += (arrowOrigin - y1); arrowOrigin = y1; } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) { if (arrowOrigin < y2) resX -= (y2 - arrowOrigin); arrowOrigin = y2; } break; } this.setArrowOrigin(arrowOrigin); let parent = this.actor.get_parent(); let success, x, y; while (!success) { [success, x, y] = parent.transform_stage_point(resX, resY); parent = parent.get_parent(); } this._xPosition = Math.floor(x); this._yPosition = Math.floor(y); this._shiftActor(); }, // @origin: Coordinate specifying middle of the arrow, along // the Y axis for St.Side.LEFT, St.Side.RIGHT from the top and X axis from // the left for St.Side.TOP and St.Side.BOTTOM. setArrowOrigin: function(origin) { if (this._arrowOrigin != origin) { this._arrowOrigin = origin; this._border.queue_repaint(); } }, _shiftActor : function() { // Since the position of the BoxPointer depends on the allocated size // of the BoxPointer and the position of the source actor, trying // to position the BoxPoiner via the x/y properties will result in // allocation loops and warnings. Instead we do the positioning via // the anchor point, which is independent of allocation, and leave // x == y == 0. this.actor.set_anchor_point(-(this._xPosition + this._xOffset), -(this._yPosition + this._yOffset)); }, _calculateArrowSide: function(arrowSide) { let sourceAllocation = Cinnamon.util_get_transformed_allocation(this._sourceActor); let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size(); let monitor = Main.layoutManager.findMonitorForActor(this.actor); switch (arrowSide) { case St.Side.TOP: if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height && boxHeight < sourceAllocation.y1 - monitor.y) return St.Side.BOTTOM; break; case St.Side.BOTTOM: if (sourceAllocation.y1 - boxHeight < monitor.y && boxHeight < monitor.y + monitor.height - sourceAllocation.y2) return St.Side.TOP; break; case St.Side.LEFT: if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width && boxWidth < sourceAllocation.x1 - monitor.x) return St.Side.RIGHT; break; case St.Side.RIGHT: if (sourceAllocation.x1 - boxWidth < monitor.x && boxWidth < monitor.x + monitor.width - sourceAllocation.x2) return St.Side.LEFT; break; } return arrowSide; }, _updateFlip: function() { let arrowSide = this._calculateArrowSide(this._userArrowSide); if (this._arrowSide != arrowSide) { this._arrowSide = arrowSide; this._reposition(); Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._container.queue_relayout(); return false; })); } }, set xOffset(offset) { this._xOffset = offset; this._shiftActor(); }, get xOffset() { return this._xOffset; }, set yOffset(offset) { this._yOffset = offset; this._shiftActor(); }, get yOffset() { return this._yOffset; }, set opacity(opacity) { this.actor.opacity = opacity; }, get opacity() { return this.actor.opacity; } }; Cinnamon-2.8.6/js/ui/systray.js0000664000175000017500000000207512625666430015340 0ustar fabiofabioconst Signals = imports.signals; function SystrayManager() { this._init(); } SystrayManager.prototype = { _init: function() { this._roles = []; }, registerRole: function(role, id) { this._roles.push({role: role, id: id}); this.emit("changed"); }, unregisterRole: function(role, id) { for (let i = this._roles.length - 1; i >= 0; i--) { if (this._roles[i].id == id && this._roles[i].role == role) { this._roles.splice(i, 1); } } this.emit("changed"); }, unregisterId: function(id) { for (let i = this._roles.length - 1; i >= 0; i--) { if (this._roles[i].id == id) { this._roles.splice(i, 1); } } this.emit("changed"); }, getRoles: function(id) { let roles = []; for (let i = 0; i < this._roles.length; i++) { roles.push(this._roles[i].role); } return roles; } } Signals.addSignalMethods(SystrayManager.prototype); Cinnamon-2.8.6/js/ui/cinnamonDBus.js0000664000175000017500000003206012625666430016177 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Config = imports.misc.config; const Extension = imports.ui.extension; const Flashspot = imports.ui.flashspot; const Main = imports.ui.main; const AppletManager = imports.ui.appletManager; const DeskletManager = imports.ui.deskletManager; const ExtensionSystem = imports.ui.extensionSystem; const SearchProviderManager = imports.ui.searchProviderManager; const Util = imports.misc.util; const CinnamonIface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; function Cinnamon() { this._init(); } Cinnamon.prototype = { _init: function() { this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(CinnamonIface, this); this._dbusImpl.export(Gio.DBus.session, '/org/Cinnamon'); }, /** * Eval: * @code: A string containing JavaScript code * * This function executes arbitrary code in the main * loop, and returns a boolean success and * JSON representation of the object as a string. * * If evaluation completes without throwing an exception, * then the return value will be [true, JSON.stringify(result)]. * If evaluation fails, then the return value will be * [false, JSON.stringify(exception)]; * */ Eval: function(code) { let returnValue; let success; try { returnValue = JSON.stringify(eval(code)); // A hack; DBus doesn't have null/undefined if (returnValue == undefined) returnValue = ''; success = true; } catch (e) { returnValue = JSON.stringify(e); success = false; } return [success, returnValue]; }, _onScreenshotComplete: function(obj, result, area, flash, invocation) { if (flash) { let flashspot = new Flashspot.Flashspot(area); flashspot.fire(); } let retval = GLib.Variant.new('(b)', [result]); invocation.return_value(retval); }, /** * ScreenshotArea: * @include_cursor: Whether to include the mouse cursor * @x: The X coordinate of the area * @y: The Y coordinate of the area * @width: The width of the area * @height: The height of the area * @flash: Whether to flash the edges of area * @filename: The filename for the screenshot * * Takes a screenshot of the passed in area and saves it * in @filename as png image, it returns a boolean * indicating whether the operation was successful or not. * */ ScreenshotAreaAsync : function (params, invocation) { let [include_cursor, x, y, width, height, flash, filename, callback] = params; let screenshot = new Cinnamon.Screenshot(); screenshot.screenshot_area (include_cursor, x, y, width, height, filename, Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, /** * ScreenshotWindow: * @include_frame: Whether to include the frame or not * @include_cursor: Whether to include the mouse cursor * @flash: Whether to flash the edges of the window * @filename: The filename for the screenshot * * Takes a screenshot of the focused window (optionally omitting the frame) * and saves it in @filename as png image, it returns a boolean * indicating whether the operation was successful or not. * */ ScreenshotWindowAsync : function (params, invocation) { let [include_frame, include_cursor, flash, filename] = params; let screenshot = new Cinnamon.Screenshot(); screenshot.screenshot_window (include_frame, include_cursor, filename, Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, /** * Screenshot: * @include_cursor: Whether to include the mouse cursor * @flash: Whether to flash the edges of the screen * @filename: The filename for the screenshot * * Takes a screenshot of the whole screen and saves it * in @filename as png image, it returns a boolean * indicating whether the operation was successful or not. * */ ScreenshotAsync : function (params, invocation) { let [include_cursor, flash, filename] = params; let screenshot = new Cinnamon.Screenshot(); screenshot.screenshot(include_cursor, filename, Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, ShowOSD: function(params) { for (let param in params) params[param] = params[param].deep_unpack(); let icon = null; if (params['icon']) icon = Gio.Icon.new_for_string(params['icon']); Main.osdWindow.setIcon(icon); Main.osdWindow.setLevel(params['level']); if (params) Main.osdWindow.show(); }, FlashArea: function(x, y, width, height) { let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height}); flashspot.fire(); }, get OverviewActive() { return Main.overview.visible; }, set OverviewActive(visible) { if (visible) Main.overview.show(); else Main.overview.hide(); }, get ExpoActive() { return Main.expo.visible; }, set ExpoActive(visible) { if (visible) Main.expo.show(); else Main.expo.hide(); }, _getXletObject: function(uuid, instance_id) { var obj = null; obj = AppletManager.get_object_for_uuid(uuid, instance_id); if (!obj) { obj = DeskletManager.get_object_for_uuid(uuid, instance_id); } return obj }, EmitXletAddedComplete: function(success, uuid, name) { this._dbusImpl.emit_signal('XletAddedComplete', GLib.Variant.new('(bs)', [success,uuid])); }, GetRunningXletUUIDs: function(type) { let list = null; let res = []; if (type == "applet") { list = AppletManager.appletObj; for (let key in list) { res.push(list[key]._uuid); } } else if (type == "desklet") { list = DeskletManager.deskletObj; for (let key in list) { res.push(list[key]._uuid); } } else { list = ExtensionSystem.runningExtensions; for (let uuid in list) { res.push(uuid); } } return res; }, ReloadXlet: function(uuid, type) { Extension.reloadExtension(uuid, Extension.Type[type]); }, highlightApplet: function(uuid, instance_id) { let obj = this._getXletObject(uuid, instance_id); if (!obj) return; let actor = obj.actor; if (actor) { let [x, y] = actor.get_transformed_position(); let [w, h] = actor.get_transformed_size(); this.FlashArea(x, y, w, h) } }, highlightPanel: function(id, highlight) { if (Main.panelManager.panels[id]) Main.panelManager.panels[id].highlight(highlight); }, addPanelQuery: function() { Main.panelManager.addPanelQuery(); }, destroyDummyPanels: function() { Main.panelManager._destroyDummyPanels(); }, activateCallback: function(callback, uuid, instance_id) { let obj = this._getXletObject(uuid, instance_id); let cb = Lang.bind(obj, obj[callback]); cb(); }, updateSetting: function(uuid, instance_id, key, payload) { Main.settingsManager.uuids[uuid][instance_id].remote_set(key, payload); }, switchWorkspaceLeft: function() { Main.wm.actionMoveWorkspaceLeft(); }, switchWorkspaceRight: function() { Main.wm.actionMoveWorkspaceRight(); }, switchWorkspaceUp: function() { Main.overview.toggle(); }, switchWorkspaceDown: function() { Main.expo.toggle(); }, JumpToNewWorkspace: function() { Main._addWorkspace(); let num = global.screen.get_n_workspaces(); if (global.screen.get_workspace_by_index(num - 1) != null) { global.screen.get_workspace_by_index(num - 1).activate(global.get_current_time()); } }, RemoveCurrentWorkspace: function() { let index = global.screen.get_active_workspace_index(); if (global.screen.get_workspace_by_index(index) != null) { Main._removeWorkspace(global.screen.get_workspace_by_index(index)); } }, ShowExpo: function() { if (!Main.expo.animationInProgress) Main.expo.toggle(); }, PushSubprocessResult: function(process_id, result) { if (Util.subprocess_callbacks[process_id]) { Util.subprocess_callbacks[process_id](result); } }, CinnamonVersion: Config.PACKAGE_VERSION }; Cinnamon-2.8.6/js/ui/soundManager.js0000664000175000017500000001133712625666430016246 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Lang = imports.lang; const Gio = imports.gi.Gio; const Main = imports.ui.main; const Mainloop = imports.mainloop; const iface = " \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "; const proxy = Gio.DBusProxy.makeProxyWrapper(iface); const PLAY_ONCE_FLAG = 8675309; function SoundManager() { this._init(); } SoundManager.prototype = { _init : function() { this.keys = ["switch", "close", "map", "minimize", "maximize", "unmaximize", "tile", "login", "plug", "unplug"]; this.desktop_keys = ["volume"]; this.startup_delay = true; this.enabled = {}; this.file = {}; this.settings = new Gio.Settings({ schema_id: 'org.cinnamon.sounds' }); this.desktop_settings = new Gio.Settings({ schema_id: 'org.cinnamon.desktop.sound' }); this._cacheSettings(); this._cacheDesktopSettings(); this.settings.connect("changed", Lang.bind(this, this._cacheSettings)); this.desktop_settings.connect("changed", Lang.bind(this, this._cacheDesktopSettings)); Mainloop.timeout_add_seconds(10, Lang.bind(this, function() { this.startup_delay = false; })); this.proxy = new proxy(Gio.DBus.session, 'org.cinnamon.SettingsDaemon', '/org/cinnamon/SettingsDaemon/Sound'); /* patch public methods into global to keep backward compatibility */ global.play_theme_sound = Lang.bind(this, this.playSound); global.play_sound_file = Lang.bind(this, this.playSoundFile); global.cancel_sound = Lang.bind(this, this.cancelSound); }, _cacheSettings: function() { for (var i in this.keys) { let key = this.keys[i]; this.enabled[key] = this.settings.get_boolean(key + "-enabled"); this.file[key] = this.settings.get_string(key + "-file"); } }, _cacheDesktopSettings: function() { for (var i in this.desktop_keys) { let key = this.desktop_keys[i]; this.enabled[key] = this.desktop_settings.get_boolean(key + "-sound-enabled"); this.file[key] = this.desktop_settings.get_string(key + "-sound-file"); } }, play: function(sound) { if (this.startup_delay) return; if (this.enabled[sound] && this.file[sound] != "") { this.playSoundFile(0, this.file[sound]); } }, playVolume: function(sound, volume) { if (this.startup_delay) return; if (this.enabled[sound] && this.file[sound] != "") { this.playSoundFileVolume(0, this.file[sound], volume); } }, /* We want the login sound synced to the fade-in animation * but we don't want it playing every time someone restarts * Cinnamon - passing PLAY_ONCE_FLAG will let the sound handler * know not to play this more than once for its lifetime (usually * for the session.) */ play_once_per_session: function(sound) { if (this.enabled[sound] && this.file[sound] != "") { this.playSoundFile(PLAY_ONCE_FLAG, this.file[sound]); } }, /* Public methods. */ playSoundFile: function(id, filename) { this.proxy.PlaySoundFileRemote(id, filename); }, playSoundFileVolume: function(id, filename, volume) { //ignore volume parameter if it is not valid (no mute) if (!Number.isFinite(volume) || Number.isNaN(volume) || volume < 0) this.playSoundFile(id, filename); else this.proxy.PlaySoundFileVolumeRemote(id, filename, volume + ""); }, playSound: function(id, name) { this.proxy.PlaySoundRemote(id, name); }, cancelSound: function(id) { this.proxy.CancelSoundRemote(id); } }; Cinnamon-2.8.6/js/Makefile.am0000664000175000017500000000362212625666430014702 0ustar fabiofabio jsdir = $(pkgdatadir)/js nobase_dist_js_DATA = \ misc/config.js \ misc/docInfo.js \ misc/fileDialog.js \ misc/fileUtils.js \ misc/format.js \ misc/gnomeSession.js \ misc/gridNavigator.js \ misc/history.js \ misc/interfaces.js \ misc/modemManager.js \ misc/params.js \ misc/pointerTracker.js \ misc/screenSaver.js \ misc/signalManager.js \ misc/timers.js \ misc/util.js \ misc/windowUtils.js \ perf/core.js \ ui/appSwitcher/appSwitcher.js \ ui/appSwitcher/appSwitcher3D.js \ ui/appSwitcher/classicSwitcher.js \ ui/appSwitcher/coverflowSwitcher.js \ ui/appSwitcher/timelineSwitcher.js \ ui/applet.js \ ui/appletManager.js \ ui/searchProviderManager.js \ ui/appFavorites.js \ ui/backgroundManager.js \ ui/boxpointer.js \ ui/checkBox.js \ ui/radioButton.js \ ui/dbusMenu.js \ ui/indicatorManager.js \ ui/desklet.js \ ui/deskletManager.js \ ui/dnd.js \ ui/edgeFlip.js \ ui/environment.js \ ui/expo.js \ ui/expoThumbnail.js \ ui/extension.js \ ui/extensionSystem.js \ ui/flashspot.js \ ui/hotCorner.js \ ui/keybindings.js \ ui/keyboard.js \ ui/layout.js \ ui/lightbox.js \ ui/lookingGlass.js \ ui/magnifier.js \ ui/magnifierDBus.js \ ui/main.js \ ui/messageTray.js \ ui/modalDialog.js \ ui/cinnamonEntry.js \ ui/cinnamonMountOperation.js \ ui/osdWindow.js \ ui/notificationDaemon.js \ ui/overview.js \ ui/overrides.js \ ui/panel.js \ ui/panelMenu.js \ ui/placesManager.js \ ui/popupMenu.js \ ui/runDialog.js \ ui/scripting.js \ ui/separator.js \ ui/slider.js \ ui/soundManager.js \ ui/search.js \ ui/settings.js \ ui/slideshowManager.js \ ui/systray.js \ ui/cinnamonDBus.js \ ui/statusIconDispatcher.js \ ui/themeManager.js \ ui/tooltips.js \ ui/tweener.js \ ui/windowAttentionHandler.js \ ui/windowManager.js \ ui/windowEffects.js \ ui/workspace.js \ ui/workspacesView.js \ ui/xdndHandler.js Cinnamon-2.8.6/generate_cs_module_desktop_files.py0000775000175000017500000000330212625666430021341 0ustar fabiofabio#!/usr/bin/python2 DOMAIN = "cinnamon" PATH = "/usr/share/locale" import os, gettext, sys sys.path.append('/usr/lib/linuxmint/common') import additionalfiles os.environ['LANG'] = "en_US.UTF-8" gettext.install(DOMAIN, PATH) import os import glob import polib import sys from gi.repository import GLib try: sys.path.append('files/usr/share/cinnamon/cinnamon-settings/modules') sys.path.append('files/usr/share/cinnamon/cinnamon-settings/bin') mod_files = glob.glob('files/usr/share/cinnamon/cinnamon-settings/modules/*.py') mod_files.sort() if len(mod_files) is 0: raise Exception("No settings modules found!!") for i in range(len(mod_files)): mod_files[i] = mod_files[i].split('/')[-1] mod_files[i] = mod_files[i].split('.')[0] if mod_files[i][0:3] != "cs_": raise Exception("Settings modules must have a prefix of 'cs_' !!") modules = map(__import__, mod_files) except Exception, detail: print detail sys.exit(1) for i in range(len(modules)): try: mod = modules[i].Module(None) if mod.category in ("admin"): category = "Settings;System;" else: category = "Settings;" prefix = """[Desktop Entry] Icon=%(icon)s Exec=cinnamon-settings %(module)s Type=Application OnlyShowIn=X-Cinnamon; Categories=Settings; """ % {'module': mod.name, 'category': category, 'icon': mod.sidePage.icon} additionalfiles.generate(DOMAIN, PATH, "files/usr/share/applications/cinnamon-settings-%s.desktop" % mod.name, prefix, mod.sidePage.name, mod.comment, "") except: print "Failed to load module %s" % modules[i] import traceback traceback.print_exc() Cinnamon-2.8.6/configure.ac0000664000175000017500000002066012625666430014521 0ustar fabiofabioAC_PREREQ(2.63) AC_INIT([cinnamon],[2.8.6],[https://github.com/linuxmint/Cinnamon/issues],[cinnamon]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([src/cinnamon-global.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([config]) AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"]) AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"]) AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar foreign subdir-objects]) AM_MAINTAINER_MODE([enable]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) # Checks for programs. AC_PROG_CC AC_PROG_CXX # Needed for per-target cflags, like in cinnamon-taskpanel AM_PROG_CC_C_O # Initialize libtool LT_PREREQ([2.2.6]) LT_INIT([disable-static]) # i18n IT_PROG_INTLTOOL([0.40]) AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.17]) GETTEXT_PACKAGE=cinnamon AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [The prefix for our gettext translation domains.]) PKG_PROG_PKG_CONFIG([0.22]) GLIB_GSETTINGS # Get a value to substitute into cinnamon.in AM_PATH_PYTHON([2.5]) AC_SUBST(PYTHON) # We need at least this, since gst_plugin_register_static() was added # in 0.10.16, but nothing older than 0.10.21 has been tested. GSTREAMER_MIN_VERSION=0.11.92 recorder_modules= build_recorder=false AC_MSG_CHECKING([for GStreamer (needed for recording functionality)]) if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then AC_MSG_RESULT(yes) build_recorder=true recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11" PKG_CHECK_MODULES(TEST_CINNAMON_RECORDER, $recorder_modules clutter-1.0 xfixes) else AC_MSG_RESULT(no) fi AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) CLUTTER_MIN_VERSION=1.10.0 GOBJECT_INTROSPECTION_MIN_VERSION=0.9.2 GJS_MIN_VERSION=2.3.1 MUFFIN_MIN_VERSION=2.5.0 GTK_MIN_VERSION=3.9.12 GIO_MIN_VERSION=2.35.0 POLKIT_MIN_VERSION=0.100 STARTUP_NOTIFICATION_MIN_VERSION=0.11 # Collect more than 20 libraries for a prize! PKG_CHECK_MODULES(CINNAMON, gio-2.0 >= $GIO_MIN_VERSION gio-unix-2.0 dbus-glib-1 libxml-2.0 gtk+-3.0 >= $GTK_MIN_VERSION libmuffin >= $MUFFIN_MIN_VERSION cjs-internals-1.0 >= $GJS_MIN_VERSION libcinnamon-menu-3.0 $recorder_modules gconf-2.0 gdk-x11-3.0 libsoup-2.4 gl clutter-x11-1.0 >= $CLUTTER_MIN_VERSION libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes libnm-glib libnm-util gnome-keyring-1 atk-bridge-2.0) PKG_CHECK_MODULES(CINNAMON_JS, gio-2.0 cjs-internals-1.0 >= $GJS_MIN_VERSION) PKG_CHECK_MODULES(CINNAMON_PERF_HELPER, gtk+-3.0 gio-2.0) PKG_CHECK_MODULES(CINNAMON_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0) GJS_VERSION=`$PKG_CONFIG --modversion cjs-internals-1.0` AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to]) AC_SUBST([GJS_VERSION], ["$GJS_VERSION"]) GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR" AC_SUBST(JHBUILD_TYPELIBDIR) saved_CFLAGS=$CFLAGS saved_LIBS=$LIBS CFLAGS=$CINNAMON_CFLAGS LIBS=$CINNAMON_LIBS AC_CHECK_FUNCS(XFixesCreatePointerBarrier) CFLAGS=$saved_CFLAGS LIBS=$saved_LIBS PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 cinnamon-desktop >= 2.4.0 x11) PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0) PKG_CHECK_MODULES(TRAY, gtk+-3.0) PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0) PKG_CHECK_MODULES(DESKTOP_SCHEMAS, cinnamon-desktop >= 2.4.0) AC_MSG_CHECKING([for bluetooth support]) PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0], [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0` BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0` AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"]) AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"]) AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library]) AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet]) AC_SUBST([HAVE_BLUETOOTH],[1]) AC_MSG_RESULT([yes])], [AC_DEFINE([HAVE_BLUETOOTH],[0]) AC_SUBST([HAVE_BLUETOOTH],[0]) AC_MSG_RESULT([no])]) MUFFIN_GIR_DIR=`$PKG_CONFIG --variable=girdir libmuffin` MUFFIN_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmuffin` AC_SUBST(MUFFIN_GIR_DIR) AC_SUBST(MUFFIN_TYPELIB_DIR) GJS_CONSOLE=`$PKG_CONFIG --variable=cjs_console cjs-1.0` AC_SUBST(GJS_CONSOLE) AC_CHECK_FUNCS(fdwalk) AC_CHECK_FUNCS(mallinfo) AC_CHECK_HEADERS([sys/resource.h]) # _NL_TIME_FIRST_WEEKDAY is an enum and not a define AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])], [langinfo_ok=yes], [langinfo_ok=no]) AC_MSG_RESULT($langinfo_ok) if test "$langinfo_ok" = "yes"; then AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1], [Define if _NL_TIME_FIRST_WEEKDAY is available]) fi # Sets GLIB_GENMARSHAL and GLIB_MKENUMS AM_PATH_GLIB_2_0() G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` AC_SUBST(G_IR_SCANNER) G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` AC_SUBST(G_IR_COMPILER) G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` AC_SUBST(G_IR_GENERATE) GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` AC_SUBST(GIRDIR) TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" AC_SUBST(TYPELIBDIR) # Stay command-line compatible with the gnome-common configure option. Here # minimum/yes/maximum are the same, however. AC_ARG_ENABLE(compile_warnings, AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),, enable_compile_warnings=yes) changequote(,)dnl if test "$enable_compile_warnings" != no ; then if test "x$GCC" = "xyes"; then case " $CFLAGS " in *[\ \ ]-Wall[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wall" ;; esac case " $CFLAGS " in *[\ \ ]-Wmissing-prototypes[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;; esac if test "$enable_compile_warnings" = error ; then case " $CFLAGS " in *[\ \ ]-Werror[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Werror" ;; esac fi fi fi changequote([,])dnl AC_ARG_ENABLE(jhbuild-wrapper-script, AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "cinnamon" script work for jhbuild]),,enable_jhbuild_wrapper_script=no) AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes) AC_ARG_ENABLE([gtk-doc], AC_HELP_STRING([--enable-gtk-doc], [use gtk-doc to build documentation [[default=yes]]]),, enable_gtk_doc=yes) AC_MSG_CHECKING([location of system Certificate Authority list]) AC_ARG_WITH(ca-certificates, [AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@], [path to system Certificate Authority list])]) if test "$with_ca_certificates" = "no"; then AC_MSG_RESULT([disabled]) else if test -z "$with_ca_certificates"; then for f in /etc/pki/tls/certs/ca-bundle.crt \ /etc/ssl/certs/ca-certificates.crt; do if test -f "$f"; then with_ca_certificates="$f" fi done if test -z "$with_ca_certificates"; then AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable]) fi fi AC_MSG_RESULT($with_ca_certificates) AC_DEFINE_UNQUOTED(CINNAMON_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list]) fi AC_SUBST(CINNAMON_SYSTEM_CA_FILE,["$with_ca_certificates"]) GTK_DOC_CHECK([1.15], [--flavour no-tmpl]) AC_CONFIG_FILES([ Makefile data/Makefile data/icons/Makefile docs/Makefile docs/reference/Makefile docs/reference/cinnamon/Makefile docs/reference/cinnamon/cinnamon-docs.sgml docs/reference/cinnamon-js/Makefile docs/reference/cinnamon-tutorials/Makefile docs/reference/cinnamon-tutorials/cinnamon-tutorials-docs.sgml docs/reference/st/Makefile docs/reference/st/cinnamon-st-docs.sgml js/Makefile js/misc/config.js src/Makefile tests/Makefile po/Makefile.in man/Makefile files/Makefile ]) AC_OUTPUT Cinnamon-2.8.6/src/0000775000175000017500000000000012625666430013016 5ustar fabiofabioCinnamon-2.8.6/src/cinnamon-util.h0000664000175000017500000000461112625666430015746 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_UTIL_H__ #define __CINNAMON_UTIL_H__ #include #include #include G_BEGIN_DECLS char *cinnamon_util_get_label_for_uri (const char *text_uri); GIcon *cinnamon_util_get_icon_for_uri (const char *text_uri); void cinnamon_util_set_hidden_from_pick (ClutterActor *actor, gboolean hidden); void cinnamon_util_get_transformed_allocation (ClutterActor *actor, ClutterActorBox *box); int cinnamon_util_get_week_start (void); char *cinnamon_util_normalize_and_casefold (const char *str); char *cinnamon_util_format_date (const char *format, gint64 time_ms); ClutterModifierType cinnamon_get_event_state (ClutterEvent *event); void cinnamon_write_soup_message_to_stream (GOutputStream *stream, SoupMessage *message, GError **error); gboolean cinnamon_write_string_to_stream (GOutputStream *stream, const char *str, GError **error); char *cinnamon_get_file_contents_utf8_sync (const char *path, GError **error); void cinnamon_breakpoint (void); gboolean cinnamon_parse_search_provider (const char *data, char **name, char **url, GList **langs, char **icon_data_uri, GError **error); void cinnamon_shader_effect_set_double_uniform (ClutterShaderEffect *effect, const gchar *name, gdouble value); G_END_DECLS #endif /* __CINNAMON_UTIL_H__ */ Cinnamon-2.8.6/src/cinnamon-global.h0000664000175000017500000001441012625666430016227 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_GLOBAL_H__ #define __CINNAMON_GLOBAL_H__ #include #include #include #include #include #include "cinnamon-js.h" G_BEGIN_DECLS typedef struct _CinnamonGlobal CinnamonGlobal; typedef struct _CinnamonGlobalClass CinnamonGlobalClass; #define CINNAMON_TYPE_GLOBAL (cinnamon_global_get_type ()) #define CINNAMON_GLOBAL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_GLOBAL, CinnamonGlobal)) #define CINNAMON_GLOBAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_GLOBAL, CinnamonGlobalClass)) #define CINNAMON_IS_GLOBAL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_GLOBAL)) #define CINNAMON_IS_GLOBAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_GLOBAL)) #define CINNAMON_GLOBAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_GLOBAL, CinnamonGlobalClass)) struct _CinnamonGlobalClass { GObjectClass parent_class; }; GType cinnamon_global_get_type (void) G_GNUC_CONST; CinnamonGlobal *cinnamon_global_get (void); ClutterStage *cinnamon_global_get_stage (CinnamonGlobal *global); MetaScreen *cinnamon_global_get_screen (CinnamonGlobal *global); GdkScreen *cinnamon_global_get_gdk_screen (CinnamonGlobal *global); MetaDisplay *cinnamon_global_get_display (CinnamonGlobal *global); GList *cinnamon_global_get_window_actors (CinnamonGlobal *global); GSettings *cinnamon_global_get_settings (CinnamonGlobal *global); guint32 cinnamon_global_get_current_time (CinnamonGlobal *global); pid_t cinnamon_global_get_pid (void); gchar *cinnamon_global_get_md5_for_string (const gchar *string); gint64 cinnamon_global_get_last_gc_end_time (CinnamonGlobal *global); void cinnamon_global_dump_gjs_stack (CinnamonGlobal *global); /* Input/event handling */ gboolean cinnamon_global_begin_modal (CinnamonGlobal *global, guint32 timestamp, MetaModalOptions options); void cinnamon_global_end_modal (CinnamonGlobal *global, guint32 timestamp); typedef enum { CINNAMON_STAGE_INPUT_MODE_NONREACTIVE, CINNAMON_STAGE_INPUT_MODE_NORMAL, CINNAMON_STAGE_INPUT_MODE_FOCUSED, CINNAMON_STAGE_INPUT_MODE_FULLSCREEN } CinnamonStageInputMode; void cinnamon_global_set_stage_input_mode (CinnamonGlobal *global, CinnamonStageInputMode mode); void cinnamon_global_set_stage_input_region (CinnamonGlobal *global, GSList *rectangles); /* X utilities */ typedef enum { CINNAMON_CURSOR_DND_IN_DRAG, CINNAMON_CURSOR_DND_UNSUPPORTED_TARGET, CINNAMON_CURSOR_DND_MOVE, CINNAMON_CURSOR_DND_COPY, CINNAMON_CURSOR_POINTING_HAND, CINNAMON_CURSOR_RESIZE_BOTTOM, CINNAMON_CURSOR_RESIZE_TOP, CINNAMON_CURSOR_RESIZE_LEFT, CINNAMON_CURSOR_RESIZE_RIGHT, CINNAMON_CURSOR_RESIZE_BOTTOM_RIGHT, CINNAMON_CURSOR_RESIZE_BOTTOM_LEFT, CINNAMON_CURSOR_RESIZE_TOP_RIGHT, CINNAMON_CURSOR_RESIZE_TOP_LEFT, CINNAMON_CURSOR_CROSSHAIR, CINNAMON_CURSOR_TEXT } CinnamonCursor; void cinnamon_global_set_cursor (CinnamonGlobal *global, CinnamonCursor type); void cinnamon_global_unset_cursor (CinnamonGlobal *global); guint32 cinnamon_global_create_pointer_barrier (CinnamonGlobal *global, int x1, int y1, int x2, int y2, int directions); void cinnamon_global_destroy_pointer_barrier (CinnamonGlobal *global, guint32 barrier); void cinnamon_global_get_pointer (CinnamonGlobal *global, int *x, int *y, ClutterModifierType *mods); void cinnamon_global_set_pointer (CinnamonGlobal *global, int x, int y); void cinnamon_global_get_memory_info (CinnamonGlobal *global, CinnamonJSMemoryInfo *meminfo); /* Run-at-leisure API */ void cinnamon_global_begin_work (CinnamonGlobal *global); void cinnamon_global_end_work (CinnamonGlobal *global); typedef void (*CinnamonLeisureFunction) (gpointer data); void cinnamon_global_run_at_leisure (CinnamonGlobal *global, CinnamonLeisureFunction func, gpointer user_data, GDestroyNotify notify); /* Misc utilities / Cinnamon API */ void cinnamon_global_sync_pointer (CinnamonGlobal *global); GAppLaunchContext * cinnamon_global_create_app_launch_context (CinnamonGlobal *global); void cinnamon_global_notify_error (CinnamonGlobal *global, const char *msg, const char *details); void cinnamon_global_init_xdnd (CinnamonGlobal *global); void cinnamon_global_shutdown (void); void cinnamon_global_reexec_self (CinnamonGlobal *global); void cinnamon_global_segfault (CinnamonGlobal *global); G_END_DECLS #endif /* __CINNAMON_GLOBAL_H__ */ Cinnamon-2.8.6/src/cinnamon-wm.h0000664000175000017500000000401112625666430015406 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_WM_H__ #define __CINNAMON_WM_H__ #include #include G_BEGIN_DECLS typedef struct _CinnamonWM CinnamonWM; typedef struct _CinnamonWMClass CinnamonWMClass; #define CINNAMON_TYPE_WM (cinnamon_wm_get_type ()) #define CINNAMON_WM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_WM, CinnamonWM)) #define CINNAMON_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_WM, CinnamonWMClass)) #define CINNAMON_IS_WM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_WM)) #define CINNAMON_IS_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_WM)) #define CINNAMON_WM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_WM, CinnamonWMClass)) struct _CinnamonWMClass { GObjectClass parent_class; }; GType cinnamon_wm_get_type (void) G_GNUC_CONST; CinnamonWM *cinnamon_wm_new (MetaPlugin *plugin); void cinnamon_wm_completed_minimize (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_maximize (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_tile (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_unmaximize (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_map (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_destroy (CinnamonWM *wm, MetaWindowActor *actor); void cinnamon_wm_completed_switch_workspace (CinnamonWM *wm); G_END_DECLS #endif /* __CINNAMON_WM_H__ */ Cinnamon-2.8.6/src/cinnamon-extension-tool.in0000664000175000017500000001074112625666430020140 0ustar fabiofabio#!@PYTHON@ # -*- mode: Python; indent-tabs-mode: nil; -*- import os import re import socket import subprocess import sys import optparse import tempfile try: import json except ImportError: try: import simplejson as json except ImportError: print 'The Python simplejson module is required' sys.exit(1) parser = optparse.OptionParser() parser.add_option("", "--create-extension", action="store_true", help="Create a new Cinnamon extension") options, args = parser.parse_args() if args: parser.print_usage() sys.exit(1) SAMPLE_EXTENSION_FILES = { "extension.js": """ const St = imports.gi.St; const Main = imports.ui.main; const Tweener = imports.ui.tweener; let text, button; function _hideHello() { Main.uiGroup.remove_actor(text); text = null; } function _showHello() { if (!text) { text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" }); Main.uiGroup.add_actor(text); } text.opacity = 255; let monitor = Main.layoutManager.primaryMonitor; text.set_position(Math.floor(monitor.width / 2 - text.width / 2), Math.floor(monitor.height / 2 - text.height / 2)); Tweener.addTween(text, { opacity: 0, time: 2, transition: 'easeOutQuad', onComplete: _hideHello }); } function init() { button = new St.Bin({ style_class: 'panel-button', reactive: true, can_focus: true, x_fill: true, y_fill: false, track_hover: true }); let icon = new St.Icon({ icon_name: 'system-run', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' }); button.set_child(icon); button.connect('button-press-event', _showHello); } function enable() { Main.panel._rightBox.insert_actor(button, 0); } function disable() { Main.panel._rightBox.remove_actor(button); } """, "stylesheet.css": """ .helloworld-label { font-size: 36px; font-weight: bold; color: #ffffff; background-color: rgba(10,10,10,0.7); border-radius: 5px; padding: .5em; } """, } if options.create_extension: print print '''Name should be a very short (ideally descriptive) string. Examples are: "Click To Focus", "Adblock", "Cinnamon Window Shrinker". ''' name = raw_input('Name: ').strip() print print '''Description is a single-sentence explanation of what your extension does. Examples are: "Make windows visible on click", "Block advertisement popups" "Animate windows shrinking on minimize" ''' description = raw_input('Description: ').strip() underifier = re.compile('[^A-Za-z]') sample_uuid = underifier.sub('_', name) hostname = socket.gethostname() sample_uuid = sample_uuid + '@' + hostname print print '''Uuid is a globally-unique identifier for your extension. This should be in the format of an email address (foo.bar@extensions.example.com), but need not be an actual email address, though it's a good idea to base the uuid on your email address. For example, if your email address is janedoe@example.com, you might use an extension title clicktofocus@janedoe.example.com.''' uuid = raw_input('Uuid [%s]: ' % (sample_uuid, )).strip() if uuid == '': uuid = sample_uuid extension_path = os.path.join(os.path.expanduser('~/.local'), 'share', 'cinnamon', 'extensions', uuid) if os.path.exists(extension_path): print "Extension path %r already exists" % (extension_path, ) sys.exit(0) os.makedirs(extension_path) meta = { 'name': name, 'description': description, 'uuid': uuid, 'cinnamon-version': ['@VERSION@'] } f = open(os.path.join(extension_path, 'metadata.json'), 'w') try: json.dump(meta, f) except AttributeError: # For Python versions older than 2.6, try using the json-py module f.write(json.write(meta) + '\n') f.close() for filename, contents in SAMPLE_EXTENSION_FILES.iteritems(): path = os.path.join(extension_path, filename) f = open(path, 'w') f.write(contents) f.close() print "Created extension in %r" % (extension_path, ) extensionjs_path = os.path.join(extension_path, 'extension.js') subprocess.Popen(['xdg-open', extensionjs_path]) sys.exit(0) Cinnamon-2.8.6/src/cinnamon-app-system.h0000664000175000017500000001036112625666430017072 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_APP_SYSTEM_H__ #define __CINNAMON_APP_SYSTEM_H__ #include #include #include #define GMENU_I_KNOW_THIS_IS_UNSTABLE #include #include "cinnamon-app.h" #define CINNAMON_TYPE_APP_SYSTEM (cinnamon_app_system_get_type ()) #define CINNAMON_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_APP_SYSTEM, CinnamonAppSystem)) #define CINNAMON_APP_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_APP_SYSTEM, CinnamonAppSystemClass)) #define CINNAMON_IS_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_APP_SYSTEM)) #define CINNAMON_IS_APP_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_APP_SYSTEM)) #define CINNAMON_APP_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_APP_SYSTEM, CinnamonAppSystemClass)) typedef struct _CinnamonAppSystem CinnamonAppSystem; typedef struct _CinnamonAppSystemClass CinnamonAppSystemClass; typedef struct _CinnamonAppSystemPrivate CinnamonAppSystemPrivate; struct _CinnamonAppSystem { GObject parent; CinnamonAppSystemPrivate *priv; }; struct _CinnamonAppSystemClass { GObjectClass parent_class; void (*installed_changed)(CinnamonAppSystem *appsys, gpointer user_data); void (*favorites_changed)(CinnamonAppSystem *appsys, gpointer user_data); }; GType cinnamon_app_system_get_type (void) G_GNUC_CONST; CinnamonAppSystem *cinnamon_app_system_get_default (void); GMenuTree *cinnamon_app_system_get_tree (CinnamonAppSystem *system); CinnamonApp *cinnamon_app_system_lookup_app (CinnamonAppSystem *system, const char *id); CinnamonApp *cinnamon_app_system_lookup_settings_app (CinnamonAppSystem *system, const char *id); CinnamonApp *cinnamon_app_system_lookup_app_by_tree_entry (CinnamonAppSystem *system, GMenuTreeEntry *entry); CinnamonApp *cinnamon_app_system_lookup_settings_app_by_tree_entry (CinnamonAppSystem *system, GMenuTreeEntry *entry); CinnamonApp *cinnamon_app_system_lookup_app_for_path (CinnamonAppSystem *system, const char *desktop_path); CinnamonApp *cinnamon_app_system_lookup_heuristic_basename (CinnamonAppSystem *system, const char *id); CinnamonApp *cinnamon_app_system_lookup_startup_wmclass (CinnamonAppSystem *system, const char *wmclass); CinnamonApp *cinnamon_app_system_lookup_desktop_wmclass (CinnamonAppSystem *system, const char *wmclass); GSList *cinnamon_app_system_get_all (CinnamonAppSystem *system); GSList *cinnamon_app_system_get_running (CinnamonAppSystem *self); GSList *cinnamon_app_system_initial_search (CinnamonAppSystem *system, GSList *terms); GSList *cinnamon_app_system_subsearch (CinnamonAppSystem *system, GSList *previous_results, GSList *terms); GMenuTree *cinnamon_app_system_get_settings_tree (CinnamonAppSystem *system); GSList *cinnamon_app_system_search_settings (CinnamonAppSystem *system, GSList *terms); CinnamonApp *cinnamon_app_system_lookup_setting (CinnamonAppSystem *system, const char *id); #endif /* __CINNAMON_APP_SYSTEM_H__ */ Cinnamon-2.8.6/src/tray/0000775000175000017500000000000012625666430013775 5ustar fabiofabioCinnamon-2.8.6/src/tray/na-marshal.list0000664000175000017500000000010112625666430016705 0ustar fabiofabioVOID:OBJECT,OBJECT VOID:OBJECT,STRING,LONG,LONG VOID:OBJECT,LONG Cinnamon-2.8.6/src/tray/na-tray-manager.h0000664000175000017500000000734612625666430017143 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* na-tray-manager.h * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2003-2006 Vincent Untz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, * Boston, MA 02110-1335, USA. * * Used to be: eggtraymanager.h */ #ifndef __NA_TRAY_MANAGER_H__ #define __NA_TRAY_MANAGER_H__ #ifdef GDK_WINDOWING_X11 #include #endif #include #include "na-tray-child.h" G_BEGIN_DECLS #define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ()) #define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager)) #define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) #define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER)) #define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER)) #define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) typedef struct _NaTrayManager NaTrayManager; typedef struct _NaTrayManagerClass NaTrayManagerClass; struct _NaTrayManager { GObject parent_instance; #ifdef GDK_WINDOWING_X11 GdkAtom selection_atom; Atom opcode_atom; Atom message_data_atom; #endif GtkWidget *invisible; GdkScreen *screen; GtkOrientation orientation; GdkColor fg; GdkColor error; GdkColor warning; GdkColor success; GList *messages; GHashTable *socket_table; gint scale; }; struct _NaTrayManagerClass { GObjectClass parent_class; void (* tray_icon_added) (NaTrayManager *manager, NaTrayChild *child); void (* tray_icon_removed) (NaTrayManager *manager, NaTrayChild *child); void (* message_sent) (NaTrayManager *manager, NaTrayChild *child, const gchar *message, glong id, glong timeout); void (* message_cancelled) (NaTrayManager *manager, NaTrayChild *child, glong id); void (* lost_selection) (NaTrayManager *manager); }; GType na_tray_manager_get_type (void); gboolean na_tray_manager_check_running (GdkScreen *screen); NaTrayManager *na_tray_manager_new (void); gboolean na_tray_manager_manage_screen (NaTrayManager *manager, GdkScreen *screen); void na_tray_manager_set_orientation (NaTrayManager *manager, GtkOrientation orientation); GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager); void na_tray_manager_set_colors (NaTrayManager *manager, GdkColor *fg, GdkColor *error, GdkColor *warning, GdkColor *success); void na_tray_manager_set_scale (NaTrayManager *manager, gint scale); G_END_DECLS #endif /* __NA_TRAY_MANAGER_H__ */ Cinnamon-2.8.6/src/tray/na-tray-child.c0000664000175000017500000003636312625666430016610 0ustar fabiofabio/* na-tray-child.c * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2003-2006 Vincent Untz * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, * Boston, MA 02110-1335, USA. */ #include #include #include "na-tray-child.h" #include #include #include G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET) static void na_tray_child_finalize (GObject *object) { G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object); } static void na_tray_child_realize (GtkWidget *widget) { NaTrayChild *child = NA_TRAY_CHILD (widget); GdkVisual *visual = gtk_widget_get_visual (widget); GdkWindow *window; GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget); window = gtk_widget_get_window (widget); if (child->has_alpha) { /* We have real transparency with an ARGB visual and the Composite * extension. */ /* Set a transparent background */ cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0); gdk_window_set_background_pattern (window, transparent); gdk_window_set_composited (window, TRUE); cairo_pattern_destroy (transparent); child->parent_relative_bg = FALSE; } else if (visual == gdk_window_get_visual (gdk_window_get_parent (window))) { /* Otherwise, if the visual matches the visual of the parent window, we * can use a parent-relative background and fake transparency. */ gdk_window_set_background_pattern (window, NULL); child->parent_relative_bg = TRUE; } else { /* Nothing to do; the icon will sit on top of an ugly gray box */ child->parent_relative_bg = FALSE; } gdk_window_set_composited (window, child->composited); gtk_widget_set_app_paintable (GTK_WIDGET (child), child->parent_relative_bg || child->has_alpha); /* Double-buffering will interfere with the parent-relative-background fake * transparency, since the double-buffer code doesn't know how to fill in the * background of the double-buffer correctly. */ gtk_widget_set_double_buffered (GTK_WIDGET (child), child->parent_relative_bg); } static void na_tray_child_style_set (GtkWidget *widget, GtkStyle *previous_style) { /* The default handler resets the background according to the new style. * We either use a transparent background or a parent-relative background * and ignore the style background. So, just don't chain up. */ } #define SIZE_BASELINE 20 static void na_tray_child_get_preferred_height (GtkWidget *widget, gint *min_size, gint *natural_size) { gint scaled_size = SIZE_BASELINE * NA_TRAY_CHILD (widget)->scale; *min_size = scaled_size; *natural_size = scaled_size; } static void na_tray_child_get_preferred_width (GtkWidget *widget, gint *min_size, gint *natural_size) { gint scaled_size = SIZE_BASELINE * NA_TRAY_CHILD (widget)->scale; *min_size = scaled_size; *natural_size = scaled_size; } static void na_tray_child_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { NaTrayChild *child = NA_TRAY_CHILD (widget); GtkAllocation widget_allocation; gboolean moved, resized; gtk_widget_get_allocation (widget, &widget_allocation); moved = (allocation->x != widget_allocation.x || allocation->y != widget_allocation.y); resized = (allocation->width != widget_allocation.width || allocation->height != widget_allocation.height); /* When we are allocating the widget while mapped we need special handling * for both real and fake transparency. * * Real transparency: we need to invalidate and trigger a redraw of the old * and new areas. (GDK really should handle this for us, but doesn't as of * GTK+-2.14) * * Fake transparency: if the widget moved, we need to force the contents to * be redrawn with the new offset for the parent-relative background. */ if ((moved || resized) && gtk_widget_get_mapped (widget)) { if (na_tray_child_has_alpha (child)) gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)), &widget_allocation, FALSE); } GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget, allocation); if ((moved || resized) && gtk_widget_get_mapped (widget)) { if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget))) gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)), &widget_allocation, FALSE); else if (moved && child->parent_relative_bg) na_tray_child_force_redraw (child); } } /* The plug window should completely occupy the area of the child, so we won't * get a draw event. But in case we do (the plug unmaps itself, say), this * draw handler draws with real or fake transparency. */ static gboolean na_tray_child_draw (GtkWidget *widget, cairo_t *cr) { NaTrayChild *child = NA_TRAY_CHILD (widget); if (na_tray_child_has_alpha (child)) { /* Clear to transparent */ cairo_set_source_rgba (cr, 0, 0, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); } else if (child->parent_relative_bg) { GdkWindow *window; cairo_surface_t *target; GdkRectangle clip_rect; window = gtk_widget_get_window (widget); target = cairo_get_group_target (cr); gdk_cairo_get_clip_rectangle (cr, &clip_rect); /* Clear to parent-relative pixmap * We need to use direct X access here because GDK doesn't know about * the parent relative pixmap. */ cairo_surface_flush (target); XClearArea (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height, False); cairo_surface_mark_dirty_rectangle (target, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height); } return FALSE; } static void na_tray_child_init (NaTrayChild *child) { child->scale = 1; } static void na_tray_child_class_init (NaTrayChildClass *klass) { GObjectClass *gobject_class; GtkWidgetClass *widget_class; gobject_class = (GObjectClass *)klass; widget_class = (GtkWidgetClass *)klass; gobject_class->finalize = na_tray_child_finalize; widget_class->style_set = na_tray_child_style_set; widget_class->realize = na_tray_child_realize; widget_class->size_allocate = na_tray_child_size_allocate; widget_class->draw = na_tray_child_draw; widget_class->get_preferred_height = na_tray_child_get_preferred_height; widget_class->get_preferred_width = na_tray_child_get_preferred_width; } GtkWidget * na_tray_child_new (GdkScreen *screen, Window icon_window, gint scale) { XWindowAttributes window_attributes; Display *xdisplay; NaTrayChild *child; GdkVisual *visual; gboolean visual_has_alpha; int red_prec, green_prec, blue_prec, depth; int result; g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); g_return_val_if_fail (icon_window != None, NULL); xdisplay = GDK_SCREEN_XDISPLAY (screen); /* We need to determine the visual of the window we are embedding and create * the socket in the same visual. */ gdk_error_trap_push (); result = XGetWindowAttributes (xdisplay, icon_window, &window_attributes); gdk_error_trap_pop_ignored (); if (!result) /* Window already gone */ return NULL; visual = gdk_x11_screen_lookup_visual (screen, window_attributes.visual->visualid); if (!visual) /* Icon window is on another screen? */ return NULL; child = g_object_new (NA_TYPE_TRAY_CHILD, NULL); child->icon_window = icon_window; child->scale = scale; gtk_widget_set_visual (GTK_WIDGET (child), visual); /* We have alpha if the visual has something other than red, green, * and blue */ gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec); gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec); gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec); depth = gdk_visual_get_depth (visual); visual_has_alpha = red_prec + blue_prec + green_prec < depth; child->has_alpha = (visual_has_alpha && gdk_display_supports_composite (gdk_screen_get_display (screen))); child->composited = child->has_alpha; return GTK_WIDGET (child); } char * na_tray_child_get_title (NaTrayChild *child) { char *retval = NULL; GdkDisplay *display; Atom utf8_string, atom, type; int result; int format; gulong nitems; gulong bytes_after; gchar *val; g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL); display = gtk_widget_get_display (GTK_WIDGET (child)); utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"); atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"); gdk_error_trap_push (); result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), child->icon_window, atom, 0, G_MAXLONG, False, utf8_string, &type, &format, &nitems, &bytes_after, (guchar **)&val); if (gdk_error_trap_pop () || result != Success) return NULL; if (type != utf8_string || format != 8 || nitems == 0) { if (val) XFree (val); return NULL; } if (!g_utf8_validate (val, nitems, NULL)) { XFree (val); return NULL; } retval = g_strndup (val, nitems); XFree (val); return retval; } /** * na_tray_child_has_alpha; * @child: a #NaTrayChild * * Checks if the child has an ARGB visual and real alpha transparence. * (as opposed to faked alpha transparency with an parent-relative * background) * * Return value: %TRUE if the child has an alpha transparency */ gboolean na_tray_child_has_alpha (NaTrayChild *child) { g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE); return child->has_alpha; } /** * na_tray_child_set_composited; * @child: a #NaTrayChild * @composited: %TRUE if the child's window should be redirected * * Sets whether the #GdkWindow of the child should be set redirected * using gdk_window_set_composited(). By default this is based off of * na_tray_child_has_alpha(), but it may be useful to override it in * certain circumstances; for example, if the #NaTrayChild is added * to a parent window and that parent window is composited against the * background. */ void na_tray_child_set_composited (NaTrayChild *child, gboolean composited) { g_return_if_fail (NA_IS_TRAY_CHILD (child)); if (child->composited == composited) return; child->composited = composited; if (gtk_widget_get_realized (GTK_WIDGET (child))) gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)), composited); } /* If we are faking transparency with a window-relative background, force a * redraw of the icon. This should be called if the background changes or if * the child is shifted with respect to the background. */ void na_tray_child_force_redraw (NaTrayChild *child) { GtkWidget *widget = GTK_WIDGET (child); if (gtk_widget_get_mapped (widget) && child->parent_relative_bg) { #if 1 /* Sending an ExposeEvent might cause redraw problems if the * icon is expecting the server to clear-to-background before * the redraw. It should be ok for GtkStatusIcon or EggTrayIcon. */ Display *xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); XEvent xev; GdkWindow *plug_window; GtkAllocation allocation; plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child)); gtk_widget_get_allocation (widget, &allocation); xev.xexpose.type = Expose; xev.xexpose.window = GDK_WINDOW_XID (plug_window); xev.xexpose.x = 0; xev.xexpose.y = 0; xev.xexpose.width = allocation.width; xev.xexpose.height = allocation.height; xev.xexpose.count = 0; gdk_error_trap_push (); XSendEvent (xdisplay, xev.xexpose.window, False, ExposureMask, &xev); gdk_error_trap_pop_ignored (); #else /* Hiding and showing is the safe way to do it, but can result in more * flickering. */ gdk_window_hide (widget->window); gdk_window_show (widget->window); #endif } } /* from libwnck/xutils.c, comes as LGPLv2+ */ static char * latin1_to_utf8 (const char *latin1) { GString *str; const char *p; str = g_string_new (NULL); p = latin1; while (*p) { g_string_append_unichar (str, (gunichar) *p); ++p; } return g_string_free (str, FALSE); } /* derived from libwnck/xutils.c, comes as LGPLv2+ */ static void _get_wmclass (Display *xdisplay, Window xwindow, char **res_class, char **res_name) { XClassHint ch; ch.res_name = NULL; ch.res_class = NULL; gdk_error_trap_push (); XGetClassHint (xdisplay, xwindow, &ch); gdk_error_trap_pop_ignored (); if (res_class) *res_class = NULL; if (res_name) *res_name = NULL; if (ch.res_name) { if (res_name) *res_name = latin1_to_utf8 (ch.res_name); XFree (ch.res_name); } if (ch.res_class) { if (res_class) *res_class = latin1_to_utf8 (ch.res_class); XFree (ch.res_class); } } /** * na_tray_child_get_wm_class; * @child: a #NaTrayChild * @res_name: return location for a string containing the application name of * @child, or %NULL * @res_class: return location for a string containing the application class of * @child, or %NULL * * Fetches the resource associated with @child. */ void na_tray_child_get_wm_class (NaTrayChild *child, char **res_name, char **res_class) { GdkDisplay *display; g_return_if_fail (NA_IS_TRAY_CHILD (child)); display = gtk_widget_get_display (GTK_WIDGET (child)); _get_wmclass (GDK_DISPLAY_XDISPLAY (display), child->icon_window, res_class, res_name); } Cinnamon-2.8.6/src/tray/na-tray-manager.c0000664000175000017500000006356312625666430017141 0ustar fabiofabio/* na-tray-manager.c * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2003-2006 Vincent Untz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, * Boston, MA 02110-1335, USA. * * Used to be: eggtraymanager.c */ #include #include #include #include "na-tray-manager.h" #if defined (GDK_WINDOWING_X11) #include #include #elif defined (GDK_WINDOWING_WIN32) #include #endif #include #include "na-marshal.h" /* Signals */ enum { TRAY_ICON_ADDED, TRAY_ICON_REMOVED, MESSAGE_SENT, MESSAGE_CANCELLED, LOST_SELECTION, LAST_SIGNAL }; enum { PROP_0, PROP_ORIENTATION }; typedef struct { long id, len; long remaining_len; long timeout; char *str; #ifdef GDK_WINDOWING_X11 Window window; #endif } PendingMessage; static guint manager_signals[LAST_SIGNAL]; #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 #define SYSTEM_TRAY_ORIENTATION_HORZ 0 #define SYSTEM_TRAY_ORIENTATION_VERT 1 #ifdef GDK_WINDOWING_X11 static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen); #endif static void na_tray_manager_finalize (GObject *object); static void na_tray_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void na_tray_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void na_tray_manager_unmanage (NaTrayManager *manager); G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT) static void na_tray_manager_init (NaTrayManager *manager) { manager->invisible = NULL; manager->socket_table = g_hash_table_new (NULL, NULL); manager->scale = 1; manager->fg.red = 0; manager->fg.green = 0; manager->fg.blue = 0; manager->error.red = 0xffff; manager->error.green = 0; manager->error.blue = 0; manager->warning.red = 0xffff; manager->warning.green = 0xffff; manager->warning.blue = 0; manager->success.red = 0; manager->success.green = 0xffff; manager->success.blue = 0; } static void na_tray_manager_class_init (NaTrayManagerClass *klass) { GObjectClass *gobject_class; gobject_class = (GObjectClass *)klass; gobject_class->finalize = na_tray_manager_finalize; gobject_class->set_property = na_tray_manager_set_property; gobject_class->get_property = na_tray_manager_get_property; g_object_class_install_property (gobject_class, PROP_ORIENTATION, g_param_spec_enum ("orientation", "orientation", "orientation", GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); manager_signals[TRAY_ICON_ADDED] = g_signal_new ("tray_icon_added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_SOCKET); manager_signals[TRAY_ICON_REMOVED] = g_signal_new ("tray_icon_removed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_SOCKET); manager_signals[MESSAGE_SENT] = g_signal_new ("message_sent", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NaTrayManagerClass, message_sent), NULL, NULL, _na_marshal_VOID__OBJECT_STRING_LONG_LONG, G_TYPE_NONE, 4, GTK_TYPE_SOCKET, G_TYPE_STRING, G_TYPE_LONG, G_TYPE_LONG); manager_signals[MESSAGE_CANCELLED] = g_signal_new ("message_cancelled", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled), NULL, NULL, _na_marshal_VOID__OBJECT_LONG, G_TYPE_NONE, 2, GTK_TYPE_SOCKET, G_TYPE_LONG); manager_signals[LOST_SELECTION] = g_signal_new ("lost_selection", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); #if defined (GDK_WINDOWING_X11) /* Nothing */ #elif defined (GDK_WINDOWING_WIN32) g_warning ("Port NaTrayManager to Win32"); #else g_warning ("Port NaTrayManager to this GTK+ backend"); #endif } static void na_tray_manager_finalize (GObject *object) { NaTrayManager *manager; manager = NA_TRAY_MANAGER (object); na_tray_manager_unmanage (manager); g_list_free (manager->messages); g_hash_table_destroy (manager->socket_table); G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object); } static void na_tray_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NaTrayManager *manager = NA_TRAY_MANAGER (object); switch (prop_id) { case PROP_ORIENTATION: na_tray_manager_set_orientation (manager, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void na_tray_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NaTrayManager *manager = NA_TRAY_MANAGER (object); switch (prop_id) { case PROP_ORIENTATION: g_value_set_enum (value, manager->orientation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } NaTrayManager * na_tray_manager_new (void) { NaTrayManager *manager; manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL); return manager; } #ifdef GDK_WINDOWING_X11 static gboolean na_tray_manager_plug_removed (GtkSocket *socket, NaTrayManager *manager) { NaTrayChild *child = NA_TRAY_CHILD (socket); g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (child->icon_window)); g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); /* This destroys the socket. */ return FALSE; } typedef struct { NaTrayManager *manager; Window window; GtkWidget *child; } TrayAddPacket; static gboolean wait_after_added (TrayAddPacket *packet) { /* If the child wasn't attached, then destroy it */ NaTrayManager *manager = NA_TRAY_MANAGER (packet->manager); GtkWidget *child = GTK_WIDGET (packet->child); if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child)))) { gtk_widget_destroy (child); goto out; } g_signal_connect (child, "plug_removed", G_CALLBACK (na_tray_manager_plug_removed), manager); gtk_socket_add_id (GTK_SOCKET (child), packet->window); if (!gtk_socket_get_plug_window (GTK_SOCKET (child))) { /* Embedding failed, we won't get a plug-removed signal */ /* This signal destroys the socket */ g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); goto out; } g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (packet->window), child); gtk_widget_show (child); out: g_object_unref (child); /* Free it since we tell the caller to stop calling us by returning FALSE */ g_free (packet); return FALSE; } static void na_tray_manager_handle_dock_request (NaTrayManager *manager, XClientMessageEvent *xevent) { Window icon_window = xevent->data.l[2]; GtkWidget *child; if (g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (icon_window))) { /* We already got this notification earlier, ignore this one */ return; } child = na_tray_child_new (manager->screen, icon_window, manager->scale); if (child == NULL) /* already gone or other error */ return; g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, child); TrayAddPacket *packet = g_new0 (TrayAddPacket, 1); packet->child = g_object_ref (child); packet->window = icon_window; packet->manager = manager; g_timeout_add (250, (GSourceFunc) wait_after_added, packet); } static void pending_message_free (PendingMessage *message) { g_free (message->str); g_free (message); } static void na_tray_manager_handle_message_data (NaTrayManager *manager, XClientMessageEvent *xevent) { GList *p; int len; /* Try to see if we can find the pending message in the list */ for (p = manager->messages; p; p = p->next) { PendingMessage *msg = p->data; if (xevent->window == msg->window) { /* Append the message */ len = MIN (msg->remaining_len, 20); memcpy ((msg->str + msg->len - msg->remaining_len), &xevent->data, len); msg->remaining_len -= len; if (msg->remaining_len == 0) { GtkSocket *socket; socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (msg->window)); if (socket) g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, socket, msg->str, msg->id, msg->timeout); pending_message_free (msg); manager->messages = g_list_remove_link (manager->messages, p); g_list_free_1 (p); } break; } } } static void na_tray_manager_handle_begin_message (NaTrayManager *manager, XClientMessageEvent *xevent) { GtkSocket *socket; GList *p; PendingMessage *msg; long timeout; long len; long id; socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->window)); /* we don't know about this tray icon, so ignore the message */ if (!socket) return; timeout = xevent->data.l[2]; len = xevent->data.l[3]; id = xevent->data.l[4]; /* Check if the same message is already in the queue and remove it if so */ for (p = manager->messages; p; p = p->next) { PendingMessage *pmsg = p->data; if (xevent->window == pmsg->window && id == pmsg->id) { /* Hmm, we found it, now remove it */ pending_message_free (pmsg); manager->messages = g_list_remove_link (manager->messages, p); g_list_free_1 (p); break; } } if (len == 0) { g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, socket, "", id, timeout); } else { /* Now add the new message to the queue */ msg = g_new0 (PendingMessage, 1); msg->window = xevent->window; msg->timeout = timeout; msg->len = len; msg->id = id; msg->remaining_len = msg->len; msg->str = g_malloc (msg->len + 1); msg->str[msg->len] = '\0'; manager->messages = g_list_prepend (manager->messages, msg); } } static void na_tray_manager_handle_cancel_message (NaTrayManager *manager, XClientMessageEvent *xevent) { GList *p; GtkSocket *socket; long id; id = xevent->data.l[2]; /* Check if the message is in the queue and remove it if so */ for (p = manager->messages; p; p = p->next) { PendingMessage *msg = p->data; if (xevent->window == msg->window && id == msg->id) { pending_message_free (msg); manager->messages = g_list_remove_link (manager->messages, p); g_list_free_1 (p); break; } } socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->window)); if (socket) { g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, socket, xevent->data.l[2]); } } static GdkFilterReturn na_tray_manager_window_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) { XEvent *xevent = (GdkXEvent *)xev; NaTrayManager *manager = data; if (xevent->type == ClientMessage) { /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_REQUEST_DOCK */ if (xevent->xclient.message_type == manager->opcode_atom && xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { na_tray_manager_handle_dock_request (manager, (XClientMessageEvent *) xevent); return GDK_FILTER_REMOVE; } /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */ else if (xevent->xclient.message_type == manager->opcode_atom && xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE) { na_tray_manager_handle_begin_message (manager, (XClientMessageEvent *) event); return GDK_FILTER_REMOVE; } /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */ else if (xevent->xclient.message_type == manager->opcode_atom && xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE) { na_tray_manager_handle_cancel_message (manager, (XClientMessageEvent *) event); return GDK_FILTER_REMOVE; } /* _NET_SYSTEM_TRAY_MESSAGE_DATA */ else if (xevent->xclient.message_type == manager->message_data_atom) { na_tray_manager_handle_message_data (manager, (XClientMessageEvent *) event); return GDK_FILTER_REMOVE; } } else if (xevent->type == SelectionClear) { g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); na_tray_manager_unmanage (manager); } return GDK_FILTER_CONTINUE; } #if 0 //FIXME investigate why this doesn't work static gboolean na_tray_manager_selection_clear_event (GtkWidget *widget, GdkEventSelection *event, NaTrayManager *manager) { g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); na_tray_manager_unmanage (manager); return FALSE; } #endif #endif static void na_tray_manager_unmanage (NaTrayManager *manager) { #ifdef GDK_WINDOWING_X11 GdkDisplay *display; guint32 timestamp; GtkWidget *invisible; GdkWindow *window; if (manager->invisible == NULL) return; invisible = manager->invisible; window = gtk_widget_get_window (invisible); g_assert (GTK_IS_INVISIBLE (invisible)); g_assert (gtk_widget_get_realized (invisible)); g_assert (GDK_IS_WINDOW (window)); display = gtk_widget_get_display (invisible); if (gdk_selection_owner_get_for_display (display, manager->selection_atom) == window) { timestamp = gdk_x11_get_server_time (window); gdk_selection_owner_set_for_display (display, NULL, manager->selection_atom, timestamp, TRUE); } gdk_window_remove_filter (window, na_tray_manager_window_filter, manager); manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ gtk_widget_destroy (invisible); g_object_unref (G_OBJECT (invisible)); #endif } static void na_tray_manager_set_orientation_property (NaTrayManager *manager) { #ifdef GDK_WINDOWING_X11 GdkWindow *window; GdkDisplay *display; Atom orientation_atom; gulong data[1]; g_return_if_fail (manager->invisible != NULL); window = gtk_widget_get_window (manager->invisible); g_return_if_fail (window != NULL); display = gtk_widget_get_display (manager->invisible); orientation_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_SYSTEM_TRAY_ORIENTATION"); data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ? SYSTEM_TRAY_ORIENTATION_HORZ : SYSTEM_TRAY_ORIENTATION_VERT; XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), orientation_atom, XA_CARDINAL, 32, PropModeReplace, (guchar *) &data, 1); #endif } static void na_tray_manager_set_visual_property (NaTrayManager *manager) { #ifdef GDK_WINDOWING_X11 GdkWindow *window; GdkDisplay *display; Visual *xvisual; Atom visual_atom; gulong data[1]; g_return_if_fail (manager->invisible != NULL); window = gtk_widget_get_window (manager->invisible); g_return_if_fail (window != NULL); /* The visual property is a hint to the tray icons as to what visual they * should use for their windows. If the X server has RGBA colormaps, then * we tell the tray icons to use a RGBA colormap and we'll composite the * icon onto its parents with real transparency. Otherwise, we just tell * the icon to use our colormap, and we'll do some hacks with parent * relative backgrounds to simulate transparency. */ display = gtk_widget_get_display (manager->invisible); visual_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_SYSTEM_TRAY_VISUAL"); if (gdk_screen_get_rgba_visual (manager->screen) != NULL && gdk_display_supports_composite (display)) xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen)); else { /* We actually want the visual of the tray where the icons will * be embedded. In almost all cases, this will be the same as the visual * of the screen. */ xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen)); } data[0] = XVisualIDFromVisual (xvisual); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), visual_atom, XA_VISUALID, 32, PropModeReplace, (guchar *) &data, 1); #endif } static void na_tray_manager_set_colors_property (NaTrayManager *manager) { #ifdef GDK_WINDOWING_X11 GdkWindow *window; GdkDisplay *display; Atom atom; gulong data[12]; g_return_if_fail (manager->invisible != NULL); window = gtk_widget_get_window (manager->invisible); g_return_if_fail (window != NULL); display = gtk_widget_get_display (manager->invisible); atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_SYSTEM_TRAY_COLORS"); data[0] = manager->fg.red; data[1] = manager->fg.green; data[2] = manager->fg.blue; data[3] = manager->error.red; data[4] = manager->error.green; data[5] = manager->error.blue; data[6] = manager->warning.red; data[7] = manager->warning.green; data[8] = manager->warning.blue; data[9] = manager->success.red; data[10] = manager->success.green; data[11] = manager->success.blue; XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), atom, XA_CARDINAL, 32, PropModeReplace, (guchar *) &data, 12); #endif } #ifdef GDK_WINDOWING_X11 static gboolean na_tray_manager_manage_screen_x11 (NaTrayManager *manager, GdkScreen *screen) { GdkDisplay *display; Screen *xscreen; GtkWidget *invisible; GdkWindow *window; char *selection_atom_name; guint32 timestamp; g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE); g_return_val_if_fail (manager->screen == NULL, FALSE); /* If there's already a manager running on the screen * we can't create another one. */ #if 0 if (na_tray_manager_check_running_screen_x11 (screen)) return FALSE; #endif manager->screen = screen; display = gdk_screen_get_display (screen); xscreen = GDK_SCREEN_XSCREEN (screen); invisible = gtk_invisible_new_for_screen (screen); gtk_widget_realize (invisible); gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", gdk_screen_get_number (screen)); manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE); g_free (selection_atom_name); manager->invisible = invisible; g_object_ref (G_OBJECT (manager->invisible)); na_tray_manager_set_orientation_property (manager); na_tray_manager_set_visual_property (manager); na_tray_manager_set_colors_property (manager); window = gtk_widget_get_window (invisible); timestamp = gdk_x11_get_server_time (window); /* Check if we could set the selection owner successfully */ if (gdk_selection_owner_set_for_display (display, window, manager->selection_atom, timestamp, TRUE)) { XClientMessageEvent xev; GdkAtom opcode_atom; GdkAtom message_data_atom; xev.type = ClientMessage; xev.window = RootWindowOfScreen (xscreen); xev.message_type = gdk_x11_get_xatom_by_name_for_display (display, "MANAGER"); xev.format = 32; xev.data.l[0] = timestamp; xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, manager->selection_atom); xev.data.l[2] = GDK_WINDOW_XID (window); xev.data.l[3] = 0; /* manager specific data */ xev.data.l[4] = 0; /* manager specific data */ XSendEvent (GDK_DISPLAY_XDISPLAY (display), RootWindowOfScreen (xscreen), False, StructureNotifyMask, (XEvent *)&xev); opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE); manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display, opcode_atom); message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA", FALSE); manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display, message_data_atom); /* Add a window filter */ #if 0 /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */ g_signal_connect (invisible, "selection-clear-event", G_CALLBACK (na_tray_manager_selection_clear_event), manager); #endif gdk_window_add_filter (window, na_tray_manager_window_filter, manager); return TRUE; } else { gtk_widget_destroy (invisible); g_object_unref (invisible); manager->invisible = NULL; manager->screen = NULL; return FALSE; } } #endif gboolean na_tray_manager_manage_screen (NaTrayManager *manager, GdkScreen *screen) { g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); g_return_val_if_fail (manager->screen == NULL, FALSE); #ifdef GDK_WINDOWING_X11 return na_tray_manager_manage_screen_x11 (manager, screen); #else return FALSE; #endif } #ifdef GDK_WINDOWING_X11 static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen) { GdkDisplay *display; Atom selection_atom; char *selection_atom_name; display = gdk_screen_get_display (screen); selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", gdk_screen_get_number (screen)); selection_atom = gdk_x11_get_xatom_by_name_for_display (display, selection_atom_name); g_free (selection_atom_name); if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), selection_atom) != None) return TRUE; else return FALSE; } #endif gboolean na_tray_manager_check_running (GdkScreen *screen) { g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); #ifdef GDK_WINDOWING_X11 return na_tray_manager_check_running_screen_x11 (screen); #else return FALSE; #endif } void na_tray_manager_set_orientation (NaTrayManager *manager, GtkOrientation orientation) { g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); if (manager->orientation != orientation) { manager->orientation = orientation; na_tray_manager_set_orientation_property (manager); g_object_notify (G_OBJECT (manager), "orientation"); } } void na_tray_manager_set_colors (NaTrayManager *manager, GdkColor *fg, GdkColor *error, GdkColor *warning, GdkColor *success) { g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); if (!gdk_color_equal (&manager->fg, fg) || !gdk_color_equal (&manager->error, error) || !gdk_color_equal (&manager->warning, warning) || !gdk_color_equal (&manager->success, success)) { manager->fg = *fg; manager->error = *error; manager->warning = *warning; manager->success = *success; na_tray_manager_set_colors_property (manager); } } void na_tray_manager_set_scale (NaTrayManager *manager, gint scale) { g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); manager->scale = scale; } GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager) { g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL); return manager->orientation; } Cinnamon-2.8.6/src/tray/na-tray-child.h0000664000175000017500000000541012625666430016602 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* na-tray-child.h * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2003-2006 Vincent Untz * Copyright (C) 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, * Boston, MA 02110-1335, USA. */ #ifndef __NA_TRAY_CHILD_H__ #define __NA_TRAY_CHILD_H__ #include #include G_BEGIN_DECLS #define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ()) #define NA_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_CHILD, NaTrayChild)) #define NA_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_CHILD, NaTrayChildClass)) #define NA_IS_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_CHILD)) #define NA_IS_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_CHILD)) #define NA_TRAY_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_CHILD, NaTrayChildClass)) typedef struct _NaTrayChild NaTrayChild; typedef struct _NaTrayChildClass NaTrayChildClass; typedef struct _NaTrayChildChild NaTrayChildChild; struct _NaTrayChild { GtkSocket parent_instance; Window icon_window; guint has_alpha : 1; guint composited : 1; guint parent_relative_bg : 1; gint scale; }; struct _NaTrayChildClass { GtkSocketClass parent_class; }; GType na_tray_child_get_type (void); GtkWidget *na_tray_child_new (GdkScreen *screen, Window icon_window, gint scale); char *na_tray_child_get_title (NaTrayChild *child); gboolean na_tray_child_has_alpha (NaTrayChild *child); void na_tray_child_set_composited (NaTrayChild *child, gboolean composited); void na_tray_child_force_redraw (NaTrayChild *child); void na_tray_child_get_wm_class (NaTrayChild *child, char **res_name, char **res_class); G_END_DECLS #endif /* __NA_TRAY_CHILD_H__ */ Cinnamon-2.8.6/src/cinnamon-global-private.h0000664000175000017500000000125112625666430017676 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_GLOBAL_PRIVATE_H__ #define __CINNAMON_GLOBAL_PRIVATE_H__ #include "cinnamon-global.h" #include void _cinnamon_global_init (const char *first_property_name, ...); void _cinnamon_global_set_plugin (CinnamonGlobal *global, MetaPlugin *plugin); GjsContext *_cinnamon_global_get_gjs_context (CinnamonGlobal *global); gboolean _cinnamon_global_check_xdnd_event (CinnamonGlobal *global, XEvent *xev); #endif /* __CINNAMON_GLOBAL_PRIVATE_H__ */ Cinnamon-2.8.6/src/cinnamon-app-system-private.h0000664000175000017500000000044212625666430020541 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_APP_SYSTEM_PRIVATE_H__ #define __CINNAMON_APP_SYSTEM_PRIVATE_H__ #include "cinnamon-app-system.h" void _cinnamon_app_system_notify_app_state_changed (CinnamonAppSystem *self, CinnamonApp *app); #endif Cinnamon-2.8.6/src/cinnamon-enum-types.c.in0000664000175000017500000000132712625666430017500 0ustar fabiofabio/*** BEGIN file-header ***/ #include "cinnamon-enum-types.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type(void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; enum_type_id = g_@type@_register_static("@EnumName@", values); } return enum_type_id; } /*** END value-tail ***/ Cinnamon-2.8.6/src/cinnamon-embedded-window-private.h0000664000175000017500000000142012625666430021472 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_EMBEDDED_WINDOW_PRIVATE_H__ #define __CINNAMON_EMBEDDED_WINDOW_PRIVATE_H__ #include "cinnamon-embedded-window.h" #include "cinnamon-gtk-embed.h" void _cinnamon_embedded_window_set_actor (CinnamonEmbeddedWindow *window, CinnamonGtkEmbed *embed); void _cinnamon_embedded_window_allocate (CinnamonEmbeddedWindow *window, int x, int y, int width, int height); void _cinnamon_embedded_window_realize (CinnamonEmbeddedWindow *window); void _cinnamon_embedded_window_unrealize (CinnamonEmbeddedWindow *window); #endif /* __CINNAMON_EMBEDDED_WINDOW_PRIVATE_H__ */ Cinnamon-2.8.6/src/cinnamon-app-system.c0000664000175000017500000006471312625666430017077 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-app-system.h" #include #include #include #include #include #include #include #include "cinnamon-app-private.h" #include "cinnamon-window-tracker-private.h" #include "cinnamon-app-system-private.h" #include "cinnamon-global.h" #include "cinnamon-util.h" #include "st.h" /* Vendor prefixes are something that can be preprended to a .desktop * file name. Undo this. */ static const char*const vendor_prefixes[] = { "gnome-", "fedora-", "mozilla-", NULL }; enum { PROP_0, }; enum { APP_STATE_CHANGED, INSTALLED_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _CinnamonAppSystemPrivate { GMenuTree *apps_tree; GHashTable *running_apps; GHashTable *id_to_app; GHashTable *startup_wm_class_to_app; GSList *known_vendor_prefixes; GMenuTree *settings_tree; GHashTable *setting_id_to_app; }; static void cinnamon_app_system_finalize (GObject *object); static void on_apps_tree_changed_cb (GMenuTree *tree, gpointer user_data); gchar *strip_extension (gchar *wm_class); gboolean case_insensitive_search (const char *key, const char *value, gpointer user_data); G_DEFINE_TYPE(CinnamonAppSystem, cinnamon_app_system, G_TYPE_OBJECT); static void cinnamon_app_system_class_init(CinnamonAppSystemClass *klass) { GObjectClass *gobject_class = (GObjectClass *)klass; gobject_class->finalize = cinnamon_app_system_finalize; signals[APP_STATE_CHANGED] = g_signal_new ("app-state-changed", CINNAMON_TYPE_APP_SYSTEM, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CINNAMON_TYPE_APP); signals[INSTALLED_CHANGED] = g_signal_new ("installed-changed", CINNAMON_TYPE_APP_SYSTEM, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CinnamonAppSystemClass, installed_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_type_class_add_private (gobject_class, sizeof (CinnamonAppSystemPrivate)); } static void setup_merge_dir_symlink(void) { gchar *user_config = g_get_user_config_dir(); gchar *merge_path = g_build_filename (user_config, "menus", "applications-merged", NULL); GFile *merge_file = g_file_new_for_path (merge_path); g_file_make_directory_with_parents (merge_file, NULL, NULL); gchar *sym_path = g_build_filename (user_config, "menus", "cinnamon-applications-merged", NULL); GFile *sym_file = g_file_new_for_path (sym_path); if (!g_file_query_exists (sym_file, NULL)) { g_file_make_symbolic_link (sym_file, merge_path, NULL, NULL); } g_free (merge_path); g_free (sym_path); g_object_unref (merge_file); g_object_unref (sym_file); } static void cinnamon_app_system_init (CinnamonAppSystem *self) { CinnamonAppSystemPrivate *priv; self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CINNAMON_TYPE_APP_SYSTEM, CinnamonAppSystemPrivate); priv->running_apps = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) g_object_unref, NULL); priv->id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_object_unref); priv->startup_wm_class_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_object_unref); /* According to desktop spec, since our menu file is called 'cinnamon-applications', our * merged menu folders need to be called 'cinnamon-applications-merged'. We'll setup the folder * 'applications-merged' if it doesn't exist yet, and a symlink pointing to it in the * ~/.config/menus directory */ setup_merge_dir_symlink(); /* For now, we want to pick up Evince, Nemo, etc. We'll * handle NODISPLAY semantics at a higher level or investigate them * case by case. */ priv->apps_tree = gmenu_tree_new ("cinnamon-applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY); g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self); on_apps_tree_changed_cb (priv->apps_tree, self); } static void cinnamon_app_system_finalize (GObject *object) { CinnamonAppSystem *self = CINNAMON_APP_SYSTEM (object); CinnamonAppSystemPrivate *priv = self->priv; g_object_unref (priv->apps_tree); g_object_unref (priv->settings_tree); g_hash_table_destroy (priv->running_apps); g_hash_table_destroy (priv->id_to_app); g_hash_table_destroy (priv->setting_id_to_app); g_hash_table_destroy (priv->startup_wm_class_to_app); g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL); g_slist_free (priv->known_vendor_prefixes); priv->known_vendor_prefixes = NULL; G_OBJECT_CLASS (cinnamon_app_system_parent_class)->finalize (object); } static char * get_prefix_for_entry (GMenuTreeEntry *entry) { char *prefix = NULL, *file_prefix = NULL; const char *id; GFile *file; char *name; int i = 0; id = gmenu_tree_entry_get_desktop_file_id (entry); file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path (entry)); name = g_file_get_basename (file); if (!name) { g_object_unref (file); return NULL; } for (i = 0; vendor_prefixes[i]; i++) { if (g_str_has_prefix (name, vendor_prefixes[i])) { file_prefix = g_strdup (vendor_prefixes[i]); break; } } while (strcmp (name, id) != 0) { char *t; char *pname; GFile *parent = g_file_get_parent (file); if (!parent) { g_warn_if_reached (); break; } pname = g_file_get_basename (parent); if (!pname) { g_object_unref (parent); break; } if (!g_strstr_len (id, -1, pname)) { /* handle */ char *t; size_t name_len = strlen (name); size_t id_len = strlen (id); char *t_id = g_strdup (id); t_id[id_len - name_len] = '\0'; t = g_strdup(t_id); g_free (prefix); g_free (t_id); g_free (name); name = g_strdup (id); prefix = t; g_object_unref (file); file = parent; g_free (pname); g_free (file_prefix); file_prefix = NULL; break; } t = g_strconcat (pname, "-", name, NULL); g_free (name); name = t; t = g_strconcat (pname, "-", prefix, NULL); g_free (prefix); prefix = t; g_object_unref (file); file = parent; g_free (pname); } if (file) g_object_unref (file); if (strcmp (name, id) == 0) { g_free (name); if (file_prefix && !prefix) return file_prefix; if (file_prefix) { char *t = g_strconcat (prefix, "-", file_prefix, NULL); g_free (prefix); g_free (file_prefix); prefix = t; } return prefix; } g_free (name); g_free (prefix); g_free (file_prefix); g_return_val_if_reached (NULL); } static void get_flattened_entries_recurse (GMenuTreeDirectory *dir, GHashTable *entry_set) { GMenuTreeIter *iter = gmenu_tree_directory_iter (dir); GMenuTreeItemType next_type; while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID) { gpointer item = NULL; switch (next_type) { case GMENU_TREE_ITEM_ENTRY: { GMenuTreeEntry *entry; item = entry = gmenu_tree_iter_get_entry (iter); /* Key is owned by entry */ g_hash_table_replace (entry_set, (char*)gmenu_tree_entry_get_desktop_file_id (entry), gmenu_tree_item_ref (entry)); } break; case GMENU_TREE_ITEM_DIRECTORY: { item = gmenu_tree_iter_get_directory (iter); get_flattened_entries_recurse ((GMenuTreeDirectory*)item, entry_set); } break; default: break; } if (item != NULL) gmenu_tree_item_unref (item); } gmenu_tree_iter_unref (iter); } static GHashTable * get_flattened_entries_from_tree (GMenuTree *tree) { GHashTable *table; GMenuTreeDirectory *root; table = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) NULL, (GDestroyNotify) gmenu_tree_item_unref); root = gmenu_tree_get_root_directory (tree); if (root != NULL) get_flattened_entries_recurse (root, table); gmenu_tree_item_unref (root); return table; } static void on_apps_tree_changed_cb (GMenuTree *tree, gpointer user_data) { CinnamonAppSystem *self = CINNAMON_APP_SYSTEM (user_data); GError *error = NULL; GHashTable *new_apps; GHashTableIter iter; gpointer key, value; GSList *removed_apps = NULL; GSList *removed_node; g_assert (tree == self->priv->apps_tree); g_slist_foreach (self->priv->known_vendor_prefixes, (GFunc)g_free, NULL); g_slist_free (self->priv->known_vendor_prefixes); self->priv->known_vendor_prefixes = NULL; if (!gmenu_tree_load_sync (self->priv->apps_tree, &error)) { g_warning ("Failed to load apps: %s", error->message); return; } new_apps = get_flattened_entries_from_tree (self->priv->apps_tree); g_hash_table_iter_init (&iter, new_apps); while (g_hash_table_iter_next (&iter, &key, &value)) { const char *id = key; GMenuTreeEntry *entry = value; GMenuTreeEntry *old_entry; char *prefix; CinnamonApp *app; GDesktopAppInfo *info; const char *startup_wm_class; prefix = get_prefix_for_entry (entry); if (prefix != NULL && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix, (GCompareFunc)g_strcmp0)) self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes, prefix); else g_free (prefix); app = g_hash_table_lookup (self->priv->id_to_app, id); if (app != NULL) { /* We hold a reference to the original entry temporarily, * because otherwise the hash table would be referencing * potentially free'd memory until we replace it below with * the new data. */ old_entry = cinnamon_app_get_tree_entry (app); gmenu_tree_item_ref (old_entry); _cinnamon_app_set_entry (app, entry); g_object_ref (app); /* Extra ref, removed in _replace below */ } else { old_entry = NULL; app = _cinnamon_app_new (entry); } /* Note that "id" is owned by app->entry. Since we're always * setting a new entry, even if the app already exists in the * hash table we need to replace the key so that the new id * string is pointed to. */ g_hash_table_replace (self->priv->id_to_app, (char*)id, app); // if (!gmenu_tree_entry_get_is_nodisplay_recurse (entry)) // g_hash_table_replace (self->priv->visible_id_to_app, (char*)id, app); if (old_entry) { GDesktopAppInfo *old_info; const gchar *old_startup_wm_class; old_info = gmenu_tree_entry_get_app_info (old_entry); old_startup_wm_class = g_desktop_app_info_get_startup_wm_class (old_info); if (old_startup_wm_class) g_hash_table_remove (self->priv->startup_wm_class_to_app, old_startup_wm_class); } info = gmenu_tree_entry_get_app_info (entry); startup_wm_class = g_desktop_app_info_get_startup_wm_class (info); if (startup_wm_class) g_hash_table_replace (self->priv->startup_wm_class_to_app, (char*)startup_wm_class, g_object_ref (app)); if (old_entry) gmenu_tree_item_unref (old_entry); } /* Now iterate over the apps again; we need to unreference any apps * which have been removed. The JS code may still be holding a * reference; that's fine. */ g_hash_table_iter_init (&iter, self->priv->id_to_app); while (g_hash_table_iter_next (&iter, &key, &value)) { const char *id = key; if (!g_hash_table_lookup (new_apps, id)) removed_apps = g_slist_prepend (removed_apps, (char*)id); } for (removed_node = removed_apps; removed_node; removed_node = removed_node->next) { const char *id = removed_node->data; g_hash_table_remove (self->priv->id_to_app, id); } g_slist_free (removed_apps); g_hash_table_destroy (new_apps); g_signal_emit (self, signals[INSTALLED_CHANGED], 0); } /** * cinnamon_app_system_get_tree: * * Return Value: (transfer none): The #GMenuTree for apps */ GMenuTree * cinnamon_app_system_get_tree (CinnamonAppSystem *self) { return self->priv->apps_tree; } /** * cinnamon_app_system_get_settings_tree: * * Return Value: (transfer none): The #GMenuTree for apps * OBSOLETE - ONLY LEFT IN FOR COMPATIBILITY * RETURNS EMPTY GMenuTree */ GMenuTree * cinnamon_app_system_get_settings_tree (CinnamonAppSystem *self) { return self->priv->settings_tree; } /** * cinnamon_app_system_lookup_setting: * * Returns: (transfer none): Application in gnomecc.menu, or %NULL if none * OBSOLETE - ONLY LEFT IN FOR COMPATIBILITY * RETURNS NULL IF NOT FOUND IN STANDARD APPS * */ CinnamonApp * cinnamon_app_system_lookup_setting (CinnamonAppSystem *self, const char *id) { CinnamonApp *app; /* Actually defer to the main app set if there's overlap */ app = cinnamon_app_system_lookup_app (self, id); if (app != NULL) return app; return NULL; } /** * cinnamon_app_system_get_default: * * Return Value: (transfer none): The global #CinnamonAppSystem singleton */ CinnamonAppSystem * cinnamon_app_system_get_default () { static CinnamonAppSystem *instance = NULL; if (instance == NULL) instance = g_object_new (CINNAMON_TYPE_APP_SYSTEM, NULL); return instance; } gboolean case_insensitive_search (const char *key, const char *value, gpointer user_data) { char *given_id = (char *) user_data; if (g_ascii_strcasecmp(key, given_id) == 0) { return TRUE; } else { return FALSE; } } /** * cinnamon_app_system_lookup_app: * * Find a #CinnamonApp corresponding to an id. * * Return value: (transfer none): The #CinnamonApp for id, or %NULL if none */ CinnamonApp * cinnamon_app_system_lookup_app (CinnamonAppSystem *self, const char *id) { CinnamonApp *result; result = g_hash_table_lookup (self->priv->id_to_app, id); if (result == NULL) { result = g_hash_table_find (self->priv->id_to_app, (GHRFunc) case_insensitive_search, id); } return result; } /** * cinnamon_app_system_lookup_settings_app: * * Return value: (transfer none): The #CinnamonApp for id, or %NULL if none * OBSOLETE - ONLY LEFT IN FOR COMPATIBILITY * RETURNS NULL * */ CinnamonApp * cinnamon_app_system_lookup_settings_app (CinnamonAppSystem *self, const char *id) { return NULL; } /** * cinnamon_app_system_lookup_app_by_tree_entry: * @system: a #CinnamonAppSystem * @entry: a #GMenuTreeEntry * * Find a #CinnamonApp corresponding to a #GMenuTreeEntry. * * Return value: (transfer none): The #CinnamonApp for @entry, or %NULL if none */ CinnamonApp * cinnamon_app_system_lookup_app_by_tree_entry (CinnamonAppSystem *self, GMenuTreeEntry *entry) { /* If we looked up directly in ->entry_to_app, we'd lose the * override of running apps. Thus, indirect through the id. */ return cinnamon_app_system_lookup_app (self, gmenu_tree_entry_get_desktop_file_id (entry)); } /** * cinnamon_app_system_lookup_settings_app_by_tree_entry: * * Return value: (transfer none): The #CinnamonApp for @entry, or %NULL if none * OBSOLETE - ONLY LEFT IN FOR COMPATIBILITY * RETURNS NULL * */ CinnamonApp * cinnamon_app_system_lookup_settings_app_by_tree_entry (CinnamonAppSystem *self, GMenuTreeEntry *entry) { return NULL; } /** * cinnamon_app_system_lookup_app_for_path: * @system: a #CinnamonAppSystem * @desktop_path: (type utf8): UTF-8 encoded absolute file name * * Find or create a #CinnamonApp corresponding to a given absolute file * name which must be in the standard paths (XDG_DATA_DIRS). For * files outside the datadirs, this function returns %NULL. * * Return value: (transfer none): The #CinnamonApp for id, or %NULL if none */ CinnamonApp * cinnamon_app_system_lookup_app_for_path (CinnamonAppSystem *system, const char *desktop_path) { const char *basename; const char *app_path; CinnamonApp *app; basename = g_strrstr (desktop_path, "/"); if (basename) basename += 1; else basename = desktop_path; app = cinnamon_app_system_lookup_app (system, basename); if (!app) return NULL; app_path = gmenu_tree_entry_get_desktop_file_path (cinnamon_app_get_tree_entry (app)); if (strcmp (desktop_path, app_path) != 0) return NULL; return app; } /** * cinnamon_app_system_lookup_heuristic_basename: * @system: a #CinnamonAppSystem * @id: Probable application identifier * * Find a valid application corresponding to a given * heuristically determined application identifier * string, or %NULL if none. * * Returns: (transfer none): A #CinnamonApp for @name */ CinnamonApp * cinnamon_app_system_lookup_heuristic_basename (CinnamonAppSystem *system, const char *name) { CinnamonApp *result; GSList *prefix; result = cinnamon_app_system_lookup_app (system, name); if (result != NULL) return result; result = cinnamon_app_system_lookup_settings_app (system, name); if (result != NULL) return result; for (prefix = system->priv->known_vendor_prefixes; prefix; prefix = g_slist_next (prefix)) { char *tmpid = g_strconcat ((char*)prefix->data, name, NULL); result = cinnamon_app_system_lookup_app (system, tmpid); g_free (tmpid); if (result != NULL) return result; } return NULL; } gchar * strip_extension (gchar *wm_class) { char *result; if (g_str_has_suffix (wm_class, ".py") || g_str_has_suffix (wm_class, ".sh")) { result = g_strndup (wm_class, strlen (wm_class) - 3); } else { result = g_strdup (wm_class); } return result; } /** * cinnamon_app_system_lookup_desktop_wmclass: * @system: a #CinnamonAppSystem * @wmclass: (allow-none): A WM_CLASS value * * Find a valid application whose .desktop file, without the extension * and properly canonicalized, matches @wmclass. * * Returns: (transfer none): A #CinnamonApp for @wmclass */ CinnamonApp * cinnamon_app_system_lookup_desktop_wmclass (CinnamonAppSystem *system, const char *wmclass) { char *canonicalized; char *desktop_file; char *stripped_name; CinnamonApp *app; if (wmclass == NULL) return NULL; canonicalized = g_ascii_strdown (wmclass, -1); stripped_name = strip_extension(canonicalized); /* This handles "Fedora Eclipse", probably others. * Note g_strdelimit is modify-in-place. */ g_strdelimit (stripped_name, " ", '-'); desktop_file = g_strconcat (stripped_name, ".desktop", NULL); app = cinnamon_app_system_lookup_heuristic_basename (system, desktop_file); g_free (canonicalized); g_free (stripped_name); g_free (desktop_file); return app; } /** * cinnamon_app_system_lookup_startup_wmclass: * @system: a #CinnamonAppSystem * @wmclass: (allow-none): A WM_CLASS value * * Find a valid application whose .desktop file contains a * StartupWMClass entry matching @wmclass. * * Returns: (transfer none): A #CinnamonApp for @wmclass */ CinnamonApp * cinnamon_app_system_lookup_startup_wmclass (CinnamonAppSystem *system, const char *wmclass) { if (wmclass == NULL) return NULL; return g_hash_table_lookup (system->priv->startup_wm_class_to_app, wmclass); } /** * cinnamon_app_system_get_all: * @system: * * Returns: (transfer container) (element-type CinnamonApp): All installed applications */ GSList * cinnamon_app_system_get_all (CinnamonAppSystem *self) { GSList *result = NULL; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, self->priv->id_to_app); while (g_hash_table_iter_next (&iter, &key, &value)) { CinnamonApp *app = value; if (!g_desktop_app_info_get_nodisplay (cinnamon_app_get_app_info (app))) result = g_slist_prepend (result, app); } return result; } void _cinnamon_app_system_notify_app_state_changed (CinnamonAppSystem *self, CinnamonApp *app) { CinnamonAppState state = cinnamon_app_get_state (app); switch (state) { case CINNAMON_APP_STATE_RUNNING: g_hash_table_insert (self->priv->running_apps, g_object_ref (app), NULL); break; case CINNAMON_APP_STATE_STARTING: break; case CINNAMON_APP_STATE_STOPPED: g_hash_table_remove (self->priv->running_apps, app); break; } g_signal_emit (self, signals[APP_STATE_CHANGED], 0, app); } /** * cinnamon_app_system_get_running: * @self: A #CinnamonAppSystem * * Returns the set of applications which currently have at least one * open window in the given context. The returned list will be sorted * by cinnamon_app_compare(). * * Returns: (element-type CinnamonApp) (transfer container): Active applications */ GSList * cinnamon_app_system_get_running (CinnamonAppSystem *self) { gpointer key, value; GSList *ret; GHashTableIter iter; g_hash_table_iter_init (&iter, self->priv->running_apps); ret = NULL; while (g_hash_table_iter_next (&iter, &key, &value)) { CinnamonApp *app = key; ret = g_slist_prepend (ret, app); } ret = g_slist_sort (ret, (GCompareFunc)cinnamon_app_compare); return ret; } /** * normalize_terms: * @terms: (element-type utf8): Input search terms * * Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms */ static GSList * normalize_terms (GSList *terms) { GSList *normalized_terms = NULL; GSList *iter; for (iter = terms; iter; iter = iter->next) { const char *term = iter->data; normalized_terms = g_slist_prepend (normalized_terms, cinnamon_util_normalize_and_casefold (term)); } return normalized_terms; } static GSList * search_tree (CinnamonAppSystem *self, GSList *terms, GHashTable *apps) { GSList *prefix_results = NULL; GSList *substring_results = NULL; GSList *normalized_terms; GHashTableIter iter; gpointer key, value; normalized_terms = normalize_terms (terms); g_hash_table_iter_init (&iter, apps); while (g_hash_table_iter_next (&iter, &key, &value)) { const char *id = key; CinnamonApp *app = value; (void)id; _cinnamon_app_do_match (app, normalized_terms, &prefix_results, &substring_results); } g_slist_foreach (normalized_terms, (GFunc)g_free, NULL); g_slist_free (normalized_terms); return g_slist_concat (prefix_results, substring_results); } /** * cinnamon_app_system_initial_search: * @system: A #CinnamonAppSystem * @terms: (element-type utf8): List of terms, logical AND * * Search through applications for the given search terms. * * Returns: (transfer container) (element-type CinnamonApp): List of applications */ GSList * cinnamon_app_system_initial_search (CinnamonAppSystem *self, GSList *terms) { return search_tree (self, terms, self->priv->id_to_app); } /** * cinnamon_app_system_subsearch: * @system: A #CinnamonAppSystem * @previous_results: (element-type CinnamonApp): List of previous results * @terms: (element-type utf8): List of terms, logical AND * * Search through a previous result set; for more information, see * js/ui/search.js. Note the value of @prefs must be * the same as passed to cinnamon_app_system_initial_search(). Note that returned * strings are only valid until a return to the main loop. * * Returns: (transfer container) (element-type CinnamonApp): List of application identifiers */ GSList * cinnamon_app_system_subsearch (CinnamonAppSystem *system, GSList *previous_results, GSList *terms) { GSList *iter; GSList *prefix_results = NULL; GSList *substring_results = NULL; GSList *normalized_terms = normalize_terms (terms); for (iter = previous_results; iter; iter = iter->next) { CinnamonApp *app = iter->data; _cinnamon_app_do_match (app, normalized_terms, &prefix_results, &substring_results); } g_slist_foreach (normalized_terms, (GFunc)g_free, NULL); g_slist_free (normalized_terms); /* Note that a shorter term might have matched as a prefix, but when extended only as a substring, so we have to redo the sort rather than reusing the existing ordering */ return g_slist_concat (prefix_results, substring_results); } /** * cinnamon_app_system_search_settings: * @system: A #CinnamonAppSystem * @terms: (element-type utf8): List of terms, logical AND * * Search through settings for the given search terms. * * Returns: (transfer container) (element-type CinnamonApp): List of setting applications */ GSList * cinnamon_app_system_search_settings (CinnamonAppSystem *self, GSList *terms) { GSList *null_list = NULL; /* if this is just a stub, let's at least do zero-init */ return null_list; } Cinnamon-2.8.6/src/cinnamon-jhbuild.in0000775000175000017500000005052712625666430016603 0ustar fabiofabio#!@PYTHON@ # -*- mode: Python; indent-tabs-mode: nil; -*- import atexit import datetime import dbus from dbus.mainloop.glib import DBusGMainLoop import gobject try: import json except ImportError: try: import simplejson as json except ImportError: json = None import optparse import os import random import re import shutil import signal import subprocess import sys import tempfile import termios import time import errno def show_version(option, opt_str, value, parser): print "Cinnamon @VERSION@" sys.exit() def get_running_session_environs(): wanted_environment = ['DBUS_SESSION_BUS_ADDRESS', 'DISPLAY', 'XDG_DATA_DIRS', 'XAUTHORITY', 'XDG_SESSION_COOKIE', 'ORBIT_SOCKETDIR', 'SESSION_MANAGER'] num_re = re.compile('^[0-9]+$') myuid = os.getuid() if not os.path.isdir('/proc'): return {} for filename in os.listdir('/proc'): if not num_re.match(filename): continue piddir = '/proc/' + filename try: stat = os.stat(piddir) except OSError, e: continue if not stat.st_uid == myuid: continue try: f = open(piddir + "/cmdline") command = f.read() f.close() except IOError, e: continue # /proc/cmdline is separated and terminated by NULs command = command.split("\x00")[0] command = os.path.basename(command) if command != 'gnome-session': continue try: f = open(os.path.join(piddir, 'environ')) except OSError, e: continue environ_data = f.read() f.close() # There's a trailing null at the last one, so remove the # empty string environs = environ_data.split('\0')[:-1] # Rumor has it the presence of just FOO (instead of FOO=bar) # represents a deleted environment variable environs = filter(lambda x: '=' in x, environs) # Turn it into a dictionary environs = dict(map(lambda x: x.split('=', 1), environs)) result = {} for key in wanted_environment: if key in environs: result[key] = environs[key] return result _bus = None _bus_iface = None _name_owner_changed_hook = None def on_name_owner_changed(name, prev_owner, new_owner): if _name_owner_changed_hook: _name_owner_changed_hook(name, prev_owner, new_owner) def get_bus(): global _bus if _bus is None: dbus_loop = DBusGMainLoop() _bus = dbus.SessionBus(mainloop=dbus_loop) return _bus def get_bus_iface(): global _bus_iface if _bus_iface is None: bus = get_bus() bus_proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') _bus_iface = dbus.Interface(bus_proxy, 'org.freedesktop.DBus') _bus_iface.connect_to_signal('NameOwnerChanged', on_name_owner_changed) return _bus_iface def wait_for_dbus_name(wait_name): global _name_owner_changed_hook bus_iface = get_bus_iface() loop = gobject.MainLoop() def on_name_owner_changed(name, prev_owner, new_owner): if not (name == wait_name and new_owner != ''): return loop.quit() return _name_owner_changed_hook = on_name_owner_changed def on_timeout(): print "\nFailed to start %s: timed out" % (wait_name,) sys.exit(1) gobject.timeout_add_seconds(7, on_timeout) loop.run() _name_owner_changed_hook = None def start_dconf_await_service(): DCONF_NAME = 'ca.desrt.dconf' bus = get_bus() get_bus_iface() # connect to NameOwnerChanged signal # See if the service is already running or normal D-Bus activation works need_manual_activate = False try: dconf_proxy = bus.get_object(DCONF_NAME, '/') dconf_proxy.Ping(dbus_interface='org.freedesktop.DBus.Peer') except dbus.exceptions.DBusException, e: if e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown': need_manual_activate = True else: raise e if not need_manual_activate: return # At this point, it looks like we just have a jhbuild install # of dconf, not known to the session dbus-daemon, so we start # it manually and wait for it to join the bus print "Starting dconf-service... ", sys.stdout.flush() # dconf is linked without libtool, so unlike other GNOME modules, # won't have an embedded rpath for its library directory. env = dict(os.environ) if 'LD_LIBRARY_PATH' in env and env['LD_LIBRARY_PATH']: ld_library_path = '@libdir@:' + env['LD_LIBRARY_PATH'] else: ld_library_path = '@libdir@' env['LD_LIBRARY_PATH'] = ld_library_path dconf_path = os.path.join('@libexecdir@', 'dconf-service') try: subprocess.Popen([dconf_path, '--keep-alive'], env=env) except OSError, e: print "\nFailed to start %s: %s" % (dconf_path, e) sys.exit(1) wait_for_dbus_name (DCONF_NAME) PERF_HELPER_NAME = "org.Cinnamon.PerfHelper" PERF_HELPER_IFACE = "org.Cinnamon.PerfHelper" PERF_HELPER_PATH = "/org/Cinnamon/PerfHelper" def start_perf_helper(): get_bus_iface() # connect to NameOwnerChanged signal self_dir = os.path.dirname(os.path.abspath(sys.argv[0])) running_from_source_tree = os.path.exists(os.path.join(self_dir, 'cinnamon-jhbuild.in')) if running_from_source_tree: perf_helper_path = os.path.join(self_dir, "cinnamon-perf-helper") else: perf_helper_path = "@pkglibexecdir@/cinnamon-perf-helper" subprocess.Popen([perf_helper_path]) wait_for_dbus_name (PERF_HELPER_NAME) def stop_perf_helper(): bus = get_bus() proxy = bus.get_object(PERF_HELPER_NAME, PERF_HELPER_PATH) proxy.Exit(dbus_interface=PERF_HELPER_IFACE) def start_cinnamon(perf_output=None): self_dir = os.path.dirname(os.path.abspath(sys.argv[0])) if os.path.exists(os.path.join(self_dir, 'cinnamon-jhbuild.in')): running_from_source_tree = True top_dir = os.path.dirname(self_dir) typelib_dir = '@JHBUILD_TYPELIBDIR@:' + os.path.join(top_dir, "src") js_dir = os.path.join(top_dir, "js") data_dir = os.path.join(top_dir, "data") else: running_from_source_tree = False js_dir = os.path.join('@pkgdatadir@', 'js') typelib_dir = '@JHBUILD_TYPELIBDIR@' if os.environ.has_key('GI_TYPELIB_PATH'): typelib_dir = typelib_dir + ":" + os.environ.get('GI_TYPELIB_PATH') # Set up environment env = dict(os.environ) # TODO: Fix this, since nothing prevents it from propagating to child # processes. Why is it even here? env.update({'GNOME_DISABLE_CRASH_DIALOG' : '1'}) # This stuff should really should only happen when running # uninstalled, i.e. it should be in the conditional # below. Otherwise it's just a bad reimplementation of "jhbuild # run". See bug #642084 env.update({'CINNAMON_JS' : js_dir, 'PATH' : '@bindir@:' + os.environ.get('PATH', ''), 'GI_TYPELIB_PATH' : typelib_dir, 'XDG_CONFIG_DIRS' : '@sysconfdir@/xdg:' + (os.environ.get('XDG_CONFIG_DIRS') or '/etc/xdg'), 'XDG_DATA_DIRS' : '@datadir@:' + (os.environ.get('XDG_DATA_DIRS') or '/usr/local/share:/usr/share')}) if running_from_source_tree: env.update({'CINNAMON_BINDIR' : self_dir, 'CINNAMON_DATADIR' : data_dir, 'GSETTINGS_SCHEMA_DIR' : data_dir }) else: # This is just broken to set in the installed case; see bug #642084 env.update({'GSETTINGS_SCHEMA_DIR' : os.path.join('@datadir@', 'glib-2.0', 'schemas')}) # Also plain broken to set in the normal installed case jhbuild_gconf_source = os.path.join('@sysconfdir@', 'gconf/2/path.jhbuild') if os.path.exists(jhbuild_gconf_source): env['GCONF_DEFAULT_SOURCE_PATH'] = jhbuild_gconf_source if options.perf is not None: env['CINNAMON_PERF_MODULE'] = options.perf env['MUFFIN_WM_CLASS_FILTER'] = 'Cinnamon-perf-helper' if perf_output is not None: env['CINNAMON_PERF_OUTPUT'] = perf_output args = [] if options.debug: debug_command = options.debug_command.split() if running_from_source_tree: args += [os.path.join(top_dir, 'libtool'), '--mode=execute'] args += debug_command args.append(os.path.join(self_dir, 'cinnamon-real')) if options.replace: args.append('--replace') if options.sync: args.append('--sync') return subprocess.Popen(args, env=env) def _killall(processname): subprocess.call(['pkill', '-u', '%d' % (os.getuid(), ), '-f', r'^([^ ]*/)?' + re.escape(processname) + '($| )']) def ensure_desktop_infrastructure_state(): # This is a collection of random hacks necessary to make sure # that we can run in jhbuild scenarios or when dynamically # replacing GNOME 2. start_dconf_await_service() # We need to terminate notification-daemon _killall('notification-daemon') _killall('notify-osd') def run_cinnamon(perf_output=None): if options.debug: # Record initial terminal state so we can reset it to that # later, in case we kill gdb at a bad time termattrs = termios.tcgetattr(0); normal_exit = False if options.verbose: print "Starting cinnamon" try: cinnamon = start_cinnamon(perf_output=perf_output) # Wait for cinnamon to exit if options.verbose: print "Waiting for cinnamon to exit" cinnamon.wait() except KeyboardInterrupt, e: try: os.kill(cinnamon.pid, signal.SIGKILL) except: pass cinnamon.wait() finally: if cinnamon is None: print "Failed to start cinnamon" elif cinnamon.returncode == 0: normal_exit = True if options.verbose: print "Cinnamon exited normally" elif cinnamon.returncode < 0: # Python has no mapping for strsignal; not worth using # ctypes for this. print "Cinnamon killed with signal %d" % - cinnamon.returncode else: # Normal reason here would be losing connection the X server if options.verbose: print "Cinnamon exited with return code %d" % cinnamon.returncode if options.debug: termios.tcsetattr(0, termios.TCSANOW, termattrs); return normal_exit def upload_performance_report(report_text): # Local imports to avoid impacting cinnamon startup time import base64 from ConfigParser import RawConfigParser import hashlib import hmac import httplib import urlparse import urllib try: config_home = os.environ['XDG_CONFIG_HOME'] except KeyError: config_home = None if not config_home: config_home = os.path.expanduser("~/.config") config_file = os.path.join(config_home, "cinnamon/perf.ini") try: config = RawConfigParser() f = open(config_file) config.readfp(f) f.close() base_url = config.get('upload', 'url') system_name = config.get('upload', 'name') secret_key = config.get('upload', 'key') except Exception, e: print "Can't read upload configuration from %s: %s" % (config_file, str(e)) sys.exit(1) # Determine host, port and upload URL from provided data, we're # a bit extra-careful about normalization since the URL is part # of the signature. split = urlparse.urlsplit(base_url) scheme = split[0].lower() netloc = split[1] base_path = split[2] m = re.match(r'^(.*?)(?::(\d+))?$', netloc) if m.group(2): host, port = m.group(1), int(m.group(2)) else: host, port = m.group(1), None if scheme != "http": print "'%s' is not a HTTP URL" % base_url sys.exit(1) if port is None: port = 80 if base_path.endswith('/'): base_path = base_path[:-1] if port == 80: normalized_base = "%s://%s%s" % (scheme, host, base_path) else: normalized_base = "%s://%s:%d%s" % (scheme, host, port, base_path) upload_url = normalized_base + '/system/%s/upload' % system_name upload_path = urlparse.urlsplit(upload_url)[2] # path portion # Create signature based on upload URL and the report data signature_data = 'POST&' + upload_url + "&&" h = hmac.new(secret_key, digestmod=hashlib.sha1) h.update(signature_data) h.update(report_text) signature = urllib.quote(base64.b64encode(h.digest()), "~") headers = { 'User-Agent': 'cinnamon', 'Content-Type': 'application/json', 'X-Cinnamon-Signature': 'HMAC-SHA1 ' + signature }; connection = httplib.HTTPConnection(host, port) connection.request('POST', upload_path, report_text, headers) response = connection.getresponse() if response.status == 200: print "Performance report upload succeeded" else: print "Performance report upload failed with status %d" % response.status print response.read() def run_performance_test(): iters = options.perf_iters if options.perf_warmup: iters += 1 logs = [] metric_summaries = {} start_perf_helper() for i in xrange(0, iters): # We create an empty temporary file that Cinnamon will overwrite # with the contents. handle, output_file = tempfile.mkstemp(".json", "cinnamon-perf.") os.close(handle) # Run the performance test and collect the output as JSON normal_exit = False try: normal_exit = run_cinnamon(perf_output=output_file) except: stop_perf_helper() raise finally: if not normal_exit: os.remove(output_file) if not normal_exit: stop_perf_helper() return False try: f = open(output_file) output = json.load(f) f.close() except: stop_perf_helper() raise finally: os.remove(output_file) # Grab the event definitions and monitor layout the first time around if i == 0: events = output['events'] monitors = output['monitors'] if options.perf_warmup and i == 0: continue for metric in output['metrics']: name = metric['name'] if not name in metric_summaries: summary = {} summary['description'] = metric['description'] summary['units'] = metric['units'] summary['values'] = [] metric_summaries[name] = summary else: summary = metric_summaries[name] summary['values'].append(metric['value']) logs.append(output['log']) stop_perf_helper() if options.perf_output or options.perf_upload: # Write a complete report, formatted as JSON. The Javascript/C code that # generates the individual reports we are summarizing here is very careful # to format them nicely, but we just dump out a compressed no-whitespace # version here for simplicity. Using json.dump(indent=0) doesn't real # improve the readability of the output much. report = { 'date': datetime.datetime.utcnow().isoformat() + 'Z', 'events': events, 'monitors': monitors, 'metrics': metric_summaries, 'logs': logs } # Add the Git revision if available self_dir = os.path.dirname(os.path.abspath(sys.argv[0])) if os.path.exists(os.path.join(self_dir, 'cinnamon-jhbuild.in')): top_dir = os.path.dirname(self_dir) git_dir = os.path.join(top_dir, '.git') if os.path.exists(git_dir): env = dict(os.environ) env['GIT_DIR'] = git_dir revision = subprocess.Popen(['git', 'rev-parse', 'HEAD'], env=env, stdout=subprocess.PIPE).communicate()[0].strip() report['revision'] = revision if options.perf_output: f = open(options.perf_output, 'w') json.dump(report, f) f.close() if options.perf_upload: upload_performance_report(json.dumps(report)) else: # Write a human readable summary print '------------------------------------------------------------'; for metric in sorted(metric_summaries.keys()): summary = metric_summaries[metric] print "#", summary['description'] print metric, ", ".join((str(x) for x in summary['values'])) print '------------------------------------------------------------'; return True def restore_gnome(): # Do imports lazily to save time and memory import gio import gconf # We don't want to start the new gnome-panel in the current # directory; $HOME is better for stuff launched from it os.chdir(os.path.expanduser("~")) def launch_component(gconf_path): client = gconf.client_get_default() component = client.get_string(gconf_path) if component == None or component == "": return False # See gnome-session/gsm-util.c:gsm_util_find_desktop_file_for_app_name() # The one difference is that we don't search the autostart directories, # and just search normal application search path. (Gio doesnt' know # how to search the autostart dirs, so we'd have to do that ourselves.) appinfo = None try: appinfo = gio.unix.DesktopAppInfo(component + ".desktop") except: try: appinfo = gio.unix.DesktopAppInfo("gnome-" + component + ".desktop") except: pass if appinfo: appinfo.launch() return True return False # GNOME2 fallback wm = launch_component("/desktop/gnome/session/required_components/windowmanager") panel = launch_component("/desktop/gnome/session/required_components/panel") if not wm and not panel: # Probably GNOME3 subprocess.Popen(['cinnamon']) # Main program parser = optparse.OptionParser() parser.add_option("-r", "--replace", action="store_true", help="Replace the running metacity/gnome-panel") parser.add_option("-g", "--debug", action="store_true", help="Run under a debugger") parser.add_option("", "--debug-command", metavar="COMMAND", help="Command to use for debugging (defaults to 'gdb --args')") parser.add_option("-v", "--verbose", action="store_true") parser.add_option("", "--sync", action="store_true") parser.add_option("", "--perf", metavar="PERF_MODULE", help="Specify the name of a performance module to run") parser.add_option("", "--perf-iters", type="int", metavar="ITERS", help="Numbers of iterations of performance module to run", default=1) parser.add_option("", "--perf-warmup", action="store_true", help="Run a dry run before performance tests") parser.add_option("", "--perf-output", metavar="OUTPUT_FILE", help="Output file to write performance report") parser.add_option("", "--perf-upload", action="store_true", help="Upload performance report to server") parser.add_option("", "--version", action="callback", callback=show_version, help="Display version and exit") options, args = parser.parse_args() if args: parser.print_usage() sys.exit(1) if options.perf and json is None: print 'The Python simplejson module is required for performance tests' sys.exit(1) # Handle ssh logins if 'DISPLAY' not in os.environ: running_env = get_running_session_environs() os.environ.update(running_env) if options.debug_command: options.debug = True elif options.debug: options.debug_command = "gdb --args" # We only respawn the previous environment on abnormal exit; # for a clean exit, we assume that cinnamon was replaced with # something else. normal_exit = False try: if options.perf: normal_exit = run_performance_test() else: ensure_desktop_infrastructure_state() normal_exit = run_cinnamon() finally: if options.replace and (options.perf or not normal_exit): restore_gnome() if normal_exit: sys.exit(0) else: sys.exit(1) Cinnamon-2.8.6/src/cinnamon-tray-icon.c0000664000175000017500000001712612625666430016676 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-tray-icon.h" #include "cinnamon-gtk-embed.h" #include "cinnamon-window-tracker.h" #include "tray/na-tray-child.h" #include #include "st.h" enum { PROP_0, PROP_PID, PROP_TITLE, PROP_WM_CLASS }; struct _CinnamonTrayIconPrivate { NaTrayChild *socket; pid_t pid; char *title, *wm_class; }; G_DEFINE_TYPE (CinnamonTrayIcon, cinnamon_tray_icon, CINNAMON_TYPE_GTK_EMBED); static void cinnamon_tray_icon_finalize (GObject *object) { CinnamonTrayIcon *icon = CINNAMON_TRAY_ICON (object); g_free (icon->priv->title); g_free (icon->priv->wm_class); G_OBJECT_CLASS (cinnamon_tray_icon_parent_class)->finalize (object); } static void cinnamon_tray_icon_constructed (GObject *object) { GdkWindow *icon_app_window; CinnamonTrayIcon *icon = CINNAMON_TRAY_ICON (object); CinnamonEmbeddedWindow *window; GdkDisplay *display; Window plug_xid; Atom _NET_WM_PID, type; int result, format; gulong nitems, bytes_after, *val = NULL; /* We do all this now rather than computing it on the fly later, * because Cinnamon may want to see their values from a * tray-icon-removed signal handler, at which point the plug has * already been removed from the socket. */ g_object_get (object, "window", &window, NULL); g_return_if_fail (window != NULL); icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window))); g_object_unref (window); icon->priv->title = na_tray_child_get_title (icon->priv->socket); na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class); icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket)); plug_xid = GDK_WINDOW_XID (icon_app_window); display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket)); gdk_error_trap_push (); _NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID"); result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid, _NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after, (guchar **)&val); if (!gdk_error_trap_pop () && result == Success && type == XA_CARDINAL && nitems == 1) icon->priv->pid = *val; if (val) XFree (val); } static void cinnamon_tray_icon_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonTrayIcon *icon = CINNAMON_TRAY_ICON (object); switch (prop_id) { case PROP_PID: g_value_set_uint (value, icon->priv->pid); break; case PROP_TITLE: g_value_set_string (value, icon->priv->title); break; case PROP_WM_CLASS: g_value_set_string (value, icon->priv->wm_class); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_tray_icon_class_init (CinnamonTrayIconClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (CinnamonTrayIconPrivate)); object_class->get_property = cinnamon_tray_icon_get_property; object_class->constructed = cinnamon_tray_icon_constructed; object_class->finalize = cinnamon_tray_icon_finalize; g_object_class_install_property (object_class, PROP_PID, g_param_spec_uint ("pid", "PID", "The PID of the icon's application", 0, G_MAXUINT, 0, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_TITLE, g_param_spec_string ("title", "Title", "The icon's window title", NULL, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_WM_CLASS, g_param_spec_string ("wm-class", "WM Class", "The icon's window WM_CLASS", NULL, G_PARAM_READABLE)); } static void cinnamon_tray_icon_init (CinnamonTrayIcon *icon) { icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, CINNAMON_TYPE_TRAY_ICON, CinnamonTrayIconPrivate); } /* * Public API */ ClutterActor * cinnamon_tray_icon_new (CinnamonEmbeddedWindow *window) { g_return_val_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window), NULL); return g_object_new (CINNAMON_TYPE_TRAY_ICON, "window", window, NULL); } /** * cinnamon_tray_icon_click: * @icon: a #CinnamonTrayIcon * @event: the #ClutterEvent triggering the fake click * * Fakes a press and release on @icon. @event must be a * %CLUTTER_BUTTON_RELEASE event. Its relevant details will be passed * on to the icon, but its coordinates will be ignored; the click is * always made on the center of @icon. */ void cinnamon_tray_icon_click (CinnamonTrayIcon *icon, ClutterEvent *event) { XButtonEvent xbevent; XCrossingEvent xcevent; GdkWindow *remote_window; GdkScreen *screen; int x_root, y_root; Display *xdisplay; Window xwindow, xrootwindow; g_return_if_fail (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE); gdk_error_trap_push (); remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket)); xwindow = GDK_WINDOW_XID (remote_window); xdisplay = GDK_WINDOW_XDISPLAY (remote_window); screen = gdk_window_get_screen (remote_window); xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen)); gdk_window_get_origin (remote_window, &x_root, &y_root); /* First make the icon believe the pointer is inside it */ xcevent.type = EnterNotify; xcevent.window = xwindow; xcevent.root = xrootwindow; xcevent.subwindow = None; xcevent.time = clutter_event_get_time (event); xcevent.x = gdk_window_get_width (remote_window) / 2; xcevent.y = gdk_window_get_height (remote_window) / 2; xcevent.x_root = x_root + xcevent.x; xcevent.y_root = y_root + xcevent.y; xcevent.mode = NotifyNormal; xcevent.detail = NotifyNonlinear; xcevent.same_screen = True; XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent); /* Now do the click */ xbevent.type = ButtonPress; xbevent.window = xwindow; xbevent.root = xrootwindow; xbevent.subwindow = None; xbevent.time = xcevent.time; xbevent.x = xcevent.x; xbevent.y = xcevent.y; xbevent.x_root = xcevent.x_root; xbevent.y_root = xcevent.y_root; xbevent.state = clutter_event_get_state (event); xbevent.button = clutter_event_get_button (event); xbevent.same_screen = True; XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent); xbevent.type = ButtonRelease; XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent); /* And move the pointer back out */ xcevent.type = LeaveNotify; XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent); gdk_error_trap_pop_ignored (); } Cinnamon-2.8.6/src/cinnamon-wm-private.h0000664000175000017500000000523312625666430017065 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_WM_PRIVATE_H__ #define __CINNAMON_WM_PRIVATE_H__ #include "cinnamon-wm.h" G_BEGIN_DECLS /* These forward along the different effects from CinnamonPlugin */ void _cinnamon_wm_minimize (CinnamonWM *wm, MetaWindowActor *actor); void _cinnamon_wm_maximize (CinnamonWM *wm, MetaWindowActor *actor, gint x, gint y, gint width, gint height); void _cinnamon_wm_unmaximize (CinnamonWM *wm, MetaWindowActor *actor, gint x, gint y, gint width, gint height); void _cinnamon_wm_tile (CinnamonWM *wm, MetaWindowActor *actor, gint x, gint y, gint width, gint height); void _cinnamon_wm_map (CinnamonWM *wm, MetaWindowActor *actor); void _cinnamon_wm_destroy (CinnamonWM *wm, MetaWindowActor *actor); void _cinnamon_wm_switch_workspace (CinnamonWM *wm, gint from, gint to, MetaMotionDirection direction); void _cinnamon_wm_show_tile_preview (CinnamonWM *wm, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor, guint snap_queued); void _cinnamon_wm_hide_tile_preview (CinnamonWM *wm); void _cinnamon_wm_show_hud_preview (CinnamonWM *wm, guint current_proximity_zone, MetaRectangle *work_area, guint snap_queued); void _cinnamon_wm_hide_hud_preview (CinnamonWM *wm); void _cinnamon_wm_kill_window_effects (CinnamonWM *wm, MetaWindowActor *actor); G_END_DECLS #endif /* __CINNAMON_WM_PRIVATE_H__ */ Cinnamon-2.8.6/src/cinnamon-slicer.h0000664000175000017500000000226412625666430016254 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_SLICER_H__ #define __CINNAMON_SLICER_H__ #include "st.h" #define CINNAMON_TYPE_SLICER (cinnamon_slicer_get_type ()) #define CINNAMON_SLICER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_SLICER, CinnamonSlicer)) #define CINNAMON_SLICER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_SLICER, CinnamonSlicerClass)) #define CINNAMON_IS_SLICER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_SLICER)) #define CINNAMON_IS_SLICER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_SLICER)) #define CINNAMON_SLICER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_SLICER, CinnamonSlicerClass)) typedef struct _CinnamonSlicer CinnamonSlicer; typedef struct _CinnamonSlicerClass CinnamonSlicerClass; typedef struct _CinnamonSlicerPrivate CinnamonSlicerPrivate; struct _CinnamonSlicer { StBin parent; CinnamonSlicerPrivate *priv; }; struct _CinnamonSlicerClass { StBinClass parent_class; }; GType cinnamon_slicer_get_type (void) G_GNUC_CONST; #endif /* __CINNAMON_SLICER_H__ */ Cinnamon-2.8.6/src/Makefile-st.am0000664000175000017500000001125112625666430015476 0ustar fabiofabiost_cflags = \ -I$(top_srcdir)/src \ -DPREFIX=\""$(prefix)"\" \ -DLIBDIR=\""$(libdir)"\" \ -DG_DISABLE_DEPRECATED \ -DG_LOG_DOMAIN=\"St\" \ -DST_COMPILATION \ -DCLUTTER_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_API \ -DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" \ $(ST_CFLAGS) \ $(NULL) st_built_sources = \ st-enum-types.h \ st-enum-types.c \ st-marshal.h \ st-marshal.c BUILT_SOURCES += $(st_built_sources) EXTRA_DIST += \ st/test-theme.css \ st/st-marshal.list \ st/st-enum-types.h.in \ st/st-enum-types.c.in CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h st-marshal.h: stamp-st-marshal.h @true stamp-st-marshal.h: Makefile st/st-marshal.list $(AM_V_GEN) $(GLIB_GENMARSHAL) \ --prefix=_st_marshal \ --header \ $(srcdir)/st/st-marshal.list > $@.tmp && \ (cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \ rm -f $@.tmp && \ echo timestamp > $(@F) st-marshal.c: Makefile st/st-marshal.list $(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \ $(GLIB_GENMARSHAL) \ --prefix=_st_marshal \ --body \ $(srcdir)/st/st-marshal.list ) > $@.tmp && \ cp -f $@.tmp st-marshal.c && \ rm -f $@.tmp st-enum-types.h: stamp-st-enum-types.h Makefile @true stamp-st-enum-types.h: $(source_h) $(srcdir)/st/st-enum-types.h.in $(st_source_h) $(AM_V_GEN) ( \ $(GLIB_MKENUMS) \ --template $(srcdir)/st/st-enum-types.h.in \ $(addprefix $(srcdir)/, $(st_source_h)) ) >> $@.tmp && \ (cmp -s $@.tmp st-enum-types.h || cp $@.tmp st-enum-types.h) && \ rm -f $@.tmp && \ echo timestamp > $(@F) st-enum-types.c: stamp-st-enum-types.h $(srcdir)/st/st-enum-types.c.in $(AM_V_GEN) ( \ $(GLIB_MKENUMS) \ --template $(srcdir)/st/st-enum-types.c.in \ $(addprefix $(srcdir)/,$(st_source_h)) ) >> $@.tmp && \ cp $@.tmp $@ && \ rm -f $@.tmp # please, keep this sorted alphabetically st_source_h = \ st/st-adjustment.h \ st/st-background-effect.h \ st/st-bin.h \ st/st-border-image.h \ st/st-box-layout.h \ st/st-box-layout-child.h \ st/st-button.h \ st/st-clipboard.h \ st/st-cogl-wrapper.h \ st/st-container.h \ st/st-drawing-area.h \ st/st-entry.h \ st/st-focus-manager.h \ st/st-group.h \ st/st-icon.h \ st/st-icon-colors.h \ st/st-im-text.h \ st/st-label.h \ st/st-overflow-box.h \ st/st-polygon.h \ st/st-private.h \ st/st-scrollable.h \ st/st-scroll-bar.h \ st/st-scroll-view.h \ st/st-shadow.h \ st/st-table.h \ st/st-table-child.h \ st/st-texture-cache.h \ st/st-theme.h \ st/st-theme-context.h \ st/st-theme-node.h \ st/st-types.h \ st/st-widget.h \ st/st-widget-accessible.h \ $(NULL) st.h: stamp-st.h @true stamp-st.h: Makefile $(AM_V_GEN) (echo "#define ST_H_INSIDE 1"; \ for name in $(st_source_h); do \ echo "#include <"$$name">"; \ done; echo "#undef ST_H_INSIDE") > $@.tmp && \ (cmp -s $@.tmp st.h || cp $@.tmp st.h) && \ rm -f $@.tmp && \ echo timestamp > $(@F) BUILT_SOURCES += st.h CLEANFILES += stamp-st.h st_source_private_h = \ st/st-private.h \ st/st-table-private.h \ st/st-theme-private.h \ st/st-theme-node-private.h \ st/st-theme-node-transition.h # please, keep this sorted alphabetically st_source_c = \ st/st-adjustment.c \ st/st-background-effect.c \ st/st-bin.c \ st/st-border-image.c \ st/st-box-layout.c \ st/st-box-layout-child.c \ st/st-button.c \ st/st-clipboard.c \ st/st-cogl-wrapper.c \ st/st-container.c \ st/st-drawing-area.c \ st/st-entry.c \ st/st-focus-manager.c \ st/st-group.c \ st/st-icon.c \ st/st-icon-colors.c \ st/st-im-text.c \ st/st-label.c \ st/st-overflow-box.c \ st/st-polygon.c \ st/st-private.c \ st/st-scrollable.c \ st/st-scroll-bar.c \ st/st-scroll-view.c \ st/st-shadow.c \ st/st-table.c \ st/st-table-child.c \ st/st-texture-cache.c \ st/st-theme.c \ st/st-theme-context.c \ st/st-theme-node.c \ st/st-theme-node-drawing.c \ st/st-theme-node-transition.c \ st/st-widget.c \ $(NULL) st_non_gir_sources = \ st/st-scroll-view-fade.c \ st/st-scroll-view-fade.h \ $(NULL) noinst_LTLIBRARIES += libst-1.0.la libst_1_0_la_LIBADD = -lm $(ST_LIBS) libst_1_0_la_SOURCES = \ $(st_source_c) \ $(st_non_gir_sources) \ $(st_source_private_h) \ $(st_source_private_c) \ $(st_source_h) \ st.h \ $(st_built_sources) \ $(NULL) libst_1_0_la_CPPFLAGS = $(st_cflags) noinst_PROGRAMS += test-theme test_theme_CPPFLAGS = $(st_cflags) test_theme_LDADD = libst-1.0.la test_theme_SOURCES = st/test-theme.c Cinnamon-2.8.6/src/cinnamon-screenshot.c0000664000175000017500000003224612625666430017146 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include #include #include #include #include #include #include #include #include "cinnamon-global.h" #include "cinnamon-screen-grabber.h" #include "cinnamon-screenshot.h" struct _CinnamonScreenshotClass { GObjectClass parent_class; }; struct _CinnamonScreenshot { GObject parent_instance; CinnamonGlobal *global; }; /* Used for async screenshot grabbing */ typedef struct _screenshot_data { CinnamonScreenshot *screenshot; char *filename; cairo_surface_t *image; cairo_rectangle_int_t screenshot_area; gboolean include_cursor; CinnamonScreenshotCallback callback; } _screenshot_data; G_DEFINE_TYPE(CinnamonScreenshot, cinnamon_screenshot, G_TYPE_OBJECT); static void cinnamon_screenshot_class_init (CinnamonScreenshotClass *screenshot_class) { (void) screenshot_class; } static void cinnamon_screenshot_init (CinnamonScreenshot *screenshot) { screenshot->global = cinnamon_global_get (); } static void on_screenshot_written (GObject *source, GAsyncResult *result, gpointer user_data) { _screenshot_data *screenshot_data = (_screenshot_data*) user_data; if (screenshot_data->callback) screenshot_data->callback (screenshot_data->screenshot, g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)), &screenshot_data->screenshot_area); cairo_surface_destroy (screenshot_data->image); g_object_unref (screenshot_data->screenshot); g_free (screenshot_data->filename); g_free (screenshot_data); } static void write_screenshot_thread (GSimpleAsyncResult *result, GObject *object, GCancellable *cancellable) { cairo_status_t status; _screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result)); g_assert (screenshot_data != NULL); status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename); g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS); } static void do_grab_screenshot (_screenshot_data *screenshot_data, int x, int y, int width, int height) { CinnamonScreenGrabber *grabber; static const cairo_user_data_key_t key; guchar *data; int stride; grabber = cinnamon_screen_grabber_new (); data = cinnamon_screen_grabber_grab (grabber, x, y, width, height); g_object_unref (grabber); stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width); screenshot_data->image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, width, height,stride); cairo_surface_set_user_data (screenshot_data->image, &key, data, (cairo_destroy_func_t)g_free); } static void _draw_cursor_image (cairo_surface_t *surface, cairo_rectangle_int_t area) { XFixesCursorImage *cursor_image; cairo_surface_t *cursor_surface; cairo_region_t *screenshot_region; cairo_t *cr; guchar *data; int stride; int i, j; cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); if (!cursor_image) return; screenshot_region = cairo_region_create_rectangle (&area); if (!cairo_region_contains_point (screenshot_region, cursor_image->x, cursor_image->y)) { XFree (cursor_image); cairo_region_destroy (screenshot_region); return; } cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height); /* The pixel data (in typical Xlib breakage) is longs even on * 64-bit platforms, so we have to data-convert there. For simplicity, * just do it always */ data = cairo_image_surface_get_data (cursor_surface); stride = cairo_image_surface_get_stride (cursor_surface); for (i = 0; i < cursor_image->height; i++) for (j = 0; j < cursor_image->width; j++) *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j]; cairo_surface_mark_dirty (cursor_surface); cr = cairo_create (surface); cairo_set_source_surface (cr, cursor_surface, cursor_image->x - cursor_image->xhot - area.x, cursor_image->y - cursor_image->yhot - area.y); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (cursor_surface); cairo_region_destroy (screenshot_region); XFree (cursor_image); } static void grab_screenshot (ClutterActor *stage, _screenshot_data *screenshot_data) { MetaScreen *screen = cinnamon_global_get_screen (screenshot_data->screenshot->global); int width, height; GSimpleAsyncResult *result; meta_screen_get_size (screen, &width, &height); do_grab_screenshot (screenshot_data, 0, 0, width, height); if (meta_screen_get_n_monitors (screen) > 1) { cairo_region_t *screen_region = cairo_region_create (); cairo_region_t *stage_region; MetaRectangle monitor_rect; cairo_rectangle_int_t stage_rect; int i; cairo_t *cr; for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--) { meta_screen_get_monitor_geometry (screen, i, &monitor_rect); cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect); } stage_rect.x = 0; stage_rect.y = 0; stage_rect.width = width; stage_rect.height = height; stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect); cairo_region_xor (stage_region, screen_region); cairo_region_destroy (screen_region); cr = cairo_create (screenshot_data->image); for (i = 0; i < cairo_region_num_rectangles (stage_region); i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (stage_region, i, &rect); cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height); cairo_fill (cr); } cairo_destroy (cr); cairo_region_destroy (stage_region); } screenshot_data->screenshot_area.x = 0; screenshot_data->screenshot_area.y = 0; screenshot_data->screenshot_area.width = width; screenshot_data->screenshot_area.height = height; if (screenshot_data->include_cursor) _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_screenshot); g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); } static void grab_area_screenshot (ClutterActor *stage, _screenshot_data *screenshot_data) { GSimpleAsyncResult *result; do_grab_screenshot (screenshot_data, screenshot_data->screenshot_area.x, screenshot_data->screenshot_area.y, screenshot_data->screenshot_area.width, screenshot_data->screenshot_area.height); if (screenshot_data->include_cursor) _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_area_screenshot); g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); } /** * cinnamon_screenshot_screenshot: * @screenshot: the #CinnamonScreenshot * @include_cursor: Whether to include the cursor or not * @filename: The filename for the screenshot * @callback: (scope async): function to call returning success or failure * of the async grabbing * * Takes a screenshot of the whole screen * in @filename as png image. * */ void cinnamon_screenshot_screenshot (CinnamonScreenshot *screenshot, gboolean include_cursor, const char *filename, CinnamonScreenshotCallback callback) { ClutterActor *stage; _screenshot_data *data = g_new0 (_screenshot_data, 1); data->screenshot = g_object_ref (screenshot); data->filename = g_strdup (filename); data->callback = callback; data->include_cursor = include_cursor; stage = CLUTTER_ACTOR (cinnamon_global_get_stage (screenshot->global)); g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data); clutter_actor_queue_redraw (stage); } /** * cinnamon_screenshot_screenshot_area: * @screenshot: the #CinnamonScreenshot * @x: The X coordinate of the area * @y: The Y coordinate of the area * @width: The width of the area * @height: The height of the area * @filename: The filename for the screenshot * @callback: (scope async): function to call returning success or failure * of the async grabbing * * Takes a screenshot of the passed in area and saves it * in @filename as png image. * */ void cinnamon_screenshot_screenshot_area (CinnamonScreenshot *screenshot, gboolean include_cursor, int x, int y, int width, int height, const char *filename, CinnamonScreenshotCallback callback) { ClutterActor *stage; _screenshot_data *data = g_new0 (_screenshot_data, 1); data->screenshot = g_object_ref (screenshot); data->filename = g_strdup (filename); data->screenshot_area.x = x; data->screenshot_area.y = y; data->screenshot_area.width = width; data->screenshot_area.height = height; data->callback = callback; data->include_cursor = include_cursor; stage = CLUTTER_ACTOR (cinnamon_global_get_stage (screenshot->global)); g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data); clutter_actor_queue_redraw (stage); } /** * cinnamon_screenshot_screenshot_window: * @screenshot: the #CinnamonScreenshot * @include_frame: Whether to include the frame or not * @include_cursor: Whether to include the cursor or not * @filename: The filename for the screenshot * @callback: (scope async): function to call returning success or failure * of the async grabbing * * Takes a screenshot of the focused window (optionally omitting the frame) * in @filename as png image. * */ void cinnamon_screenshot_screenshot_window (CinnamonScreenshot *screenshot, gboolean include_frame, gboolean include_cursor, const char *filename, CinnamonScreenshotCallback callback) { GSimpleAsyncResult *result; _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1); MetaScreen *screen = cinnamon_global_get_screen (screenshot->global); MetaDisplay *display = meta_screen_get_display (screen); MetaWindow *window = meta_display_get_focus_window (display); ClutterActor *window_actor; gfloat actor_x, actor_y; MetaShapedTexture *stex; MetaRectangle rect; cairo_rectangle_int_t clip; screenshot_data->screenshot = g_object_ref (screenshot); screenshot_data->filename = g_strdup (filename); screenshot_data->callback = callback; window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); clutter_actor_get_position (window_actor, &actor_x, &actor_y); if (include_frame || !meta_window_get_frame (window)) { meta_window_get_outer_rect (window, &rect); screenshot_data->screenshot_area.x = rect.x; screenshot_data->screenshot_area.y = rect.y; clip.x = rect.x - (gint) actor_x; clip.y = rect.y - (gint) actor_y; } else { rect = *meta_window_get_rect (window); screenshot_data->screenshot_area.x = (gint) actor_x + rect.x; screenshot_data->screenshot_area.y = (gint) actor_y + rect.y; clip.x = rect.x; clip.y = rect.y; } clip.width = screenshot_data->screenshot_area.width = rect.width; clip.height = screenshot_data->screenshot_area.height = rect.height; stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor))); screenshot_data->image = meta_shaped_texture_get_image (stex, &clip); if (include_cursor) _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, cinnamon_screenshot_screenshot_window); g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); } CinnamonScreenshot * cinnamon_screenshot_new (void) { return g_object_new (CINNAMON_TYPE_SCREENSHOT, NULL); } Cinnamon-2.8.6/src/cinnamon-window-tracker.c0000664000175000017500000006344712625666430017740 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #define SN_API_NOT_YET_FROZEN 1 #include #include "cinnamon-window-tracker-private.h" #include "cinnamon-app-private.h" #include "cinnamon-global.h" #include "cinnamon-marshal.h" #include "st.h" /* This file includes modified code from * desktop-data-engine/engine-dbus/hippo-application-monitor.c * in the functions collecting application usage data. * Written by Owen Taylor, originally licensed under LGPL 2.1. * Copyright Red Hat, Inc. 2006-2008 */ /** * SECTION:cinnamon-window-tracker * @short_description: Associate windows with applications * * Maintains a mapping from windows to applications (.desktop file ids). * It currently implements this with some heuristics on the WM_CLASS X11 * property (and some static override regexps); in the future, we want to * have it also track through startup-notification. */ struct _CinnamonWindowTracker { GObject parent; CinnamonApp *focus_app; /* */ GHashTable *window_to_app; /* */ GHashTable *launched_pid_to_app; }; G_DEFINE_TYPE (CinnamonWindowTracker, cinnamon_window_tracker, G_TYPE_OBJECT); enum { PROP_0, PROP_FOCUS_APP }; enum { STARTUP_SEQUENCE_CHANGED, TRACKED_WINDOWS_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void cinnamon_window_tracker_finalize (GObject *object); static void set_focus_app (CinnamonWindowTracker *tracker, CinnamonApp *new_focus_app); static void on_focus_window_changed (MetaDisplay *display, GParamSpec *spec, CinnamonWindowTracker *tracker); static void track_window (CinnamonWindowTracker *tracker, MetaWindow *window); static void disassociate_window (CinnamonWindowTracker *tracker, MetaWindow *window); static void cinnamon_window_tracker_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonWindowTracker *tracker = CINNAMON_WINDOW_TRACKER (gobject); switch (prop_id) { case PROP_FOCUS_APP: g_value_set_object (value, tracker->focus_app); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void cinnamon_window_tracker_class_init (CinnamonWindowTrackerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = cinnamon_window_tracker_get_property; gobject_class->finalize = cinnamon_window_tracker_finalize; g_object_class_install_property (gobject_class, PROP_FOCUS_APP, g_param_spec_object ("focus-app", "Focus App", "Focused application", CINNAMON_TYPE_APP, G_PARAM_READABLE)); signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed", CINNAMON_TYPE_WINDOW_TRACKER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, CINNAMON_TYPE_STARTUP_SEQUENCE); signals[TRACKED_WINDOWS_CHANGED] = g_signal_new ("tracked-windows-changed", CINNAMON_TYPE_WINDOW_TRACKER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * cinnamon_window_tracker_is_window_interesting: * * The CinnamonWindowTracker associates certain kinds of windows with * applications; however, others we don't want to * appear in places where we want to give a list of windows * for an application, such as the alt-tab dialog. * * An example of a window we don't want to show is the root * desktop window. We skip all override-redirect types, and also * exclude other window types like tooltip explicitly, though generally * most of these should be override-redirect. * * Returns: %TRUE iff a window is "interesting" */ gboolean cinnamon_window_tracker_is_window_interesting (MetaWindow *window) { if (meta_window_is_override_redirect (window) || meta_window_is_skip_taskbar (window)) return FALSE; switch (meta_window_get_window_type (window)) { /* Definitely ignore these. */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_SPLASHSCREEN: /* Should have already been handled by override_redirect above, * but explicitly list here so we get the "unhandled enum" * warning if in the future anything is added.*/ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: return FALSE; case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_MENU: case META_WINDOW_TOOLBAR: case META_WINDOW_UTILITY: break; } return TRUE; } /** * get_app_from_window_wmclass: * * Looks only at the given window, and attempts to determine * an application based on WM_CLASS. If one can't be determined, * return %NULL. * * Return value: (transfer full): A newly-referenced #CinnamonApp, or %NULL */ static CinnamonApp * get_app_from_window_wmclass (MetaWindow *window) { CinnamonApp *app; CinnamonAppSystem *appsys; const char *wm_class; const char *wm_instance; appsys = cinnamon_app_system_get_default (); /* Notes on the heuristics used here: much of the complexity here comes from the desire to support Chrome apps. From https://bugzilla.gnome.org/show_bug.cgi?id=673657#c13 Currently chrome sets WM_CLASS as follows (the first string is the 'instance', the second one is the 'class': For the normal browser: WM_CLASS(STRING) = "chromium", "Chromium" For a bookmarked page (through 'Tools -> Create application shortcuts') WM_CLASS(STRING) = "wiki.gnome.org__GnomeShell_ApplicationBased", "Chromium" For an application from the chrome store (with a .desktop file created through right click, "Create shortcuts" from Chrome's apps overview) WM_CLASS(STRING) = "crx_blpcfgokakmgnkcojhhkbfbldkacnbeo", "Chromium" The .desktop file has a matching StartupWMClass, but the name differs, e.g. for the store app (youtube) there is .local/share/applications/chrome-blpcfgokakmgnkcojhhkbfbldkacnbeo-Default.desktop with StartupWMClass=crx_blpcfgokakmgnkcojhhkbfbldkacnbeo Note that chromium (but not google-chrome!) includes a StartupWMClass=chromium in their .desktop file, so we must match the instance first. Also note that in the good case (regular gtk+ app without hacks), instance and class are the same except for case and there is no StartupWMClass at all. */ /* first try a match from WM_CLASS (instance part) to StartupWMClass */ wm_instance = meta_window_get_wm_class_instance (window); app = cinnamon_app_system_lookup_startup_wmclass (appsys, wm_instance); if (app != NULL) return g_object_ref (app); /* then try a match from WM_CLASS to StartupWMClass */ wm_class = meta_window_get_wm_class (window); app = cinnamon_app_system_lookup_startup_wmclass (appsys, wm_class); if (app != NULL) return g_object_ref (app); /* then try a match from WM_CLASS (instance part) to .desktop */ app = cinnamon_app_system_lookup_desktop_wmclass (appsys, wm_instance); if (app != NULL) return g_object_ref (app); /* finally, try a match from WM_CLASS to .desktop */ app = cinnamon_app_system_lookup_desktop_wmclass (appsys, wm_class); if (app != NULL) return g_object_ref (app); return NULL; } /** * get_app_from_window_group: * @monitor: a #CinnamonWindowTracker * @window: a #MetaWindow * * Check other windows in the group for @window to see if we have * an application for one of them. * * Return value: (transfer full): A newly-referenced #CinnamonApp, or %NULL */ static CinnamonApp* get_app_from_window_group (CinnamonWindowTracker *tracker, MetaWindow *window) { CinnamonApp *result; GSList *group_windows; MetaGroup *group; GSList *iter; group = meta_window_get_group (window); if (group == NULL) return NULL; group_windows = meta_group_list_windows (group); result = NULL; /* Try finding a window in the group of type NORMAL; if we * succeed, use that as our source. */ for (iter = group_windows; iter; iter = iter->next) { MetaWindow *group_window = iter->data; if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL) continue; result = g_hash_table_lookup (tracker->window_to_app, group_window); if (result) break; } g_slist_free (group_windows); if (result) g_object_ref (result); return result; } /** * get_app_from_window_pid: * @tracker: a #CinnamonWindowTracker * @window: a #MetaWindow * * Check if the pid associated with @window corresponds to an * application we launched. * * Return value: (transfer full): A newly-referenced #CinnamonApp, or %NULL */ static CinnamonApp * get_app_from_window_pid (CinnamonWindowTracker *tracker, MetaWindow *window) { CinnamonApp *result; int pid; if (meta_window_is_remote (window)) return NULL; pid = meta_window_get_pid (window); if (pid == -1) return NULL; result = g_hash_table_lookup (tracker->launched_pid_to_app, GINT_TO_POINTER (pid)); if (result != NULL) g_object_ref (result); return result; } /** * get_app_for_window: * * Determines the application associated with a window, using * all available information such as the window's MetaGroup, * and what we know about other windows. * * Returns: (transfer full): a #CinnamonApp, or NULL if none is found */ static CinnamonApp * get_app_for_window (CinnamonWindowTracker *tracker, MetaWindow *window) { CinnamonApp *result = NULL; const char *startup_id; /* First, we check whether we already know about this window, * if so, just return that. */ if (meta_window_get_window_type (window) == META_WINDOW_NORMAL || meta_window_is_remote (window)) { result = g_hash_table_lookup (tracker->window_to_app, window); if (result != NULL) { g_object_ref (result); return result; } } if (meta_window_is_remote (window)) return _cinnamon_app_new_for_window (window); /* Check if the app's WM_CLASS specifies an app; this is * canonical if it does. */ result = get_app_from_window_wmclass (window); if (result != NULL) return result; result = get_app_from_window_pid (tracker, window); if (result != NULL) return result; /* Now we check whether we have a match through startup-notification */ startup_id = meta_window_get_startup_id (window); if (startup_id) { GSList *iter, *sequences; sequences = cinnamon_window_tracker_get_startup_sequences (tracker); for (iter = sequences; iter; iter = iter->next) { CinnamonStartupSequence *sequence = iter->data; const char *id = cinnamon_startup_sequence_get_id (sequence); if (strcmp (id, startup_id) != 0) continue; result = cinnamon_startup_sequence_get_app (sequence); if (result) { result = g_object_ref (result); break; } } } /* If we didn't get a startup-notification match, see if we matched * any other windows in the group. */ if (result == NULL) result = get_app_from_window_group (tracker, window); /* Our last resort - we create a fake app from the window */ if (result == NULL) result = _cinnamon_app_new_for_window (window); return result; } const char * _cinnamon_window_tracker_get_app_context (CinnamonWindowTracker *tracker, CinnamonApp *app) { return ""; } static void update_focus_app (CinnamonWindowTracker *self) { MetaWindow *new_focus_win; CinnamonApp *new_focus_app; new_focus_win = meta_display_get_focus_window (cinnamon_global_get_display (cinnamon_global_get ())); new_focus_app = new_focus_win ? cinnamon_window_tracker_get_window_app (self, new_focus_win) : NULL; set_focus_app (self, new_focus_app); } static void on_wm_class_changed (MetaWindow *window, GParamSpec *pspec, gpointer user_data) { CinnamonWindowTracker *self = CINNAMON_WINDOW_TRACKER (user_data); /* It's simplest to just treat this as a remove + add. */ disassociate_window (self, window); track_window (self, window); /* also just recaulcuate the focused app, in case it was the focused window that changed */ update_focus_app (self); } static void track_window (CinnamonWindowTracker *self, MetaWindow *window) { CinnamonApp *app; if (!cinnamon_window_tracker_is_window_interesting (window)) return; app = get_app_for_window (self, window); if (!app) return; /* At this point we've stored the association from window -> application */ g_hash_table_insert (self->window_to_app, window, app); g_signal_connect (window, "notify::wm-class", G_CALLBACK (on_wm_class_changed), self); _cinnamon_app_add_window (app, window); g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0); } static void cinnamon_window_tracker_on_window_added (MetaWorkspace *workspace, MetaWindow *window, gpointer user_data) { CinnamonWindowTracker *self = CINNAMON_WINDOW_TRACKER (user_data); track_window (self, window); } static void disassociate_window (CinnamonWindowTracker *self, MetaWindow *window) { CinnamonApp *app; app = g_hash_table_lookup (self->window_to_app, window); if (!app) return; g_object_ref (app); g_hash_table_remove (self->window_to_app, window); if (cinnamon_window_tracker_is_window_interesting (window)) { _cinnamon_app_remove_window (app, window); g_signal_handlers_disconnect_by_func (window, G_CALLBACK(on_wm_class_changed), self); } g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0); g_object_unref (app); } static void cinnamon_window_tracker_on_window_removed (MetaWorkspace *workspace, MetaWindow *window, gpointer user_data) { disassociate_window (CINNAMON_WINDOW_TRACKER (user_data), window); } static void load_initial_windows (CinnamonWindowTracker *tracker) { GList *workspaces, *iter; MetaScreen *screen = cinnamon_global_get_screen (cinnamon_global_get ()); workspaces = meta_screen_get_workspaces (screen); for (iter = workspaces; iter; iter = iter->next) { MetaWorkspace *workspace = iter->data; GList *windows = meta_workspace_list_windows (workspace); GList *window_iter; for (window_iter = windows; window_iter; window_iter = window_iter->next) { MetaWindow *window = window_iter->data; track_window (tracker, window); } g_list_free (windows); } } static void cinnamon_window_tracker_on_n_workspaces_changed (MetaScreen *screen, GParamSpec *pspec, gpointer user_data) { CinnamonWindowTracker *self = CINNAMON_WINDOW_TRACKER (user_data); GList *workspaces, *iter; workspaces = meta_screen_get_workspaces (screen); for (iter = workspaces; iter; iter = iter->next) { MetaWorkspace *workspace = iter->data; /* This pair of disconnect/connect is idempotent if we were * already connected, while ensuring we get connected for * new workspaces. */ g_signal_handlers_disconnect_by_func (workspace, cinnamon_window_tracker_on_window_added, self); g_signal_handlers_disconnect_by_func (workspace, cinnamon_window_tracker_on_window_removed, self); g_signal_connect (workspace, "window-added", G_CALLBACK (cinnamon_window_tracker_on_window_added), self); g_signal_connect (workspace, "window-removed", G_CALLBACK (cinnamon_window_tracker_on_window_removed), self); } } static void init_window_tracking (CinnamonWindowTracker *self) { MetaDisplay *display; MetaScreen *screen = cinnamon_global_get_screen (cinnamon_global_get ()); g_signal_connect (screen, "notify::n-workspaces", G_CALLBACK (cinnamon_window_tracker_on_n_workspaces_changed), self); display = meta_screen_get_display (screen); g_signal_connect (display, "notify::focus-window", G_CALLBACK (on_focus_window_changed), self); cinnamon_window_tracker_on_n_workspaces_changed (screen, NULL, self); } static void on_startup_sequence_changed (MetaScreen *screen, SnStartupSequence *sequence, CinnamonWindowTracker *self) { CinnamonApp *app; app = cinnamon_startup_sequence_get_app ((CinnamonStartupSequence*)sequence); if (app) _cinnamon_app_handle_startup_sequence (app, sequence); g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence); } static void cinnamon_window_tracker_init (CinnamonWindowTracker *self) { MetaScreen *screen; self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); self->launched_pid_to_app = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); screen = cinnamon_global_get_screen (cinnamon_global_get ()); g_signal_connect (G_OBJECT (screen), "startup-sequence-changed", G_CALLBACK (on_startup_sequence_changed), self); load_initial_windows (self); init_window_tracking (self); } static void cinnamon_window_tracker_finalize (GObject *object) { CinnamonWindowTracker *self = CINNAMON_WINDOW_TRACKER (object); g_hash_table_destroy (self->window_to_app); g_hash_table_destroy (self->launched_pid_to_app); G_OBJECT_CLASS (cinnamon_window_tracker_parent_class)->finalize(object); } /** * cinnamon_window_tracker_get_window_app: * @tracker: An app monitor instance * @metawin: A #MetaWindow * * Returns: (transfer full): Application associated with window */ CinnamonApp * cinnamon_window_tracker_get_window_app (CinnamonWindowTracker *tracker, MetaWindow *metawin) { MetaWindow *transient_for; CinnamonApp *app; transient_for = meta_window_get_transient_for (metawin); if (transient_for != NULL) metawin = transient_for; app = g_hash_table_lookup (tracker->window_to_app, metawin); if (app) g_object_ref (app); return app; } /** * cinnamon_window_tracker_get_app_from_pid: * @self; A #CinnamonAppSystem * @pid: A Unix process identifier * * Look up the application corresponding to a process. * * Returns: (transfer none): A #CinnamonApp, or %NULL if none */ CinnamonApp * cinnamon_window_tracker_get_app_from_pid (CinnamonWindowTracker *self, int pid) { GSList *running = cinnamon_app_system_get_running (cinnamon_app_system_get_default()); GSList *iter; CinnamonApp *result = NULL; for (iter = running; iter; iter = iter->next) { CinnamonApp *app = iter->data; GSList *pids = cinnamon_app_get_pids (app); GSList *pids_iter; for (pids_iter = pids; pids_iter; pids_iter = pids_iter->next) { int app_pid = GPOINTER_TO_INT (pids_iter->data); if (app_pid == pid) { result = app; break; } } g_slist_free (pids); if (result != NULL) break; } g_slist_free (running); return result; } static void on_child_exited (GPid pid, gint status, gpointer unused_data) { CinnamonWindowTracker *tracker; tracker = cinnamon_window_tracker_get_default (); g_hash_table_remove (tracker->launched_pid_to_app, GINT_TO_POINTER((gint)pid)); } void _cinnamon_window_tracker_add_child_process_app (CinnamonWindowTracker *tracker, GPid pid, CinnamonApp *app) { gpointer pid_ptr = GINT_TO_POINTER((int)pid); if (g_hash_table_lookup (tracker->launched_pid_to_app, &pid_ptr)) return; g_hash_table_insert (tracker->launched_pid_to_app, pid_ptr, g_object_ref (app)); g_child_watch_add (pid, on_child_exited, NULL); /* TODO: rescan unassociated windows * Unlikely in practice that the launched app gets ahead of us * enough to map an X window before we get scheduled after the fork(), * but adding this note for future reference. */ } static void set_focus_app (CinnamonWindowTracker *tracker, CinnamonApp *new_focus_app) { if (new_focus_app == tracker->focus_app) return; if (tracker->focus_app != NULL) g_object_unref (tracker->focus_app); tracker->focus_app = new_focus_app; if (tracker->focus_app != NULL) g_object_ref (tracker->focus_app); g_object_notify (G_OBJECT (tracker), "focus-app"); } static void on_focus_window_changed (MetaDisplay *display, GParamSpec *spec, CinnamonWindowTracker *tracker) { update_focus_app (tracker); } /** * cinnamon_window_tracker_get_startup_sequences: * @tracker: * * Returns: (transfer none) (element-type CinnamonStartupSequence): Currently active startup sequences */ GSList * cinnamon_window_tracker_get_startup_sequences (CinnamonWindowTracker *self) { CinnamonGlobal *global = cinnamon_global_get (); MetaScreen *screen = cinnamon_global_get_screen (global); return meta_screen_get_startup_sequences (screen); } /* sn_startup_sequence_ref returns void, so make a * wrapper which returns self */ static SnStartupSequence * sequence_ref (SnStartupSequence *sequence) { sn_startup_sequence_ref (sequence); return sequence; } GType cinnamon_startup_sequence_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("CinnamonStartupSequence", (GBoxedCopyFunc)sequence_ref, (GBoxedFreeFunc)sn_startup_sequence_unref); } return gtype; } const char * cinnamon_startup_sequence_get_id (CinnamonStartupSequence *sequence) { return sn_startup_sequence_get_id ((SnStartupSequence*)sequence); } /** * cinnamon_startup_sequence_get_app: * @sequence: A #CinnamonStartupSequence * * Returns: (transfer none): The application being launched, or %NULL if unknown. */ CinnamonApp * cinnamon_startup_sequence_get_app (CinnamonStartupSequence *sequence) { const char *appid; CinnamonAppSystem *appsys; CinnamonApp *app; appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence); if (!appid) return NULL; appsys = cinnamon_app_system_get_default (); app = cinnamon_app_system_lookup_app_for_path (appsys, appid); return app; } const char * cinnamon_startup_sequence_get_name (CinnamonStartupSequence *sequence) { return sn_startup_sequence_get_name ((SnStartupSequence*)sequence); } gboolean cinnamon_startup_sequence_get_completed (CinnamonStartupSequence *sequence) { return sn_startup_sequence_get_completed ((SnStartupSequence*)sequence); } /** * cinnamon_startup_sequence_create_icon: * @sequence: * @size: Size in pixels of icon * * Returns: (transfer none): A new #ClutterTexture containing an icon for the sequence */ ClutterActor * cinnamon_startup_sequence_create_icon (CinnamonStartupSequence *sequence, guint size) { GIcon *themed; const char *icon_name; ClutterActor *texture; icon_name = sn_startup_sequence_get_icon_name ((SnStartupSequence*)sequence); if (!icon_name) { texture = clutter_texture_new (); gint scale; CinnamonGlobal *global; StThemeContext *context; global = cinnamon_global_get (); context = st_theme_context_get_for_stage (cinnamon_global_get_stage (global)); g_object_get (context, "scale-factor", &scale, NULL); clutter_actor_set_size (texture, size * scale, size * scale); return texture; } themed = g_themed_icon_new (icon_name); texture = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, themed, size); g_object_unref (G_OBJECT (themed)); return texture; } /** * cinnamon_window_tracker_get_default: * * Return Value: (transfer none): The global #CinnamonWindowTracker instance */ CinnamonWindowTracker * cinnamon_window_tracker_get_default () { static CinnamonWindowTracker *instance; if (instance == NULL) instance = g_object_new (CINNAMON_TYPE_WINDOW_TRACKER, NULL); return instance; } Cinnamon-2.8.6/src/cinnamon-util.c0000664000175000017500000005460212625666430015746 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-util.h" #include #include #ifdef HAVE__NL_TIME_FIRST_WEEKDAY #include #endif #define DESKTOP_SCHEMA "org.cinnamon.desktop.interface" #define FIRST_WEEKDAY_KEY "first-day-of-week" #include #include #include /* Some code in this file adapted under the GPLv2+ from: * * GNOME panel utils: gnome-panel/gnome-panel/panel-util.c * (C) 1997, 1998, 1999, 2000 The Free Software Foundation * Copyright 2000 Helix Code, Inc. * Copyright 2000,2001 Eazel, Inc. * Copyright 2001 George Lebl * Copyright 2002 Sun Microsystems Inc. * * Authors: George Lebl * Jacob Berkman * Mark McLoughlin */ static GFile * cinnamon_util_get_gfile_root (GFile *file) { GFile *parent; GFile *parent_old; /* search for the root on the URI */ parent_old = g_object_ref (file); parent = g_file_get_parent (file); while (parent != NULL) { g_object_unref (parent_old); parent_old = parent; parent = g_file_get_parent (parent); } return parent_old; } static char * cinnamon_util_get_file_display_name_if_mount (GFile *file) { GFile *compare; GVolumeMonitor *monitor; GList *mounts, *l; char *ret; ret = NULL; /* compare with all mounts */ monitor = g_volume_monitor_get (); mounts = g_volume_monitor_get_mounts (monitor); for (l = mounts; l != NULL; l = l->next) { GMount *mount; mount = G_MOUNT(l->data); compare = g_mount_get_root (mount); if (!ret && g_file_equal (file, compare)) ret = g_mount_get_name (mount); g_object_unref (mount); } g_list_free (mounts); g_object_unref (monitor); return ret; } static char * cinnamon_util_get_file_display_for_common_files (GFile *file) { GFile *compare; compare = g_file_new_for_path (g_get_home_dir ()); if (g_file_equal (file, compare)) { g_object_unref (compare); /* Translators: this is the same string as the one found in * nemo */ return g_strdup (_("Home")); } compare = g_file_new_for_path ("/"); if (g_file_equal (file, compare)) { g_object_unref (compare); /* Translators: this is the same string as the one found in * nemo */ return g_strdup (_("File System")); } g_object_unref (compare); return NULL; } static char * cinnamon_util_get_file_description (GFile *file) { GFileInfo *info; char *ret; ret = NULL; info = g_file_query_info (file, "standard::description", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); if (info) { ret = g_strdup (g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION)); g_object_unref (info); } return ret; } static char * cinnamon_util_get_file_display_name (GFile *file, gboolean use_fallback) { GFileInfo *info; char *ret; ret = NULL; info = g_file_query_info (file, "standard::display-name", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); if (info) { ret = g_strdup (g_file_info_get_display_name (info)); g_object_unref (info); } if (!ret && use_fallback) { /* can happen with URI schemes non supported by gvfs */ char *basename; basename = g_file_get_basename (file); ret = g_filename_display_name (basename); g_free (basename); } return ret; } static GIcon * cinnamon_util_get_file_icon_if_mount (GFile *file) { GFile *compare; GVolumeMonitor *monitor; GList *mounts, *l; GIcon *ret; ret = NULL; /* compare with all mounts */ monitor = g_volume_monitor_get (); mounts = g_volume_monitor_get_mounts (monitor); for (l = mounts; l != NULL; l = l->next) { GMount *mount; mount = G_MOUNT (l->data); compare = g_mount_get_root (mount); if (!ret && g_file_equal (file, compare)) { ret = g_mount_get_icon (mount); } g_object_unref (mount); } g_list_free (mounts); g_object_unref (monitor); return ret; } static const char * cinnamon_util_get_icon_for_uri_known_folders (const char *uri) { const char *icon; char *path; int len; icon = NULL; if (!g_str_has_prefix (uri, "file:")) return NULL; path = g_filename_from_uri (uri, NULL, NULL); len = strlen (path); if (path[len] == '/') path[len] = '\0'; if (strcmp (path, "/") == 0) icon = "drive-harddisk"; else if (strcmp (path, g_get_home_dir ()) == 0) icon = "user-home"; else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0) icon = "user-desktop"; g_free (path); return icon; } /* This is based on nautilus_compute_title_for_uri() and * nautilus_file_get_display_name_nocopy() */ char * cinnamon_util_get_label_for_uri (const char *text_uri) { GFile *file; char *label; GFile *root; char *root_display; /* Here's what we do: * + x-nautilus-search: URI * + check if the URI is a mount * + if file: URI: * - check for known file: URI * - check for description of the GFile * - use display name of the GFile * + else: * - check for description of the GFile * - if the URI is a root: "root displayname" * - else: "root displayname: displayname" */ label = NULL; //FIXME: see nautilus_query_to_readable_string() to have a nice name if (g_str_has_prefix (text_uri, "x-nautilus-search:")) return g_strdup (_("Search")); file = g_file_new_for_uri (text_uri); label = cinnamon_util_get_file_display_name_if_mount (file); if (label) { g_object_unref (file); return label; } if (g_str_has_prefix (text_uri, "file:")) { label = cinnamon_util_get_file_display_for_common_files (file); if (!label) label = cinnamon_util_get_file_description (file); if (!label) label = cinnamon_util_get_file_display_name (file, TRUE); g_object_unref (file); return label; } label = cinnamon_util_get_file_description (file); if (label) { g_object_unref (file); return label; } root = cinnamon_util_get_gfile_root (file); root_display = cinnamon_util_get_file_description (root); if (!root_display) root_display = cinnamon_util_get_file_display_name (root, FALSE); if (!root_display) /* can happen with URI schemes non supported by gvfs */ root_display = g_file_get_uri_scheme (root); if (g_file_equal (file, root)) label = root_display; else { char *displayname; displayname = cinnamon_util_get_file_display_name (file, TRUE); /* Translators: the first string is the name of a gvfs * method, and the second string is a path. For * example, "Trash: some-directory". It means that the * directory called "some-directory" is in the trash. */ label = g_strdup_printf (_("%1$s: %2$s"), root_display, displayname); g_free (root_display); g_free (displayname); } g_object_unref (root); g_object_unref (file); return label; } /** * cinnamon_util_get_icon_for_uri: * @text_uri: A URI * * Look up the icon that should be associated with a given URI. Handles * various special GNOME-internal cases like x-nautilus-search, etc. * * Return Value: (transfer full): A new #GIcon */ GIcon * cinnamon_util_get_icon_for_uri (const char *text_uri) { const char *name; GFile *file; GFileInfo *info; GIcon *retval; /* Here's what we do: * + check for known file: URI * + x-nautilus-search: URI * + override burn: URI icon * + check if the URI is a mount * + override trash: URI icon for subfolders * + check for application/x-gnome-saved-search mime type and override * icon of the GFile * + use icon of the GFile */ /* this only checks file: URI */ name = cinnamon_util_get_icon_for_uri_known_folders (text_uri); if (name) return g_themed_icon_new (name); if (g_str_has_prefix (text_uri, "x-nautilus-search:")) return g_themed_icon_new ("folder-saved-search"); /* gvfs doesn't give us a nice icon, so overriding */ if (g_str_has_prefix (text_uri, "burn:")) return g_themed_icon_new ("nautilus-cd-burner"); file = g_file_new_for_uri (text_uri); retval = cinnamon_util_get_file_icon_if_mount (file); if (retval) return retval; /* gvfs doesn't give us a nice icon for subfolders of the trash, so * overriding */ if (g_str_has_prefix (text_uri, "trash:")) { GFile *root; root = cinnamon_util_get_gfile_root (file); g_object_unref (file); file = root; } info = g_file_query_info (file, "standard::icon", G_FILE_QUERY_INFO_NONE, NULL, NULL); g_object_unref (file); if (!info) return g_themed_icon_new ("gtk-file"); retval = g_file_info_get_icon (info); if (retval) g_object_ref (retval); g_object_unref (info); if (retval) return retval; return g_themed_icon_new ("gtk-file"); } static void stop_pick (ClutterActor *actor, const ClutterColor *color) { g_signal_stop_emission_by_name (actor, "pick"); } /** * cinnamon_util_set_hidden_from_pick: * @actor: A #ClutterActor * @hidden: Whether @actor should be hidden from pick * * If @hidden is %TRUE, hide @actor from pick even with a mode of * %CLUTTER_PICK_ALL; if @hidden is %FALSE, unhide @actor. */ void cinnamon_util_set_hidden_from_pick (ClutterActor *actor, gboolean hidden) { gpointer existing_handler_data; existing_handler_data = g_object_get_data (G_OBJECT (actor), "cinnamon-stop-pick"); if (hidden) { if (existing_handler_data != NULL) return; g_signal_connect (actor, "pick", G_CALLBACK (stop_pick), NULL); g_object_set_data (G_OBJECT (actor), "cinnamon-stop-pick", GUINT_TO_POINTER (1)); } else { if (existing_handler_data == NULL) return; g_signal_handlers_disconnect_by_func (actor, stop_pick, NULL); g_object_set_data (G_OBJECT (actor), "cinnamon-stop-pick", NULL); } } /** * cinnamon_util_get_transformed_allocation: * @actor: a #ClutterActor * @box: (out): location to store returned box in stage coordinates * * This function is similar to a combination of clutter_actor_get_transformed_position(), * and clutter_actor_get_transformed_size(), but unlike * clutter_actor_get_transformed_size(), it always returns a transform * of the current allocation, while clutter_actor_get_transformed_size() returns * bad values (the transform of the requested size) if a relayout has been * queued. * * This function is more convenient to use than * clutter_actor_get_abs_allocation_vertices() if no transformation is in effect * and also works around limitations in the GJS binding of arrays. */ void cinnamon_util_get_transformed_allocation (ClutterActor *actor, ClutterActorBox *box) { /* Code adapted from clutter-actor.c: * Copyright 2006, 2007, 2008 OpenedHand Ltd */ ClutterVertex v[4]; gfloat x_min, x_max, y_min, y_max; gint i; g_return_if_fail (CLUTTER_IS_ACTOR (actor)); clutter_actor_get_abs_allocation_vertices (actor, v); x_min = x_max = v[0].x; y_min = y_max = v[0].y; for (i = 1; i < G_N_ELEMENTS (v); ++i) { if (v[i].x < x_min) x_min = v[i].x; if (v[i].x > x_max) x_max = v[i].x; if (v[i].y < y_min) y_min = v[i].y; if (v[i].y > y_max) y_max = v[i].y; } box->x1 = x_min; box->y1 = y_min; box->x2 = x_max; box->y2 = y_max; } char * cinnamon_util_normalize_and_casefold (const char *str) { char *normalized, *result; if (str == NULL) return NULL; normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); result = g_utf8_casefold (normalized, -1); g_free (normalized); return result; } /** * cinnamon_util_format_date: * @format: a strftime-style string format, as parsed by * g_date_time_format() * @time_ms: milliseconds since 1970-01-01 00:00:00 UTC; the * value returned by Date.getTime() * * Formats a date for the current locale. This should be * used instead of the Spidermonkey Date.toLocaleFormat() * extension because Date.toLocaleFormat() is buggy for * Unicode format strings: * https://bugzilla.mozilla.org/show_bug.cgi?id=508783 * * Return value: the formatted date. If the date is * outside of the range of a GDateTime (which contains * any plausible dates we actually care about), will * return an empty string. */ char * cinnamon_util_format_date (const char *format, gint64 time_ms) { GDateTime *datetime; GTimeVal tv; char *result; tv.tv_sec = time_ms / 1000; tv.tv_usec = (time_ms % 1000) * 1000; datetime = g_date_time_new_from_timeval_local (&tv); if (!datetime) /* time_ms is out of range of GDateTime */ return g_strdup (""); result = g_date_time_format (datetime, format); g_date_time_unref (datetime); return result; } /** * cinnamon_util_get_week_start: * * Gets the first week day for the current locale, expressed as a * number in the range 0..6, representing week days from Sunday to * Saturday. * * Returns: A number representing the first week day for the current * locale */ /* Copied from gtkcalendar.c */ int cinnamon_util_get_week_start () { /* Try to get first weekday from gsettings */ /* If the value from gsettings is not in the range 0-6, * continue to get the locale's first weekday */ GSettings *settings = g_settings_new (DESKTOP_SCHEMA); int week_start = g_settings_get_int (settings, FIRST_WEEKDAY_KEY); g_object_unref (settings); if (0 <= week_start && week_start < 7) return week_start; #ifdef HAVE__NL_TIME_FIRST_WEEKDAY union { unsigned int word; char *string; } langinfo; int week_1stday = 0; int first_weekday = 1; guint week_origin; #else char *gtk_week_start; #endif #ifdef HAVE__NL_TIME_FIRST_WEEKDAY langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY); first_weekday = langinfo.string[0]; langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY); week_origin = langinfo.word; if (week_origin == 19971130) /* Sunday */ week_1stday = 0; else if (week_origin == 19971201) /* Monday */ week_1stday = 1; else g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n"); week_start = (week_1stday + first_weekday - 1) % 7; #else /* Use a define to hide the string from xgettext */ # define GTK_WEEK_START "calendar:week_start:0" gtk_week_start = dgettext ("gtk30", GTK_WEEK_START); if (strncmp (gtk_week_start, "calendar:week_start:", 20) == 0) week_start = *(gtk_week_start + 20) - '0'; else week_start = -1; if (week_start < 0 || week_start > 6) { g_warning ("Whoever translated calendar:week_start:0 for GTK+ " "did so wrongly.\n"); week_start = 0; } #endif return week_start; } /** * cinnamon_get_event_state: * @event: a #ClutterEvent * * Gets the current state of the event (the set of modifier keys that * are pressed down). Thhis is a wrapper around * clutter_event_get_state() that strips out any un-declared modifier * flags, to make gjs happy; see * https://bugzilla.gnome.org/show_bug.cgi?id=597292. * * Return value: the state from the event */ ClutterModifierType cinnamon_get_event_state (ClutterEvent *event) { ClutterModifierType state = clutter_event_get_state (event); return state & CLUTTER_MODIFIER_MASK; } /** * cinnamon_write_soup_message_to_stream: * @stream: a #GOutputStream * @message: a #SoupMessage * @error: location to store GError * * Write a string to a GOutputStream as binary data. This is a * workaround for the lack of proper binary strings in GJS. */ void cinnamon_write_soup_message_to_stream (GOutputStream *stream, SoupMessage *message, GError **error) { SoupMessageBody *body; body = message->response_body; g_output_stream_write_all (stream, body->data, body->length, NULL, NULL, error); } /** * cinnamon_write_string_to_stream: * @stream: a #GOutputStream * @str: a UTF-8 string to write to @stream * @error: location to store GError * * Write a string to a GOutputStream as UTF-8. This is a workaround * for not having binary buffers in GJS. * * Return value: %TRUE if write succeeded */ gboolean cinnamon_write_string_to_stream (GOutputStream *stream, const char *str, GError **error) { return g_output_stream_write_all (stream, str, strlen (str), NULL, NULL, error); } /** * cinnamon_get_file_contents_utf8_sync: * @path: UTF-8 encoded filename path * @error: a #GError * * Synchronously load the contents of a file as a NUL terminated * string, validating it as UTF-8. Embedded NUL characters count as * invalid content. * * Returns: (transfer full): File contents */ char * cinnamon_get_file_contents_utf8_sync (const char *path, GError **error) { char *contents; gsize len; if (!g_file_get_contents (path, &contents, &len, error)) return NULL; if (!g_utf8_validate (contents, len, NULL)) { g_free (contents); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "File %s contains invalid UTF-8", path); return NULL; } return contents; } /** * cinnamon_breakpoint: * * Using G_BREAKPOINT(), interrupt the current process. This is useful * in conjunction with a debugger such as gdb. */ void cinnamon_breakpoint (void) { G_BREAKPOINT (); } /** * cinnamon_parse_search_provider: * @data: description of provider * @name: (out): location to store a display name * @url: (out): location to store template of url * @langs: (out) (transfer full) (element-type utf8): list of supported languages * @icon_data_uri: (out): location to store uri * @error: location to store GError * * Returns: %TRUE on success */ gboolean cinnamon_parse_search_provider (const char *data, char **name, char **url, GList **langs, char **icon_data_uri, GError **error) { xmlDocPtr doc = xmlParseMemory (data, strlen (data)); xmlNode *root; *name = NULL; *url = NULL; *icon_data_uri = NULL; *langs = NULL; if (!doc) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Malformed xml"); return FALSE; } root = xmlDocGetRootElement (doc); if (root && root->name && xmlStrcmp (root->name, (const xmlChar *)"OpenSearchDescription") == 0) { xmlNode *child; for (child = root->children; child; child = child->next) { if (!child->name) continue; if (xmlStrcmp (child->name, (const xmlChar *)"Language") == 0) { xmlChar *val = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); if (!val) continue; *langs = g_list_append (*langs, g_strdup ((char *)val)); xmlFree (val); } if (!*name && xmlStrcmp (child->name, (const xmlChar *)"ShortName") == 0) { xmlChar *val = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); *name = g_strdup ((char *)val); xmlFree (val); } if (!*icon_data_uri && xmlStrcmp (child->name, (const xmlChar *)"Image") == 0) { xmlChar *val = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); if (val) *icon_data_uri = g_strdup ((char *)val); xmlFree (val); } if (!*url && xmlStrcmp (child->name, (const xmlChar *)"Url") == 0) { xmlChar *template; xmlChar *type; type = xmlGetProp(child, (const xmlChar *)"type"); if (!type) continue; if (xmlStrcmp (type, (const xmlChar *)"text/html") != 0) { xmlFree (type); continue; } xmlFree (type); template = xmlGetProp(child, (const xmlChar *)"template"); if (!template) continue; *url = g_strdup ((char *)template); xmlFree (template); } } } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid OpenSearch document"); xmlFreeDoc (doc); return FALSE; } xmlFreeDoc (doc); if (*icon_data_uri && *name && *url) return TRUE; if (*icon_data_uri) g_free (*icon_data_uri); else g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "search provider doesn't have icon"); if (*name) g_free (*name); else if (error && !*error) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "search provider doesn't have ShortName"); if (*url) g_free (*url); else if (error && !*error) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "search provider doesn't have template for url"); if (*langs) { g_list_foreach (*langs, (GFunc)g_free, NULL); g_list_free (*langs); } *url = NULL; *name = NULL; *icon_data_uri = NULL; *langs = NULL; return FALSE; } /** * cinnamon_shader_effect_set_double_uniform: * @effect: The #ClutterShaderEffect * @name: The name of the uniform * @value: The value to set it to. * * Set a double uniform on a ClutterShaderEffect. * * The problem here is that JavaScript doesn't have more than * one number type, and gjs tries to automatically guess what * type we want to set a GValue to. If the number is "1.0" or * something, it will use an integer, which will cause errors * in GLSL. */ void cinnamon_shader_effect_set_double_uniform (ClutterShaderEffect *effect, const gchar *name, gdouble value) { GValue gvalue = G_VALUE_INIT; g_value_init (&gvalue, G_TYPE_DOUBLE); g_value_set_double (&gvalue, value); clutter_shader_effect_set_uniform_value (effect, name, &gvalue); } Cinnamon-2.8.6/src/cinnamon-wm.c0000664000175000017500000002605612625666430015416 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include "cinnamon-wm-private.h" #include "cinnamon-global.h" #include "cinnamon-marshal.h" struct _CinnamonWM { GObject parent; MetaPlugin *plugin; }; /* Signals */ enum { MINIMIZE, MAXIMIZE, UNMAXIMIZE, TILE, MAP, DESTROY, SWITCH_WORKSPACE, SWITCH_WORKSPACE_COMPLETE, KILL_WINDOW_EFFECTS, SHOW_TILE_PREVIEW, HIDE_TILE_PREVIEW, SHOW_HUD_PREVIEW, HIDE_HUD_PREVIEW, LAST_SIGNAL }; G_DEFINE_TYPE(CinnamonWM, cinnamon_wm, G_TYPE_OBJECT); static guint cinnamon_wm_signals [LAST_SIGNAL] = { 0 }; static void cinnamon_wm_init (CinnamonWM *wm) { } static void cinnamon_wm_finalize (GObject *object) { G_OBJECT_CLASS (cinnamon_wm_parent_class)->finalize (object); } static void cinnamon_wm_class_init (CinnamonWMClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = cinnamon_wm_finalize; cinnamon_wm_signals[MINIMIZE] = g_signal_new ("minimize", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW_ACTOR); cinnamon_wm_signals[MAXIMIZE] = g_signal_new ("maximize", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _cinnamon_marshal_VOID__OBJECT_INT_INT_INT_INT, G_TYPE_NONE, 5, META_TYPE_WINDOW_ACTOR, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); cinnamon_wm_signals[UNMAXIMIZE] = g_signal_new ("unmaximize", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _cinnamon_marshal_VOID__OBJECT_INT_INT_INT_INT, G_TYPE_NONE, 5, META_TYPE_WINDOW_ACTOR, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); cinnamon_wm_signals[TILE] = g_signal_new ("tile", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _cinnamon_marshal_VOID__OBJECT_INT_INT_INT_INT, G_TYPE_NONE, 5, META_TYPE_WINDOW_ACTOR, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); cinnamon_wm_signals[MAP] = g_signal_new ("map", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW_ACTOR); cinnamon_wm_signals[DESTROY] = g_signal_new ("destroy", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW_ACTOR); cinnamon_wm_signals[SWITCH_WORKSPACE] = g_signal_new ("switch-workspace", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _cinnamon_marshal_VOID__INT_INT_INT, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); cinnamon_wm_signals[SWITCH_WORKSPACE_COMPLETE] = g_signal_new ("switch-workspace-complete", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); cinnamon_wm_signals[KILL_WINDOW_EFFECTS] = g_signal_new ("kill-window-effects", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW_ACTOR); cinnamon_wm_signals[SHOW_TILE_PREVIEW] = g_signal_new ("show-tile-preview", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 4, META_TYPE_WINDOW, META_TYPE_RECTANGLE, G_TYPE_INT, G_TYPE_UINT); cinnamon_wm_signals[HIDE_TILE_PREVIEW] = g_signal_new ("hide-tile-preview", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); cinnamon_wm_signals[SHOW_HUD_PREVIEW] = g_signal_new ("show-hud-preview", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_UINT, META_TYPE_RECTANGLE, G_TYPE_UINT); cinnamon_wm_signals[HIDE_HUD_PREVIEW] = g_signal_new ("hide-hud-preview", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } void _cinnamon_wm_switch_workspace (CinnamonWM *wm, gint from, gint to, MetaMotionDirection direction) { g_signal_emit (wm, cinnamon_wm_signals[SWITCH_WORKSPACE], 0, from, to, direction); } /** * cinnamon_wm_completed_switch_workspace: * @wm: the CinnamonWM * * The plugin must call this when it has finished switching the * workspace. **/ void cinnamon_wm_completed_switch_workspace (CinnamonWM *wm) { meta_plugin_switch_workspace_completed (wm->plugin); g_signal_emit (wm, cinnamon_wm_signals[SWITCH_WORKSPACE_COMPLETE], 0); } /** * cinnamon_wm_completed_minimize: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window minimize effect. **/ void cinnamon_wm_completed_minimize (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_minimize_completed (wm->plugin, actor); } /** * cinnamon_wm_completed_maximize: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window maximize effect. **/ void cinnamon_wm_completed_maximize (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_maximize_completed (wm->plugin, actor); } /** * cinnamon_wm_completed_tile: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window tile effect. **/ void cinnamon_wm_completed_tile (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_tile_completed (wm->plugin, actor); } /** * cinnamon_wm_completed_unmaximize: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window unmaximize effect. **/ void cinnamon_wm_completed_unmaximize (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_unmaximize_completed (wm->plugin, actor); } /** * cinnamon_wm_completed_map: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window map effect. **/ void cinnamon_wm_completed_map (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_map_completed (wm->plugin, actor); } /** * cinnamon_wm_completed_destroy: * @wm: the CinnamonWM * @actor: the MetaWindowActor actor * * The plugin must call this when it has completed a window destroy effect. **/ void cinnamon_wm_completed_destroy (CinnamonWM *wm, MetaWindowActor *actor) { meta_plugin_destroy_completed (wm->plugin, actor); } void _cinnamon_wm_kill_window_effects (CinnamonWM *wm, MetaWindowActor *actor) { g_signal_emit (wm, cinnamon_wm_signals[KILL_WINDOW_EFFECTS], 0, actor); } void _cinnamon_wm_show_tile_preview (CinnamonWM *wm, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor, guint snap_queued) { g_signal_emit (wm, cinnamon_wm_signals[SHOW_TILE_PREVIEW], 0, window, tile_rect, tile_monitor, snap_queued); } void _cinnamon_wm_hide_tile_preview (CinnamonWM *wm) { g_signal_emit (wm, cinnamon_wm_signals[HIDE_TILE_PREVIEW], 0); } void _cinnamon_wm_show_hud_preview (CinnamonWM *wm, guint current_proximity_zone, MetaRectangle *work_area, guint snap_queued) { g_signal_emit (wm, cinnamon_wm_signals[SHOW_HUD_PREVIEW], 0, current_proximity_zone, work_area, snap_queued); } void _cinnamon_wm_hide_hud_preview (CinnamonWM *wm) { g_signal_emit (wm, cinnamon_wm_signals[HIDE_HUD_PREVIEW], 0); } void _cinnamon_wm_minimize (CinnamonWM *wm, MetaWindowActor *actor) { g_signal_emit (wm, cinnamon_wm_signals[MINIMIZE], 0, actor); } void _cinnamon_wm_maximize (CinnamonWM *wm, MetaWindowActor *actor, int target_x, int target_y, int target_width, int target_height) { g_signal_emit (wm, cinnamon_wm_signals[MAXIMIZE], 0, actor, target_x, target_y, target_width, target_height); } void _cinnamon_wm_unmaximize (CinnamonWM *wm, MetaWindowActor *actor, int target_x, int target_y, int target_width, int target_height) { g_signal_emit (wm, cinnamon_wm_signals[UNMAXIMIZE], 0, actor, target_x, target_y, target_width, target_height); } void _cinnamon_wm_tile (CinnamonWM *wm, MetaWindowActor *actor, int target_x, int target_y, int target_width, int target_height) { g_signal_emit (wm, cinnamon_wm_signals[TILE], 0, actor, target_x, target_y, target_width, target_height); } void _cinnamon_wm_map (CinnamonWM *wm, MetaWindowActor *actor) { g_signal_emit (wm, cinnamon_wm_signals[MAP], 0, actor); } void _cinnamon_wm_destroy (CinnamonWM *wm, MetaWindowActor *actor) { g_signal_emit (wm, cinnamon_wm_signals[DESTROY], 0, actor); } /** * cinnamon_wm_new: * @plugin: the #MetaPlugin * * Creates a new window management interface by hooking into @plugin. * * Return value: the new window-management interface **/ CinnamonWM * cinnamon_wm_new (MetaPlugin *plugin) { CinnamonWM *wm; wm = g_object_new (CINNAMON_TYPE_WM, NULL); wm->plugin = plugin; return wm; } Cinnamon-2.8.6/src/cinnamon-perf-helper.c0000664000175000017500000002142112625666430017173 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* cinnamon-perf-helper: a program to create windows for performance tests * * Running performance tests with whatever windows a user has open results * in unreliable results, so instead we hide all other windows and talk * to this program over D-Bus to create just the windows we want. */ #include "config.h" #include #include #define BUS_NAME "org.Cinnamon.PerfHelper" static void destroy_windows (void); static void finish_wait_windows (void); static void check_finish_wait_windows (void); static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " ""; typedef struct { GtkWidget *window; int width; int height; guint alpha : 1; guint maximized : 1; guint mapped : 1; guint exposed : 1; guint pending : 1; } WindowInfo; static int opt_idle_timeout = 30; static GOptionEntry opt_entries[] = { { "idle-timeout", 'r', 0, G_OPTION_ARG_INT, &opt_idle_timeout, "Exit after N seconds", "N" }, { NULL } }; static Display *xdisplay; static Window xroot; static Atom atom_wm_state; static Atom atom__net_wm_name; static Atom atom_utf8_string; static guint timeout_id; static GList *our_windows; static GList *wait_windows_invocations; static gboolean on_timeout (gpointer data) { timeout_id = 0; destroy_windows (); gtk_main_quit (); return FALSE; } static void establish_timeout () { if (timeout_id != 0){ g_source_remove (timeout_id); timeout_id = 0; } timeout_id = g_timeout_add (opt_idle_timeout * 1000, on_timeout, NULL); } static void destroy_windows (void) { GList *l; for (l = our_windows; l; l = l->next) { WindowInfo *info = l->data; gtk_widget_destroy (info->window); g_free (info); } g_list_free (our_windows); our_windows = NULL; check_finish_wait_windows (); } static gboolean on_window_map_event (GtkWidget *window, GdkEventAny *event, WindowInfo *info) { info->mapped = TRUE; return FALSE; } static gboolean on_window_draw (GtkWidget *window, cairo_t *cr, WindowInfo *info) { cairo_rectangle_int_t allocation; gtk_widget_get_allocation (window, &allocation); /* We draw an arbitrary pattern of red lines near the border of the * window to make it more clear than empty windows if something * is drastrically wrong. */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); if (info->alpha) cairo_set_source_rgba (cr, 1, 1, 1, 0.5); else cairo_set_source_rgb (cr, 1, 1, 1); cairo_paint (cr); cairo_restore (cr); cairo_set_source_rgb (cr, 1, 0, 0); cairo_set_line_width (cr, 10); cairo_move_to (cr, 0, 40); cairo_line_to (cr, allocation.width, 40); cairo_move_to (cr, 0, allocation.height - 40); cairo_line_to (cr, allocation.width, allocation.height - 40); cairo_move_to (cr, 40, 0); cairo_line_to (cr, 40, allocation.height); cairo_move_to (cr, allocation.width - 40, 0); cairo_line_to (cr, allocation.width - 40, allocation.height); cairo_stroke (cr); info->exposed = TRUE; if (info->exposed && info->mapped && info->pending) { info->pending = FALSE; check_finish_wait_windows (); } return FALSE; } static void create_window (int width, int height, gboolean alpha, gboolean maximized) { WindowInfo *info; info = g_new0 (WindowInfo, 1); info->width = width; info->height = height; info->alpha = alpha; info->maximized = maximized; info->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); if (alpha) gtk_widget_set_visual (info->window, gdk_screen_get_rgba_visual (gdk_screen_get_default ())); if (maximized) gtk_window_maximize (GTK_WINDOW (info->window)); info->pending = TRUE; gtk_widget_set_size_request (info->window, width, height); gtk_widget_set_app_paintable (info->window, TRUE); g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info); g_signal_connect (info->window, "draw", G_CALLBACK (on_window_draw), info); gtk_widget_show (info->window); our_windows = g_list_prepend (our_windows, info); } static void finish_wait_windows (void) { GList *l; for (l = wait_windows_invocations; l; l = l->next) g_dbus_method_invocation_return_value (l->data, NULL); g_list_free (wait_windows_invocations); wait_windows_invocations = NULL; } static void check_finish_wait_windows (void) { GList *l; gboolean have_pending = FALSE; for (l = our_windows; l; l = l->next) { WindowInfo *info = l->data; if (info->pending) have_pending = TRUE; } if (!have_pending) finish_wait_windows (); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { /* Push off the idle timeout */ establish_timeout (); if (g_strcmp0 (method_name, "Exit") == 0) { destroy_windows (); g_dbus_method_invocation_return_value (invocation, NULL); g_dbus_connection_flush_sync (connection, NULL, NULL); gtk_main_quit (); } else if (g_strcmp0 (method_name, "CreateWindow") == 0) { int width, height; gboolean alpha, maximized; g_variant_get (parameters, "(iibb)", &width, &height, &alpha, &maximized); create_window (width, height, alpha, maximized); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "WaitWindows") == 0) { wait_windows_invocations = g_list_prepend (wait_windows_invocations, invocation); check_finish_wait_windows (); } else if (g_strcmp0 (method_name, "DestroyWindows") == 0) { destroy_windows (); g_dbus_method_invocation_return_value (invocation, NULL); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, NULL }; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_dbus_connection_register_object (connection, "/org/Cinnamon/PerfHelper", introspection_data->interfaces[0], &interface_vtable, NULL, /* user_data */ NULL, /* user_data_free_func */ NULL); /* GError** */ } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { destroy_windows (); gtk_main_quit (); } int main (int argc, char **argv) { GdkDisplay *display; GdkScreen *screen; GOptionContext *context; GError *error = NULL; /* Since we depend on this, avoid the possibility of lt-cinnamon-perf-helper */ g_set_prgname ("cinnamon-perf-helper"); context = g_option_context_new (" - server to create windows for performance testing"); g_option_context_add_main_entries (context, opt_entries, NULL); g_option_context_add_group (context, gtk_get_option_group (TRUE)); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); g_option_context_free (context); return 1; } display = gdk_display_get_default (); screen = gdk_screen_get_default (); xdisplay = gdk_x11_display_get_xdisplay (display); xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)); atom_wm_state = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE"); atom__net_wm_name = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"); atom_utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"); g_bus_own_name (G_BUS_TYPE_SESSION, BUS_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); establish_timeout (); gtk_main (); g_option_context_free (context); return 0; } Cinnamon-2.8.6/src/cinnamon-enum-types.h.in0000664000175000017500000000102512625666430017500 0ustar fabiofabio/*** BEGIN file-header ***/ #ifndef __CINNAMON_ENUM_TYPES_H__ #define __CINNAMON_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__CINNAMON_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define CINNAMON_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ Cinnamon-2.8.6/src/cinnamon-js.h0000664000175000017500000000170612625666430015407 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_JS_H__ #define __CINNAMON_JS_H__ #include #include G_BEGIN_DECLS gboolean cinnamon_js_add_extension_importer (const char *target_object_script, const char *target_property, const char *directory, GError **error); typedef struct { guint glibc_uordblks; guint js_bytes; guint gjs_boxed; guint gjs_gobject; guint gjs_function; guint gjs_closure; /* 32 bit to avoid js conversion problems with 64 bit */ guint last_gc_seconds_ago; } CinnamonJSMemoryInfo; void cinnamon_js_get_memory_info (gpointer ptr, gint64 last_gc, CinnamonJSMemoryInfo *meminfo); G_END_DECLS #endif /* __CINNAMON_JS_H__ */ Cinnamon-2.8.6/src/cinnamon-plugin.c0000664000175000017500000004100712625666430016262 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Red Hat, Inc. * Copyright (c) 2008 Intel Corp. * * Based on plugin skeleton by: * Author: Tomas Frydrych * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. */ #include "config.h" #include #include #include #include #if defined (__arm__) #else #include #include #endif #include #include #include #include "cinnamon-global-private.h" #include "cinnamon-perf-log.h" #include "cinnamon-wm-private.h" static void gnome_cinnamon_plugin_dispose (GObject *object); static void gnome_cinnamon_plugin_finalize (GObject *object); static void gnome_cinnamon_plugin_start (MetaPlugin *plugin); static void gnome_cinnamon_plugin_minimize (MetaPlugin *plugin, MetaWindowActor *actor); static void gnome_cinnamon_plugin_maximize (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height); static void gnome_cinnamon_plugin_unmaximize (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height); static void gnome_cinnamon_plugin_tile (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height); static void gnome_cinnamon_plugin_map (MetaPlugin *plugin, MetaWindowActor *actor); static void gnome_cinnamon_plugin_destroy (MetaPlugin *plugin, MetaWindowActor *actor); static void gnome_cinnamon_plugin_switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction); static void gnome_cinnamon_plugin_show_tile_preview (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor, guint snap_queued); static void gnome_cinnamon_plugin_hide_tile_preview (MetaPlugin *plugin); static void gnome_cinnamon_plugin_show_hud_preview (MetaPlugin *plugin, guint current_proximity_zone, MetaRectangle *work_area, guint snap_queued); static void gnome_cinnamon_plugin_hide_hud_preview (MetaPlugin *plugin); static void gnome_cinnamon_plugin_kill_window_effects (MetaPlugin *plugin, MetaWindowActor *actor); static gboolean gnome_cinnamon_plugin_xevent_filter (MetaPlugin *plugin, XEvent *event); static const MetaPluginInfo *gnome_cinnamon_plugin_plugin_info (MetaPlugin *plugin); #define GNOME_TYPE_CINNAMON_PLUGIN (gnome_cinnamon_plugin_get_type ()) #define CINNAMON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPlugin)) #define CINNAMON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPluginClass)) #define GNOME_IS_CINNAMON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_PLUGIN_TYPE)) #define GNOME_IS_CINNAMON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CINNAMON_PLUGIN)) #define CINNAMON_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPluginClass)) typedef struct _CinnamonPlugin CinnamonPlugin; typedef struct _CinnamonPluginClass CinnamonPluginClass; struct _CinnamonPlugin { MetaPlugin parent; Atom panel_action; Atom panel_action_run_dialog; Atom panel_action_main_menu; int glx_error_base; int glx_event_base; guint have_swap_event : 1; CinnamonGlobal *global; }; struct _CinnamonPluginClass { MetaPluginClass parent_class; }; GType gnome_cinnamon_plugin_get_type (void); G_DEFINE_TYPE (CinnamonPlugin, gnome_cinnamon_plugin, META_TYPE_PLUGIN) static void gnome_cinnamon_plugin_class_init (CinnamonPluginClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass); gobject_class->dispose = gnome_cinnamon_plugin_dispose; gobject_class->finalize = gnome_cinnamon_plugin_finalize; plugin_class->start = gnome_cinnamon_plugin_start; plugin_class->map = gnome_cinnamon_plugin_map; plugin_class->minimize = gnome_cinnamon_plugin_minimize; plugin_class->maximize = gnome_cinnamon_plugin_maximize; plugin_class->tile = gnome_cinnamon_plugin_tile; plugin_class->unmaximize = gnome_cinnamon_plugin_unmaximize; plugin_class->destroy = gnome_cinnamon_plugin_destroy; plugin_class->switch_workspace = gnome_cinnamon_plugin_switch_workspace; plugin_class->show_tile_preview = gnome_cinnamon_plugin_show_tile_preview; plugin_class->hide_tile_preview = gnome_cinnamon_plugin_hide_tile_preview; plugin_class->show_hud_preview = gnome_cinnamon_plugin_show_hud_preview; plugin_class->hide_hud_preview = gnome_cinnamon_plugin_hide_hud_preview; plugin_class->kill_window_effects = gnome_cinnamon_plugin_kill_window_effects; plugin_class->xevent_filter = gnome_cinnamon_plugin_xevent_filter; plugin_class->plugin_info = gnome_cinnamon_plugin_plugin_info; } static void gnome_cinnamon_plugin_init (CinnamonPlugin *cinnamon_plugin) { } static void gnome_cinnamon_plugin_start (MetaPlugin *plugin) { CinnamonPlugin *cinnamon_plugin = CINNAMON_PLUGIN (plugin); #if defined (__arm__) #else MetaScreen *screen; MetaDisplay *display; Display *xdisplay; #endif GError *error = NULL; int status; #if defined (__arm__) #else const char *glx_extensions; #endif GjsContext *gjs_context; #if defined (__arm__) cinnamon_plugin->have_swap_event = 0; #else screen = meta_plugin_get_screen (plugin); display = meta_screen_get_display (screen); xdisplay = meta_display_get_xdisplay (display); glXQueryExtension (xdisplay, &cinnamon_plugin->glx_error_base, &cinnamon_plugin->glx_event_base); glx_extensions = glXQueryExtensionsString (xdisplay, meta_screen_get_screen_number (screen)); cinnamon_plugin->have_swap_event = strstr (glx_extensions, "GLX_INTEL_swap_event") != NULL; #endif cinnamon_perf_log_define_event (cinnamon_perf_log_get_default (), "glx.swapComplete", "GL buffer swap complete event received (with timestamp of completion)", "x"); cinnamon_plugin->global = cinnamon_global_get (); _cinnamon_global_set_plugin (cinnamon_plugin->global, META_PLUGIN (cinnamon_plugin)); gjs_context = _cinnamon_global_get_gjs_context (cinnamon_plugin->global); if (!gjs_context_eval (gjs_context, "imports.ui.environment.init();" "imports.ui.main.start();", -1, "
", &status, &error)) { g_message ("Execution of main.js threw exception: %s", error->message); g_error_free (error); /* We just exit() here, since in a development environment you'll get the * error in your cinnamon output, and it's way better than a busted WM, * which typically manifests as a white screen. * * In production, we shouldn't crash =) But if we do, we should get * restarted by the session infrastructure, which is likely going * to be better than some undefined state. * * If there was a generic "hook into bug-buddy for non-C crashes" * infrastructure, here would be the place to put it. */ exit (1); } } static void gnome_cinnamon_plugin_dispose (GObject *object) { G_OBJECT_CLASS(gnome_cinnamon_plugin_parent_class)->dispose (object); } static void gnome_cinnamon_plugin_finalize (GObject *object) { G_OBJECT_CLASS(gnome_cinnamon_plugin_parent_class)->finalize (object); } static CinnamonWM * get_cinnamon_wm (void) { CinnamonWM *wm; g_object_get (cinnamon_global_get (), "window-manager", &wm, NULL); /* drop extra ref added by g_object_get */ g_object_unref (wm); return wm; } static void gnome_cinnamon_plugin_minimize (MetaPlugin *plugin, MetaWindowActor *actor) { _cinnamon_wm_minimize (get_cinnamon_wm (), actor); } static void gnome_cinnamon_plugin_maximize (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height) { _cinnamon_wm_maximize (get_cinnamon_wm (), actor, x, y, width, height); } static void gnome_cinnamon_plugin_tile (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height) { _cinnamon_wm_tile (get_cinnamon_wm (), actor, x, y, width, height); } static void gnome_cinnamon_plugin_unmaximize (MetaPlugin *plugin, MetaWindowActor *actor, gint x, gint y, gint width, gint height) { _cinnamon_wm_unmaximize (get_cinnamon_wm (), actor, x, y, width, height); } static void gnome_cinnamon_plugin_map (MetaPlugin *plugin, MetaWindowActor *actor) { _cinnamon_wm_map (get_cinnamon_wm (), actor); } static void gnome_cinnamon_plugin_destroy (MetaPlugin *plugin, MetaWindowActor *actor) { _cinnamon_wm_destroy (get_cinnamon_wm (), actor); } static void gnome_cinnamon_plugin_switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction) { _cinnamon_wm_switch_workspace (get_cinnamon_wm(), from, to, direction); } static void gnome_cinnamon_plugin_kill_window_effects (MetaPlugin *plugin, MetaWindowActor *actor) { _cinnamon_wm_kill_window_effects (get_cinnamon_wm(), actor); } static void gnome_cinnamon_plugin_show_tile_preview (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor, guint snap_queued) { _cinnamon_wm_show_tile_preview (get_cinnamon_wm (), window, tile_rect, tile_monitor, snap_queued); } static void gnome_cinnamon_plugin_hide_tile_preview (MetaPlugin *plugin) { _cinnamon_wm_hide_tile_preview (get_cinnamon_wm ()); } static void gnome_cinnamon_plugin_show_hud_preview (MetaPlugin *plugin, guint current_proximity_zone, MetaRectangle *work_area, guint snap_queued) { _cinnamon_wm_show_hud_preview (get_cinnamon_wm (), current_proximity_zone, work_area, snap_queued); } static void gnome_cinnamon_plugin_hide_hud_preview (MetaPlugin *plugin) { _cinnamon_wm_hide_hud_preview (get_cinnamon_wm ()); } static gboolean gnome_cinnamon_plugin_xevent_filter (MetaPlugin *plugin, XEvent *xev) { MetaScreen *screen = meta_plugin_get_screen (plugin); ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen)); CinnamonPlugin *cinnamon_plugin = CINNAMON_PLUGIN (plugin); #ifdef GLX_INTEL_swap_event if (cinnamon_plugin->have_swap_event && xev->type == (cinnamon_plugin->glx_event_base + GLX_BufferSwapComplete)) { GLXBufferSwapComplete *swap_complete_event; swap_complete_event = (GLXBufferSwapComplete *)xev; /* Buggy early versions of the INTEL_swap_event implementation in Mesa * can send this with a ust of 0. Simplify life for consumers * by ignoring such events */ if (swap_complete_event->ust != 0) cinnamon_perf_log_event_x (cinnamon_perf_log_get_default (), "glx.swapComplete", swap_complete_event->ust); } #endif if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify) && xev->xcrossing.window == clutter_x11_get_stage_window (stage)) { /* If the pointer enters a child of the stage window (eg, a * trayicon), we want to consider it to still be in the stage, * so don't let Clutter see the event. */ if (xev->xcrossing.detail == NotifyInferior) return TRUE; /* If the pointer is grabbed by a window it is not currently in, * filter that out as well. In particular, if a trayicon grabs * the pointer after a click on its label, we don't want to hide * the message tray. Filtering out this event will leave Clutter * out of sync, but that happens fairly often with grabs, and we * can work around it. (Eg, cinnamon_global_sync_pointer().) */ if (xev->xcrossing.mode == NotifyGrab && (xev->xcrossing.detail == NotifyNonlinear || xev->xcrossing.detail == NotifyNonlinearVirtual)) return TRUE; } /* * Pass the event to cinnamon-global */ if (_cinnamon_global_check_xdnd_event (cinnamon_plugin->global, xev)) return TRUE; return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE; } static const MetaPluginInfo *gnome_cinnamon_plugin_plugin_info (MetaPlugin *plugin) { static const MetaPluginInfo info = { .name = "Cinnamon", .version = "0.1", .author = "Various", .license = "GPLv2+", .description = "Provides Cinnamon core functionality" }; return &info; } Cinnamon-2.8.6/src/Makefile-tray.am0000664000175000017500000000242712625666430016034 0ustar fabiofabiotray_cflags = \ -I$(top_srcdir)/src \ -DG_DISABLE_DEPRECATED \ -DG_LOG_DOMAIN=\"notification_area\" \ $(TRAY_CFLAGS) \ $(NULL) tray_built_sources = \ na-marshal.h \ na-marshal.c BUILT_SOURCES += $(tray_built_sources) TRAY_STAMP_FILES = stamp-na-marshal.h # please, keep this sorted alphabetically tray_source = \ tray/na-tray-child.c \ tray/na-tray-child.h \ tray/na-tray-manager.c \ tray/na-tray-manager.h \ $(NULL) na-marshal.h: stamp-na-marshal.h @true stamp-na-marshal.h: Makefile tray/na-marshal.list $(AM_V_GEN) $(GLIB_GENMARSHAL) \ --prefix=_na_marshal \ --header \ $(srcdir)/tray/na-marshal.list > xgen-tmh && \ (cmp -s xgen-tmh na-marshal.h || cp -f xgen-tmh na-marshal.h) && \ rm -f xgen-tmh && \ echo timestamp > $(@F) na-marshal.c: Makefile tray/na-marshal.list $(AM_V_GEN) (echo "#include \"na-marshal.h\"" ; \ $(GLIB_GENMARSHAL) \ --prefix=_na_marshal \ --body \ $(srcdir)/tray/na-marshal.list ) > xgen-tmc && \ cp -f xgen-tmc na-marshal.c && \ rm -f xgen-tmc noinst_LTLIBRARIES += libtray.la libtray_la_LIBADD = $(TRAY_LIBS) libtray_la_SOURCES = \ $(tray_source) \ $(tray_built_sources) \ $(NULL) libtray_la_CPPFLAGS = $(tray_cflags) CLEANFILES += $(TRAY_STAMP_FILES) $(BUILT_SOURCES) EXTRA_DIST += \ tray/na-marshal.list Cinnamon-2.8.6/src/cinnamon-generic-container.c0000664000175000017500000003065012625666430020362 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:cinnamon-generic-container * @short_description: A container class with signals for allocation * * #CinnamonGenericContainer is mainly a workaround for the current * lack of GObject subclassing + vfunc overrides in gjs. We * implement the container interface, but proxy the virtual functions * into signals, which gjs can catch. * * #CinnamonGenericContainer is an #StWidget, and automatically takes its * borders and padding into account during size request and allocation. */ #include "config.h" #include "cinnamon-generic-container.h" #include #include #include static void cinnamon_generic_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE(CinnamonGenericContainer, cinnamon_generic_container, ST_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, cinnamon_generic_container_iface_init)); struct _CinnamonGenericContainerPrivate { GHashTable *skip_paint; }; /* Signals */ enum { GET_PREFERRED_WIDTH, GET_PREFERRED_HEIGHT, ALLOCATE, LAST_SIGNAL }; static guint cinnamon_generic_container_signals [LAST_SIGNAL] = { 0 }; static gpointer cinnamon_generic_container_allocation_ref (CinnamonGenericContainerAllocation *alloc) { alloc->_refcount++; return alloc; } static void cinnamon_generic_container_allocation_unref (CinnamonGenericContainerAllocation *alloc) { if (--alloc->_refcount == 0) g_slice_free (CinnamonGenericContainerAllocation, alloc); } static void cinnamon_generic_container_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StThemeNode *theme_node; ClutterActorBox content_box; CLUTTER_ACTOR_CLASS (cinnamon_generic_container_parent_class)->allocate (self, box, flags); theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_get_content_box (theme_node, box, &content_box); g_signal_emit (G_OBJECT (self), cinnamon_generic_container_signals[ALLOCATE], 0, &content_box, flags); } static void cinnamon_generic_container_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { CinnamonGenericContainerAllocation *alloc = g_slice_new0 (CinnamonGenericContainerAllocation); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_height (theme_node, &for_height); alloc->_refcount = 1; g_signal_emit (G_OBJECT (actor), cinnamon_generic_container_signals[GET_PREFERRED_WIDTH], 0, for_height, alloc); if (min_width_p) *min_width_p = alloc->min_size; if (natural_width_p) *natural_width_p = alloc->natural_size; cinnamon_generic_container_allocation_unref (alloc); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void cinnamon_generic_container_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { CinnamonGenericContainerAllocation *alloc = g_slice_new0 (CinnamonGenericContainerAllocation); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_width (theme_node, &for_width); alloc->_refcount = 1; g_signal_emit (G_OBJECT (actor), cinnamon_generic_container_signals[GET_PREFERRED_HEIGHT], 0, for_width, alloc); if (min_height_p) *min_height_p = alloc->min_size; if (natural_height_p) *natural_height_p = alloc->natural_size; cinnamon_generic_container_allocation_unref (alloc); st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void cinnamon_generic_container_paint (ClutterActor *actor) { CinnamonGenericContainer *self = (CinnamonGenericContainer*) actor; GList *iter, *children; CLUTTER_ACTOR_CLASS (cinnamon_generic_container_parent_class)->paint (actor); children = st_container_get_children_list (ST_CONTAINER (actor)); for (iter = children; iter; iter = iter->next) { ClutterActor *child = iter->data; if (g_hash_table_lookup (self->priv->skip_paint, child)) continue; clutter_actor_paint (child); } } static void cinnamon_generic_container_pick (ClutterActor *actor, const ClutterColor *color) { CinnamonGenericContainer *self = (CinnamonGenericContainer*) actor; GList *iter, *children; CLUTTER_ACTOR_CLASS (cinnamon_generic_container_parent_class)->pick (actor, color); children = st_container_get_children_list (ST_CONTAINER (actor)); for (iter = children; iter; iter = iter->next) { ClutterActor *child = iter->data; if (g_hash_table_lookup (self->priv->skip_paint, child)) continue; clutter_actor_paint (child); } } static GList * cinnamon_generic_container_get_focus_chain (StContainer *container) { CinnamonGenericContainer *self = CINNAMON_GENERIC_CONTAINER (container); GList *children, *focus_chain; focus_chain = NULL; for (children = st_container_get_children_list (container); children; children = children->next) { ClutterActor *child = children->data; if (CLUTTER_ACTOR_IS_VISIBLE (child) && !cinnamon_generic_container_get_skip_paint (self, child)) focus_chain = g_list_prepend (focus_chain, child); } return g_list_reverse (focus_chain); } /** * cinnamon_generic_container_get_n_skip_paint: * @self: A #CinnamonGenericContainer * * Returns: Number of children which will not be painted. */ guint cinnamon_generic_container_get_n_skip_paint (CinnamonGenericContainer *self) { return g_hash_table_size (self->priv->skip_paint); } /** * cinnamon_generic_container_get_skip_paint: * @self: A #CinnamonGenericContainer * @child: Child #ClutterActor * * Gets whether or not @actor is skipped when painting. * * Return value: %TRUE or %FALSE */ gboolean cinnamon_generic_container_get_skip_paint (CinnamonGenericContainer *self, ClutterActor *child) { return g_hash_table_lookup (self->priv->skip_paint, child) != NULL; } /** * cinnamon_generic_container_set_skip_paint: * @self: A #CinnamonGenericContainer * @child: Child #ClutterActor * @skip: %TRUE if we should skip painting * * Set whether or not we should skip painting @actor. Workaround for * lack of gjs ability to override _paint vfunc. */ void cinnamon_generic_container_set_skip_paint (CinnamonGenericContainer *self, ClutterActor *child, gboolean skip) { gboolean currently_skipping; currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL; if (!!skip == currently_skipping) return; if (!skip) g_hash_table_remove (self->priv->skip_paint, child); else g_hash_table_insert (self->priv->skip_paint, child, child); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } static void cinnamon_generic_container_finalize (GObject *object) { CinnamonGenericContainer *self = (CinnamonGenericContainer*) object; g_hash_table_destroy (self->priv->skip_paint); G_OBJECT_CLASS (cinnamon_generic_container_parent_class)->finalize (object); } static void cinnamon_generic_container_class_init (CinnamonGenericContainerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StContainerClass *container_class = ST_CONTAINER_CLASS (klass); gobject_class->finalize = cinnamon_generic_container_finalize; actor_class->get_preferred_width = cinnamon_generic_container_get_preferred_width; actor_class->get_preferred_height = cinnamon_generic_container_get_preferred_height; actor_class->allocate = cinnamon_generic_container_allocate; actor_class->paint = cinnamon_generic_container_paint; actor_class->pick = cinnamon_generic_container_pick; container_class->get_focus_chain = cinnamon_generic_container_get_focus_chain; /** * CinnamonGenericContainer::get-preferred-width: * @self: the #CinnamonGenericContainer * @for_height: as in clutter_actor_get_preferred_width() * @alloc: a #CinnamonGenericContainerAllocation to be filled in * * Emitted when clutter_actor_get_preferred_width() is called * on @self. You should fill in the fields of @alloc with the * your minimum and natural widths. #CinnamonGenericContainer * will deal with taking its borders and padding into account * for you. * * @alloc's fields are initialized to 0, so unless you have a fixed * width specified (via #ClutterActor:width or CSS), you must * connect to this signal and fill in the values. */ cinnamon_generic_container_signals[GET_PREFERRED_WIDTH] = g_signal_new ("get-preferred-width", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, gi_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_FLOAT, CINNAMON_TYPE_GENERIC_CONTAINER_ALLOCATION); /** * CinnamonGenericContainer::get-preferred-height: * @self: the #CinnamonGenericContainer * @for_width: as in clutter_actor_get_preferred_height() * @alloc: a #CinnamonGenericContainerAllocation to be filled in * * Emitted when clutter_actor_get_preferred_height() is called * on @self. You should fill in the fields of @alloc with the * your minimum and natural heights. #CinnamonGenericContainer * will deal with taking its borders and padding into account * for you. * * @alloc's fields are initialized to 0, so unless you have a fixed * height specified (via #ClutterActor:height or CSS), you must * connect to this signal and fill in the values. */ cinnamon_generic_container_signals[GET_PREFERRED_HEIGHT] = g_signal_new ("get-preferred-height", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, gi_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_FLOAT, CINNAMON_TYPE_GENERIC_CONTAINER_ALLOCATION); /** * CinnamonGenericContainer::allocate: * @self: the #CinnamonGenericContainer * @box: @self's content box * @flags: the allocation flags. * * Emitted when @self is allocated, after chaining up to the parent * allocate method. * * Note that @box is @self's content box (qv * st_theme_node_get_content_box()), NOT its allocation. */ cinnamon_generic_container_signals[ALLOCATE] = g_signal_new ("allocate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, gi_cclosure_marshal_generic, G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS); g_type_class_add_private (gobject_class, sizeof (CinnamonGenericContainerPrivate)); } static void cinnamon_generic_container_actor_removed (ClutterContainer *container, ClutterActor *actor) { CinnamonGenericContainerPrivate *priv = CINNAMON_GENERIC_CONTAINER (container)->priv; g_hash_table_remove (priv->skip_paint, actor); } static void cinnamon_generic_container_iface_init (ClutterContainerIface *iface) { iface->actor_removed = cinnamon_generic_container_actor_removed; } static void cinnamon_generic_container_init (CinnamonGenericContainer *area) { area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, CINNAMON_TYPE_GENERIC_CONTAINER, CinnamonGenericContainerPrivate); area->priv->skip_paint = g_hash_table_new (NULL, NULL); } GType cinnamon_generic_container_allocation_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("CinnamonGenericContainerAllocation", (GBoxedCopyFunc)cinnamon_generic_container_allocation_ref, (GBoxedFreeFunc)cinnamon_generic_container_allocation_unref); } return gtype; } Cinnamon-2.8.6/src/cinnamon-mount-operation.c0000664000175000017500000001273212625666430020127 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Author: Cosimo Cecchi * */ #include "cinnamon-mount-operation.h" /* This is a dummy class; we would like to be able to subclass the * object from JS but we can't yet; the default GMountOperation impl * automatically calls g_mount_operation_reply(UNHANDLED) after an idle, * in interactive methods. We want to handle the reply outselves * instead, so we just override the default methods with empty ones, * except for ask-password, as we don't want to handle that. * * Also, we need to workaround the fact that gjs doesn't support type * annotations for signals yet (so we can't effectively forward e.g. * the GPid array to JS). * See https://bugzilla.gnome.org/show_bug.cgi?id=645978 */ G_DEFINE_TYPE (CinnamonMountOperation, cinnamon_mount_operation, G_TYPE_MOUNT_OPERATION); enum { SHOW_PROCESSES_2, NUM_SIGNALS }; static guint signals[NUM_SIGNALS] = { 0, }; struct _CinnamonMountOperationPrivate { GArray *pids; gchar **choices; gchar *message; }; static void cinnamon_mount_operation_init (CinnamonMountOperation *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CINNAMON_TYPE_MOUNT_OPERATION, CinnamonMountOperationPrivate); } static void cinnamon_mount_operation_ask_password (GMountOperation *op, const char *message, const char *default_user, const char *default_domain, GAskPasswordFlags flags) { /* do nothing */ } static void cinnamon_mount_operation_ask_question (GMountOperation *op, const char *message, const char *choices[]) { /* do nothing */ } static void cinnamon_mount_operation_show_processes (GMountOperation *operation, const gchar *message, GArray *processes, const gchar *choices[]) { CinnamonMountOperation *self = CINNAMON_MOUNT_OPERATION (operation); if (self->priv->pids != NULL) { g_array_unref (self->priv->pids); self->priv->pids = NULL; } g_free (self->priv->message); g_strfreev (self->priv->choices); /* save the parameters */ self->priv->pids = g_array_ref (processes); self->priv->choices = g_strdupv ((gchar **) choices); self->priv->message = g_strdup (message); g_signal_emit (self, signals[SHOW_PROCESSES_2], 0); } static void cinnamon_mount_operation_finalize (GObject *obj) { CinnamonMountOperation *self = CINNAMON_MOUNT_OPERATION (obj); g_strfreev (self->priv->choices); g_free (self->priv->message); if (self->priv->pids != NULL) { g_array_unref (self->priv->pids); self->priv->pids = NULL; } G_OBJECT_CLASS (cinnamon_mount_operation_parent_class)->finalize (obj); } static void cinnamon_mount_operation_class_init (CinnamonMountOperationClass *klass) { GMountOperationClass *mclass; GObjectClass *oclass; mclass = G_MOUNT_OPERATION_CLASS (klass); mclass->show_processes = cinnamon_mount_operation_show_processes; mclass->ask_question = cinnamon_mount_operation_ask_question; mclass->ask_password = cinnamon_mount_operation_ask_password; oclass = G_OBJECT_CLASS (klass); oclass->finalize = cinnamon_mount_operation_finalize; signals[SHOW_PROCESSES_2] = g_signal_new ("show-processes-2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_type_class_add_private (klass, sizeof (CinnamonMountOperationPrivate)); } GMountOperation * cinnamon_mount_operation_new (void) { return g_object_new (CINNAMON_TYPE_MOUNT_OPERATION, NULL); } /** * cinnamon_mount_operation_get_show_processes_pids: * @self: a #CinnamonMountOperation * * Returns: (transfer full) (element-type GPid): a #GArray */ GArray * cinnamon_mount_operation_get_show_processes_pids (CinnamonMountOperation *self) { return g_array_ref (self->priv->pids); } /** * cinnamon_mount_operation_get_show_processes_choices: * @self: a #CinnamonMountOperation * * Returns: (transfer full): */ gchar ** cinnamon_mount_operation_get_show_processes_choices (CinnamonMountOperation *self) { return g_strdupv (self->priv->choices); } /** * cinnamon_mount_operation_get_show_processes_message: * @self: a #CinnamonMountOperation * * Returns: (transfer full): */ gchar * cinnamon_mount_operation_get_show_processes_message (CinnamonMountOperation *self) { return g_strdup (self->priv->message); } Cinnamon-2.8.6/src/cinnamon-tray-manager.c0000664000175000017500000003133312625666430017354 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include #include #include #include "cinnamon-tray-manager.h" #include "na-tray-manager.h" #include "cinnamon-tray-icon.h" #include "cinnamon-embedded-window.h" #include "cinnamon-global.h" struct _CinnamonTrayManagerPrivate { NaTrayManager *na_manager; ClutterStage *stage; ClutterColor bg_color; GHashTable *icons; }; typedef struct { CinnamonTrayManager *manager; GtkWidget *socket; GtkWidget *window; ClutterActor *actor; } CinnamonTrayManagerChild; enum { PROP_0, PROP_BG_COLOR }; /* Signals */ enum { TRAY_ICON_ADDED, TRAY_ICON_REMOVED, LAST_SIGNAL }; G_DEFINE_TYPE (CinnamonTrayManager, cinnamon_tray_manager, G_TYPE_OBJECT); static guint cinnamon_tray_manager_signals [LAST_SIGNAL] = { 0 }; static const ClutterColor default_color = { 0x00, 0x00, 0x00, 0xff }; static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager); static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager); static void free_tray_icon (gpointer data) { CinnamonTrayManagerChild *child = data; gtk_widget_destroy (child->window); if (child->actor) { g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, child); g_object_unref (child->actor); } g_slice_free (CinnamonTrayManagerChild, child); } static void cinnamon_tray_manager_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonTrayManager *manager = CINNAMON_TRAY_MANAGER (object); switch (prop_id) { case PROP_BG_COLOR: { ClutterColor *color = g_value_get_boxed (value); if (color) manager->priv->bg_color = *color; else manager->priv->bg_color = default_color; } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_tray_manager_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonTrayManager *manager = CINNAMON_TRAY_MANAGER (object); switch (prop_id) { case PROP_BG_COLOR: g_value_set_boxed (value, &manager->priv->bg_color); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_tray_manager_init (CinnamonTrayManager *manager) { manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, CINNAMON_TYPE_TRAY_MANAGER, CinnamonTrayManagerPrivate); manager->priv->na_manager = na_tray_manager_new (); manager->priv->icons = g_hash_table_new_full (NULL, NULL, NULL, free_tray_icon); manager->priv->bg_color = default_color; g_signal_connect (manager->priv->na_manager, "tray-icon-added", G_CALLBACK (na_tray_icon_added), manager); g_signal_connect (manager->priv->na_manager, "tray-icon-removed", G_CALLBACK (na_tray_icon_removed), manager); } static void cinnamon_tray_manager_finalize (GObject *object) { CinnamonTrayManager *manager = CINNAMON_TRAY_MANAGER (object); g_object_unref (manager->priv->na_manager); g_object_unref (manager->priv->stage); g_hash_table_destroy (manager->priv->icons); G_OBJECT_CLASS (cinnamon_tray_manager_parent_class)->finalize (object); } static void cinnamon_tray_manager_class_init (CinnamonTrayManagerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (CinnamonTrayManagerPrivate)); gobject_class->finalize = cinnamon_tray_manager_finalize; gobject_class->set_property = cinnamon_tray_manager_set_property; gobject_class->get_property = cinnamon_tray_manager_get_property; cinnamon_tray_manager_signals[TRAY_ICON_ADDED] = g_signal_new ("tray-icon-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CinnamonTrayManagerClass, tray_icon_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); cinnamon_tray_manager_signals[TRAY_ICON_REMOVED] = g_signal_new ("tray-icon-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CinnamonTrayManagerClass, tray_icon_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /* Lifting the CONSTRUCT_ONLY here isn't hard; you just need to * iterate through the icons, reset the background pixmap, and * call na_tray_child_force_redraw() */ g_object_class_install_property (gobject_class, PROP_BG_COLOR, g_param_spec_boxed ("bg-color", "BG Color", "Background color (only if we don't have transparency)", CLUTTER_TYPE_COLOR, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } CinnamonTrayManager * cinnamon_tray_manager_new (void) { return g_object_new (CINNAMON_TYPE_TRAY_MANAGER, NULL); } static void cinnamon_tray_manager_style_changed (StWidget *theme_widget, gpointer user_data) { CinnamonTrayManager *manager = user_data; StThemeNode *theme_node; StIconColors *icon_colors; GdkColor foreground, warning, error, success; theme_node = st_widget_get_theme_node (theme_widget); icon_colors = st_theme_node_get_icon_colors (theme_node); foreground.red = icon_colors->foreground.red * 0x101; foreground.green = icon_colors->foreground.green * 0x101; foreground.blue = icon_colors->foreground.blue * 0x101; warning.red = icon_colors->warning.red * 0x101; warning.green = icon_colors->warning.green * 0x101; warning.blue = icon_colors->warning.blue * 0x101; error.red = icon_colors->error.red * 0x101; error.green = icon_colors->error.green * 0x101; error.blue = icon_colors->error.blue * 0x101; success.red = icon_colors->success.red * 0x101; success.green = icon_colors->success.green * 0x101; success.blue = icon_colors->success.blue * 0x101; na_tray_manager_set_colors (manager->priv->na_manager, &foreground, &warning, &error, &success); } void cinnamon_tray_manager_manage_stage (CinnamonTrayManager *manager, ClutterStage *stage, StWidget *theme_widget) { Window stage_xwindow; GdkWindow *stage_window; GdkDisplay *display; GdkScreen *screen; g_return_if_fail (manager->priv->stage == NULL); manager->priv->stage = g_object_ref (stage); stage_xwindow = clutter_x11_get_stage_window (stage); /* This is a pretty ugly way to get the GdkScreen for the stage; it * will normally go through the foreign_new() case with a * round-trip to the X server, it might be nicer to pass the screen * in in some way. (The Clutter/Muffin combo is currently incapable * of multi-screen operation, so alternatively we could just assume * that clutter_x11_get_default_screen() gives us the right * screen.) We assume, in any case, that we are using the default * GDK display. */ display = gdk_display_get_default(); stage_window = gdk_x11_window_lookup_for_display (display, stage_xwindow); if (stage_window) g_object_ref (stage_window); else stage_window = gdk_x11_window_foreign_new_for_display (display, stage_xwindow); screen = gdk_window_get_screen (stage_window); g_object_unref (stage_window); gint scale = 1; g_object_get (cinnamon_global_get (), "ui_scale", &scale, NULL); na_tray_manager_set_scale (manager->priv->na_manager, scale); na_tray_manager_manage_screen (manager->priv->na_manager, screen); g_signal_connect (theme_widget, "style-changed", G_CALLBACK (cinnamon_tray_manager_style_changed), manager); cinnamon_tray_manager_style_changed (theme_widget, manager); } static void cinnamon_tray_manager_child_on_realize (GtkWidget *widget, CinnamonTrayManagerChild *child) { /* If the tray child is using an RGBA colormap (and so we have real * transparency), we don't need to worry about the background. If * not, we obey the bg-color property by creating a cairo pattern of * that color and setting it as our background. Then "parent-relative" * background on the socket and the plug within that will cause * the icons contents to appear on top of our background color. */ if (!na_tray_child_has_alpha (NA_TRAY_CHILD (child->socket))) { ClutterColor color = child->manager->priv->bg_color; cairo_pattern_t *bg_pattern; bg_pattern = cairo_pattern_create_rgb (color.red / 255., color.green / 255., color.blue / 255.); gdk_window_set_background_pattern (gtk_widget_get_window (widget), bg_pattern); cairo_pattern_destroy (bg_pattern); } } static void on_plug_added (GtkSocket *socket, CinnamonTrayManager *manager) { CinnamonTrayManagerChild *child; g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager); child = g_hash_table_lookup (manager->priv->icons, socket); child->actor = cinnamon_tray_icon_new (CINNAMON_EMBEDDED_WINDOW (child->window)); g_object_ref_sink (child->actor); g_signal_emit (manager, cinnamon_tray_manager_signals[TRAY_ICON_ADDED], 0, child->actor); } static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, gpointer user_data) { CinnamonTrayManager *manager = user_data; GtkWidget *win; CinnamonTrayManagerChild *child; /* We don't need the NaTrayIcon to be composited on the window we * put it in: the window is the same size as the tray icon * and transparent. We can just use the default X handling of * subwindows as mode of SOURCE (replace the parent with the * child) and then composite the parent onto the stage. */ na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE); win = cinnamon_embedded_window_new (manager->priv->stage); gtk_container_add (GTK_CONTAINER (win), socket); /* The visual of the socket matches that of its contents; make * the window we put it in match that as well */ gtk_widget_set_visual (win, gtk_widget_get_visual (socket)); child = g_slice_new0 (CinnamonTrayManagerChild); child->manager = manager; child->window = win; child->socket = socket; g_signal_connect (win, "realize", G_CALLBACK (cinnamon_tray_manager_child_on_realize), child); gtk_widget_show_all (win); g_hash_table_insert (manager->priv->icons, socket, child); g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager); } static void cinnamon_tray_manager_child_redisplay (gpointer socket_pointer, gpointer child_pointer, gpointer user_data) { CinnamonTrayManagerChild *child = child_pointer; g_return_if_fail(child != NULL); if (child->actor && CLUTTER_IS_ACTOR(child->actor)) { clutter_actor_destroy(child->actor); } on_plug_added(socket_pointer, child->manager); } void cinnamon_tray_manager_redisplay (CinnamonTrayManager *manager) { g_hash_table_foreach(manager->priv->icons, cinnamon_tray_manager_child_redisplay, manager); } static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *socket, gpointer user_data) { CinnamonTrayManager *manager = user_data; CinnamonTrayManagerChild *child; child = g_hash_table_lookup (manager->priv->icons, socket); g_return_if_fail (child != NULL); if (child->actor != NULL) { /* Only emit signal if a corresponding tray-icon-added signal was emitted, that is, if embedding did not fail and we got a plug-added */ g_signal_emit (manager, cinnamon_tray_manager_signals[TRAY_ICON_REMOVED], 0, child->actor); } g_hash_table_remove (manager->priv->icons, socket); } Cinnamon-2.8.6/src/cinnamon-embedded-window.c0000664000175000017500000002322712625666430020026 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include "cinnamon-embedded-window-private.h" /* This type is a subclass of GtkWindow that ties the window to a * CinnamonGtkEmbed; the window is reparented into the stage * window for the actor and the resizing logic is bound to the clutter * logic. * * The typical usage we might expect is * * - CinnamonEmbeddedWindow is created and filled with content * - CinnamonEmbeddedWindow is shown with gtk_widget_show_all() * - CinnamonGtkEmbed is created for the CinnamonEmbeddedWindow * - actor is added to a stage * * Ideally, the way it would work is that the GtkWindow is mapped * if and only if both: * * - GTK_WIDGET_VISIBLE (window) [widget has been shown] * - Actor is mapped [actor and all parents visible, actor in stage] * * Implementing this perfectly is not currently possible, due to problems * in Clutter, see: * * http://bugzilla.openedhand.com/show_bug.cgi?id=1138 * * So until that is fixed we use the "realized" state of the ClutterActor * as a stand-in for the ideal mapped state, this will work as long * as the ClutterActor and all its parents are in fact visible. */ G_DEFINE_TYPE (CinnamonEmbeddedWindow, cinnamon_embedded_window, GTK_TYPE_WINDOW); enum { PROP_0, PROP_STAGE }; struct _CinnamonEmbeddedWindowPrivate { CinnamonGtkEmbed *actor; GdkRectangle position; Window stage_xwindow; }; /* * The normal gtk_window_show() starts all of the complicated asynchronous * window resizing code running; we don't want or need any of that. * Bypassing the normal code does mean that the extra geometry management * available on GtkWindow: gridding, maximum sizes, etc, is ignored; we * don't really want that anyways - we just want a way of embedding a * GtkWidget into a Clutter stage. */ static void cinnamon_embedded_window_show (GtkWidget *widget) { CinnamonEmbeddedWindow *window = CINNAMON_EMBEDDED_WINDOW (widget); GtkWidgetClass *widget_class; /* Skip GtkWindow, but run the default GtkWidget handling which * marks the widget visible */ widget_class = g_type_class_peek (GTK_TYPE_WIDGET); widget_class->show (widget); if (window->priv->actor) { /* Size is 0x0 if the GtkWindow is not shown */ clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); if (CLUTTER_ACTOR_IS_REALIZED (window->priv->actor)) gtk_widget_map (widget); } } static void cinnamon_embedded_window_hide (GtkWidget *widget) { CinnamonEmbeddedWindow *window = CINNAMON_EMBEDDED_WINDOW (widget); if (window->priv->actor) clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); GTK_WIDGET_CLASS (cinnamon_embedded_window_parent_class)->hide (widget); } static void cinnamon_embedded_window_realize (GtkWidget *widget) { CinnamonEmbeddedWindow *window = CINNAMON_EMBEDDED_WINDOW (widget); GTK_WIDGET_CLASS (cinnamon_embedded_window_parent_class)->realize (widget); /* Using XReparentWindow() is simpler than using gdk_window_reparent(), * since it avoids maybe having to create a new foreign GDK window for * the stage. However, GDK will be left thinking that the parent of * window->window is the root window - it's not immediately clear * to me whether that is more or less likely to cause problems than * modifying the GDK hierarchy. */ XReparentWindow (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)), gdk_x11_window_get_xid (gtk_widget_get_window (widget)), window->priv->stage_xwindow, window->priv->position.x, window->priv->position.y); } static gboolean cinnamon_embedded_window_configure_event (GtkWidget *widget, GdkEventConfigure *event) { /* Normally a configure event coming back from X triggers the * resizing logic inside GtkWindow; we just ignore them * since we are handling the resizing logic separately. */ return FALSE; } static void cinnamon_embedded_window_check_resize (GtkContainer *container) { CinnamonEmbeddedWindow *window = CINNAMON_EMBEDDED_WINDOW (container); /* Check resize is called when a resize is queued on something * inside the GtkWindow; we need to make sure that in response * to this gtk_widget_size_request() and then * gtk_widget_size_allocate() are called; we defer to the Clutter * logic and assume it will do the right thing. */ if (window->priv->actor) clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); } static void cinnamon_embedded_window_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonEmbeddedWindow *window = CINNAMON_EMBEDDED_WINDOW (object); switch (prop_id) { case PROP_STAGE: window->priv->stage_xwindow = clutter_x11_get_stage_window (g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * cinnamon_embedded_window_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties) { GObject *object; GObjectClass *parent_class; parent_class = G_OBJECT_CLASS (cinnamon_embedded_window_parent_class); object = parent_class->constructor (gtype, n_properties, properties); /* Setting the resize mode to immediate means that calling queue_resize() * on a widget within the window will immmediately call check_resize() * to be called, instead of having it queued to an idle. From our perspective, * this is ideal since we just are going to queue a resize to Clutter's * idle resize anyways. */ g_object_set (object, "resize-mode", GTK_RESIZE_IMMEDIATE, "type", GTK_WINDOW_POPUP, NULL); return object; } static void cinnamon_embedded_window_class_init (CinnamonEmbeddedWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); g_type_class_add_private (klass, sizeof (CinnamonEmbeddedWindowPrivate)); object_class->set_property = cinnamon_embedded_window_set_property; object_class->constructor = cinnamon_embedded_window_constructor; widget_class->show = cinnamon_embedded_window_show; widget_class->hide = cinnamon_embedded_window_hide; widget_class->realize = cinnamon_embedded_window_realize; widget_class->configure_event = cinnamon_embedded_window_configure_event; container_class->check_resize = cinnamon_embedded_window_check_resize; g_object_class_install_property (object_class, PROP_STAGE, g_param_spec_object ("stage", "Stage", "ClutterStage to embed on", CLUTTER_TYPE_STAGE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static void cinnamon_embedded_window_init (CinnamonEmbeddedWindow *window) { window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, CINNAMON_TYPE_EMBEDDED_WINDOW, CinnamonEmbeddedWindowPrivate); } /* * Private routines called by CinnamonGtkEmbed */ void _cinnamon_embedded_window_set_actor (CinnamonEmbeddedWindow *window, CinnamonGtkEmbed *actor) { g_return_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window)); window->priv->actor = actor; if (actor && CLUTTER_ACTOR_IS_REALIZED (actor) && gtk_widget_get_visible (GTK_WIDGET (window))) gtk_widget_map (GTK_WIDGET (window)); } void _cinnamon_embedded_window_allocate (CinnamonEmbeddedWindow *window, int x, int y, int width, int height) { GtkAllocation allocation; g_return_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window)); if (window->priv->position.x == x && window->priv->position.y == y && window->priv->position.width == width && window->priv->position.height == height) return; window->priv->position.x = x; window->priv->position.y = y; window->priv->position.width = width; window->priv->position.height = height; if (gtk_widget_get_realized (GTK_WIDGET (window))) gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)), x, y, width, height); allocation.x = 0; allocation.y = 0; allocation.width = width; allocation.height = height; gtk_widget_size_allocate (GTK_WIDGET (window), &allocation); } void _cinnamon_embedded_window_realize (CinnamonEmbeddedWindow *window) { g_return_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window)); if (gtk_widget_get_visible (GTK_WIDGET (window))) gtk_widget_map (GTK_WIDGET (window)); } void _cinnamon_embedded_window_unrealize (CinnamonEmbeddedWindow *window) { g_return_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window)); gtk_widget_unmap (GTK_WIDGET (window)); } /* * Public API */ GtkWidget * cinnamon_embedded_window_new (ClutterStage *stage) { return g_object_new (CINNAMON_TYPE_EMBEDDED_WINDOW, "stage", stage, NULL); } Cinnamon-2.8.6/src/cinnamon-app.c0000664000175000017500000012027612625666430015552 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include #include "cinnamon-app-private.h" #include "cinnamon-enum-types.h" #include "cinnamon-global.h" #include "cinnamon-util.h" #include "cinnamon-app-system-private.h" #include "cinnamon-window-tracker-private.h" #include "st.h" typedef enum { MATCH_NONE, MATCH_SUBSTRING, /* Not prefix, substring */ MATCH_PREFIX, /* Strict prefix */ } CinnamonAppSearchMatch; /* This is mainly a memory usage optimization - the user is going to * be running far fewer of the applications at one time than they have * installed. But it also just helps keep the code more logically * separated. */ typedef struct { guint refcount; /* Last time the user interacted with any of this application's windows */ guint32 last_user_time; /* Signal connection to dirty window sort list on workspace changes */ guint workspace_switch_id; GSList *windows; /* Whether or not we need to resort the windows; this is done on demand */ gboolean window_sort_stale : 1; } CinnamonAppRunningState; /** * SECTION:cinnamon-app * @short_description: Object representing an application * * This object wraps a #GMenuTreeEntry, providing methods and signals * primarily useful for running applications. */ struct _CinnamonApp { GObject parent; int started_on_workspace; CinnamonAppState state; GMenuTreeEntry *entry; /* If NULL, this app is backed by one or more * MetaWindow. For purposes of app title * etc., we use the first window added, * because it's most likely to be what we * want (e.g. it will be of TYPE_NORMAL from * the way cinnamon-window-tracker.c works). */ CinnamonAppRunningState *running_state; char *window_id_string; char *casefolded_name; char *name_collation_key; char *casefolded_description; char *casefolded_exec; }; G_DEFINE_TYPE (CinnamonApp, cinnamon_app, G_TYPE_OBJECT); enum { PROP_0, PROP_STATE }; enum { WINDOWS_CHANGED, LAST_SIGNAL }; static guint cinnamon_app_signals[LAST_SIGNAL] = { 0 }; static void create_running_state (CinnamonApp *app); static void unref_running_state (CinnamonAppRunningState *state); static void cinnamon_app_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonApp *app = CINNAMON_APP (gobject); switch (prop_id) { case PROP_STATE: g_value_set_enum (value, app->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } const char * cinnamon_app_get_id (CinnamonApp *app) { if (app->entry) return gmenu_tree_entry_get_desktop_file_id (app->entry); return app->window_id_string; } static MetaWindow * window_backed_app_get_window (CinnamonApp *app) { g_assert (app->entry == NULL); g_assert (app->running_state); g_assert (app->running_state->windows); return app->running_state->windows->data; } static ClutterActor * window_backed_app_get_icon (CinnamonApp *app, int size) { MetaWindow *window; ClutterActor *actor; gint scale; CinnamonGlobal *global; StThemeContext *context; global = cinnamon_global_get (); context = st_theme_context_get_for_stage (cinnamon_global_get_stage (global)); g_object_get (context, "scale-factor", &scale, NULL); size *= scale; /* During a state transition from running to not-running for * window-backend apps, it's possible we get a request for the icon. * Avoid asserting here and just return an empty image. */ if (app->running_state == NULL) { actor = clutter_texture_new (); g_object_set (actor, "opacity", 0, "width", (float) size, "height", (float) size, NULL); return actor; } window = window_backed_app_get_window (app); actor = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (), G_OBJECT (window), "icon"); g_object_set (actor, "width", (float) size, "height", (float) size, NULL); return actor; } /** * cinnamon_app_create_icon_texture: * * Look up the icon for this application, and create a #ClutterTexture * for it at the given size. * * Return value: (transfer none): A floating #ClutterActor */ ClutterActor * cinnamon_app_create_icon_texture (CinnamonApp *app, int size) { GIcon *icon; ClutterActor *ret; ret = NULL; if (app->entry == NULL) return window_backed_app_get_icon (app, size); icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); if (icon != NULL) ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size); if (ret == NULL) { icon = g_themed_icon_new ("application-x-executable"); ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size); g_object_unref (icon); } return ret; } typedef struct { CinnamonApp *app; int size; int scale; } CreateFadedIconData; static CoglHandle cinnamon_app_create_faded_icon_cpu (StTextureCache *cache, const char *key, void *datap, GError **error) { CreateFadedIconData *data = datap; CinnamonApp *app; GdkPixbuf *pixbuf; int size; int scale; CoglHandle texture; gint width, height, rowstride; guint8 n_channels; gboolean have_alpha; gint fade_start; gint fade_range; guint i, j; guint pixbuf_byte_size; guint8 *orig_pixels; guint8 *pixels; GIcon *icon; GtkIconInfo *info; app = data->app; size = data->size; scale = data->scale; info = NULL; icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); if (icon != NULL) { info = gtk_icon_theme_lookup_by_gicon_for_scale (gtk_icon_theme_get_default (), icon, size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); } if (info == NULL) { icon = g_themed_icon_new ("application-x-executable"); info = gtk_icon_theme_lookup_by_gicon_for_scale (gtk_icon_theme_get_default (), icon, size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); g_object_unref (icon); } if (info == NULL) return COGL_INVALID_HANDLE; pixbuf = gtk_icon_info_load_icon (info, NULL); g_object_unref (info); if (pixbuf == NULL) return COGL_INVALID_HANDLE; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); orig_pixels = gdk_pixbuf_get_pixels (pixbuf); have_alpha = gdk_pixbuf_get_has_alpha (pixbuf); pixbuf_byte_size = (height - 1) * rowstride + + width * ((n_channels * gdk_pixbuf_get_bits_per_sample (pixbuf) + 7) / 8); pixels = g_malloc0 (rowstride * height); memcpy (pixels, orig_pixels, pixbuf_byte_size); fade_start = width / 2; fade_range = width - fade_start; for (i = fade_start; i < width; i++) { for (j = 0; j < height; j++) { guchar *pixel = &pixels[j * rowstride + i * n_channels]; float fade = 1.0 - ((float) i - fade_start) / fade_range; pixel[0] = 0.5 + pixel[0] * fade; pixel[1] = 0.5 + pixel[1] * fade; pixel[2] = 0.5 + pixel[2] * fade; if (have_alpha) pixel[3] = 0.5 + pixel[3] * fade; } } texture = st_cogl_texture_new_from_data_wrapper (width, height, COGL_TEXTURE_NONE, have_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, pixels); g_free (pixels); g_object_unref (pixbuf); return texture; } /** * cinnamon_app_get_faded_icon: * @app: A #CinnamonApp * @size: Size in pixels * * Return an actor with a horizontally faded look. * * Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon */ ClutterActor * cinnamon_app_get_faded_icon (CinnamonApp *app, int size) { CoglHandle texture; ClutterActor *result; char *cache_key; CreateFadedIconData data; gint scale; CinnamonGlobal *global; StThemeContext *context; /* Don't fade for window backed apps for now...easier to reuse the * property tracking bits, and this helps us visually distinguish * app-tracked from not. */ if (!app->entry) return window_backed_app_get_icon (app, size); global = cinnamon_global_get (); context = st_theme_context_get_for_stage (cinnamon_global_get_stage (global)); g_object_get (context, "scale-factor", &scale, NULL); cache_key = g_strdup_printf ("faded-icon:%s,size=%d,scale=%d", cinnamon_app_get_id (app), size, scale); data.app = app; data.size = size; data.scale = scale; texture = st_texture_cache_load (st_texture_cache_get_default (), cache_key, ST_TEXTURE_CACHE_POLICY_FOREVER, cinnamon_app_create_faded_icon_cpu, &data, NULL); g_free (cache_key); if (texture != COGL_INVALID_HANDLE) { result = clutter_texture_new (); clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (result), texture); } else { result = clutter_texture_new (); g_object_set (result, "opacity", 0, "width", (float) size * scale, "height", (float) size * scale, NULL); } return result; } const char * cinnamon_app_get_name (CinnamonApp *app) { if (app->entry) return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); else if (app->running_state == NULL) return _("Unknown"); else { MetaWindow *window = window_backed_app_get_window (app); const char *name; name = meta_window_get_wm_class (window); if (!name) name = _("Unknown"); return name; } } const char * cinnamon_app_get_description (CinnamonApp *app) { if (app->entry) return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); else return NULL; } /** * cinnamon_app_is_window_backed: * * A window backed application is one which represents just an open * window, i.e. there's no .desktop file assocation, so we don't know * how to launch it again. */ gboolean cinnamon_app_is_window_backed (CinnamonApp *app) { return app->entry == NULL; } typedef struct { MetaWorkspace *workspace; GSList **transients; } CollectTransientsData; static gboolean collect_transients_on_workspace (MetaWindow *window, gpointer datap) { CollectTransientsData *data = datap; if (data->workspace && meta_window_get_workspace (window) != data->workspace) return TRUE; *data->transients = g_slist_prepend (*data->transients, window); return TRUE; } /* The basic idea here is that when we're targeting a window, * if it has transients we want to pick the most recent one * the user interacted with. * This function makes raising GEdit with the file chooser * open work correctly. */ static MetaWindow * find_most_recent_transient_on_same_workspace (MetaDisplay *display, MetaWindow *reference) { GSList *transients, *transients_sorted, *iter; MetaWindow *result; CollectTransientsData data; transients = NULL; data.workspace = meta_window_get_workspace (reference); data.transients = &transients; meta_window_foreach_transient (reference, collect_transients_on_workspace, &data); transients_sorted = meta_display_sort_windows_by_stacking (display, transients); /* Reverse this so we're top-to-bottom (yes, we should probably change the order * returned from the sort_windows_by_stacking function) */ transients_sorted = g_slist_reverse (transients_sorted); g_slist_free (transients); transients = NULL; result = NULL; for (iter = transients_sorted; iter; iter = iter->next) { MetaWindow *window = iter->data; MetaWindowType wintype = meta_window_get_window_type (window); /* Don't want to focus UTILITY types, like the Gimp toolbars */ if (wintype == META_WINDOW_NORMAL || wintype == META_WINDOW_DIALOG) { result = window; break; } } g_slist_free (transients_sorted); return result; } /** * cinnamon_app_activate_window: * @app: a #CinnamonApp * @window: (allow-none): Window to be focused * @timestamp: Event timestamp * * Bring all windows for the given app to the foreground, * but ensure that @window is on top. If @window is %NULL, * the window with the most recent user time for the app * will be used. * * This function has no effect if @app is not currently running. */ void cinnamon_app_activate_window (CinnamonApp *app, MetaWindow *window, guint32 timestamp) { GSList *windows; if (cinnamon_app_get_state (app) != CINNAMON_APP_STATE_RUNNING) return; windows = cinnamon_app_get_windows (app); if (window == NULL && windows) window = windows->data; if (!g_slist_find (windows, window)) return; else { GSList *iter; CinnamonGlobal *global = cinnamon_global_get (); MetaScreen *screen = cinnamon_global_get_screen (global); MetaDisplay *display = meta_screen_get_display (screen); MetaWorkspace *active = meta_screen_get_active_workspace (screen); MetaWorkspace *workspace = meta_window_get_workspace (window); guint32 last_user_timestamp = meta_display_get_last_user_time (display); MetaWindow *most_recent_transient; if (meta_display_xserver_time_is_before (display, timestamp, last_user_timestamp)) { meta_window_set_demands_attention (window); return; } /* Now raise all the other windows for the app that are on * the same workspace, in reverse order to preserve the stacking. */ for (iter = windows; iter; iter = iter->next) { MetaWindow *other_window = iter->data; if (other_window != window) meta_window_raise (other_window); } /* If we have a transient that the user's interacted with more recently than * the window, pick that. */ most_recent_transient = find_most_recent_transient_on_same_workspace (display, window); if (most_recent_transient && meta_display_xserver_time_is_before (display, meta_window_get_user_time (window), meta_window_get_user_time (most_recent_transient))) window = most_recent_transient; if (!cinnamon_window_tracker_is_window_interesting (window)) { /* We won't get notify::user-time signals for uninteresting windows, * which means that an app's last_user_time won't get updated. * Update it here instead. */ app->running_state->last_user_time = timestamp; } if (active != workspace) meta_workspace_activate_with_focus (workspace, window, timestamp); else meta_window_activate (window, timestamp); } } /** * cinnamon_app_activate: * @app: a #CinnamonApp * * Like cinnamon_app_activate_full(), but using the default workspace and * event timestamp. */ void cinnamon_app_activate (CinnamonApp *app) { return cinnamon_app_activate_full (app, -1, 0); } /** * cinnamon_app_activate_full: * @app: a #CinnamonApp * @workspace: launch on this workspace, or -1 for default. Ignored if * activating an existing window * @timestamp: Event timestamp * * Perform an appropriate default action for operating on this application, * dependent on its current state. For example, if the application is not * currently running, launch it. If it is running, activate the most * recently used NORMAL window (or if that window has a transient, the most * recently used transient for that window). */ void cinnamon_app_activate_full (CinnamonApp *app, int workspace, guint32 timestamp) { CinnamonGlobal *global; global = cinnamon_global_get (); if (timestamp == 0) timestamp = cinnamon_global_get_current_time (global); switch (app->state) { case CINNAMON_APP_STATE_STOPPED: { GError *error = NULL; if (!cinnamon_app_launch (app, timestamp, NULL, workspace, NULL, &error)) { char *msg; msg = g_strdup_printf (_("Failed to launch '%s'"), cinnamon_app_get_name (app)); cinnamon_global_notify_error (global, msg, error->message); g_free (msg); g_clear_error (&error); } } break; case CINNAMON_APP_STATE_STARTING: break; case CINNAMON_APP_STATE_RUNNING: cinnamon_app_activate_window (app, NULL, timestamp); break; } } /** * cinnamon_app_open_new_window: * @app: a #CinnamonApp * @workspace: open on this workspace, or -1 for default * * Request that the application create a new window. */ void cinnamon_app_open_new_window (CinnamonApp *app, int workspace) { g_return_if_fail (app->entry != NULL); /* Here we just always launch the application again, even if we know * it was already running. For most applications this * should have the effect of creating a new window, whether that's * a second process (in the case of Calculator) or IPC to existing * instance (Firefox). There are a few less-sensical cases such * as say Pidgin. Ideally, we have the application express to us * that it supports an explicit new-window action. */ cinnamon_app_launch (app, 0, NULL, workspace, NULL, NULL); } /** * cinnamon_app_get_state: * @app: a #CinnamonApp * * Returns: State of the application */ CinnamonAppState cinnamon_app_get_state (CinnamonApp *app) { return app->state; } typedef struct { CinnamonApp *app; MetaWorkspace *active_workspace; } CompareWindowsData; static int cinnamon_app_compare_windows (gconstpointer a, gconstpointer b, gpointer datap) { MetaWindow *win_a = (gpointer)a; MetaWindow *win_b = (gpointer)b; CompareWindowsData *data = datap; gboolean ws_a, ws_b; gboolean vis_a, vis_b; ws_a = meta_window_get_workspace (win_a) == data->active_workspace; ws_b = meta_window_get_workspace (win_b) == data->active_workspace; if (ws_a && !ws_b) return -1; else if (!ws_a && ws_b) return 1; vis_a = meta_window_showing_on_its_workspace (win_a); vis_b = meta_window_showing_on_its_workspace (win_b); if (vis_a && !vis_b) return -1; else if (!vis_a && vis_b) return 1; return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a); } /** * cinnamon_app_get_windows: * @app: * * Get the toplevel, interesting windows which are associated with this * application. The returned list will be sorted first by whether * they're on the active workspace, then by whether they're visible, * and finally by the time the user last interacted with them. * * Returns: (transfer none) (element-type MetaWindow): List of windows */ GSList * cinnamon_app_get_windows (CinnamonApp *app) { if (app->running_state == NULL) return NULL; if (app->running_state->window_sort_stale) { CompareWindowsData data; data.app = app; data.active_workspace = meta_screen_get_active_workspace (cinnamon_global_get_screen (cinnamon_global_get ())); app->running_state->windows = g_slist_sort_with_data (app->running_state->windows, cinnamon_app_compare_windows, &data); app->running_state->window_sort_stale = FALSE; } return app->running_state->windows; } guint cinnamon_app_get_n_windows (CinnamonApp *app) { if (app->running_state == NULL) return 0; return g_slist_length (app->running_state->windows); } static gboolean cinnamon_app_has_visible_windows (CinnamonApp *app) { GSList *iter; if (app->running_state == NULL) return FALSE; for (iter = app->running_state->windows; iter; iter = iter->next) { MetaWindow *window = iter->data; if (meta_window_showing_on_its_workspace (window)) return TRUE; } return FALSE; } gboolean cinnamon_app_is_on_workspace (CinnamonApp *app, MetaWorkspace *workspace) { GSList *iter; if (cinnamon_app_get_state (app) == CINNAMON_APP_STATE_STARTING) { if (app->started_on_workspace == -1 || meta_workspace_index (workspace) == app->started_on_workspace) return TRUE; else return FALSE; } if (app->running_state == NULL) return FALSE; for (iter = app->running_state->windows; iter; iter = iter->next) { if (meta_window_get_workspace (iter->data) == workspace) return TRUE; } return FALSE; } /** * cinnamon_app_compare: * @app: * @other: A #CinnamonApp * * Compare one #CinnamonApp instance to another, in the following way: * - Running applications sort before not-running applications. * - If one of them has visible windows and the other does not, the one * with visible windows is first. * - Finally, the application which the user interacted with most recently * compares earlier. */ int cinnamon_app_compare (CinnamonApp *app, CinnamonApp *other) { gboolean vis_app, vis_other; if (app->state != other->state) { if (app->state == CINNAMON_APP_STATE_RUNNING) return -1; return 1; } vis_app = cinnamon_app_has_visible_windows (app); vis_other = cinnamon_app_has_visible_windows (other); if (vis_app && !vis_other) return -1; else if (!vis_app && vis_other) return 1; if (app->state == CINNAMON_APP_STATE_RUNNING) { if (app->running_state->windows && !other->running_state->windows) return -1; else if (!app->running_state->windows && other->running_state->windows) return 1; return other->running_state->last_user_time - app->running_state->last_user_time; } return 0; } CinnamonApp * _cinnamon_app_new_for_window (MetaWindow *window) { CinnamonApp *app; app = g_object_new (CINNAMON_TYPE_APP, NULL); app->window_id_string = g_strdup_printf ("window:%d", meta_window_get_stable_sequence (window)); _cinnamon_app_add_window (app, window); return app; } CinnamonApp * _cinnamon_app_new (GMenuTreeEntry *info) { CinnamonApp *app; app = g_object_new (CINNAMON_TYPE_APP, NULL); _cinnamon_app_set_entry (app, info); return app; } void _cinnamon_app_set_entry (CinnamonApp *app, GMenuTreeEntry *entry) { if (app->entry != NULL) gmenu_tree_item_unref (app->entry); app->entry = gmenu_tree_item_ref (entry); if (app->name_collation_key != NULL) g_free (app->name_collation_key); app->name_collation_key = g_utf8_collate_key (cinnamon_app_get_name (app), -1); } static void cinnamon_app_state_transition (CinnamonApp *app, CinnamonAppState state) { if (app->state == state) return; g_return_if_fail (!(app->state == CINNAMON_APP_STATE_RUNNING && state == CINNAMON_APP_STATE_STARTING)); app->state = state; if (app->state == CINNAMON_APP_STATE_STOPPED && app->running_state) { unref_running_state (app->running_state); app->running_state = NULL; } _cinnamon_app_system_notify_app_state_changed (cinnamon_app_system_get_default (), app); g_object_notify (G_OBJECT (app), "state"); } static void cinnamon_app_on_unmanaged (MetaWindow *window, CinnamonApp *app) { _cinnamon_app_remove_window (app, window); } static void cinnamon_app_on_user_time_changed (MetaWindow *window, GParamSpec *pspec, CinnamonApp *app) { g_assert (app->running_state != NULL); app->running_state->last_user_time = meta_window_get_user_time (window); /* Ideally we don't want to emit windows-changed if the sort order * isn't actually changing. This check catches most of those. */ if (window != app->running_state->windows->data) { app->running_state->window_sort_stale = TRUE; g_signal_emit (app, cinnamon_app_signals[WINDOWS_CHANGED], 0); } } static void cinnamon_app_on_ws_switch (MetaScreen *screen, int from, int to, MetaMotionDirection direction, gpointer data) { CinnamonApp *app = CINNAMON_APP (data); g_assert (app->running_state != NULL); app->running_state->window_sort_stale = TRUE; g_signal_emit (app, cinnamon_app_signals[WINDOWS_CHANGED], 0); } void _cinnamon_app_add_window (CinnamonApp *app, MetaWindow *window) { guint32 user_time; if (app->running_state && g_slist_find (app->running_state->windows, window)) return; g_object_freeze_notify (G_OBJECT (app)); if (!app->running_state) create_running_state (app); app->running_state->window_sort_stale = TRUE; app->running_state->windows = g_slist_prepend (app->running_state->windows, g_object_ref (window)); g_signal_connect (window, "unmanaged", G_CALLBACK(cinnamon_app_on_unmanaged), app); g_signal_connect (window, "notify::user-time", G_CALLBACK(cinnamon_app_on_user_time_changed), app); user_time = meta_window_get_user_time (window); if (user_time > app->running_state->last_user_time) app->running_state->last_user_time = user_time; if (app->state != CINNAMON_APP_STATE_STARTING) cinnamon_app_state_transition (app, CINNAMON_APP_STATE_RUNNING); g_object_thaw_notify (G_OBJECT (app)); g_signal_emit (app, cinnamon_app_signals[WINDOWS_CHANGED], 0); } void _cinnamon_app_remove_window (CinnamonApp *app, MetaWindow *window) { g_assert (app->running_state != NULL); if (!g_slist_find (app->running_state->windows, window)) return; g_signal_handlers_disconnect_by_func (window, G_CALLBACK(cinnamon_app_on_unmanaged), app); g_signal_handlers_disconnect_by_func (window, G_CALLBACK(cinnamon_app_on_user_time_changed), app); g_object_unref (window); app->running_state->windows = g_slist_remove (app->running_state->windows, window); if (app->running_state->windows == NULL) cinnamon_app_state_transition (app, CINNAMON_APP_STATE_STOPPED); g_signal_emit (app, cinnamon_app_signals[WINDOWS_CHANGED], 0); } /** * cinnamon_app_get_pids: * @app: a #CinnamonApp * * Returns: (transfer container) (element-type int): An unordered list of process identifers associated with this application. */ GSList * cinnamon_app_get_pids (CinnamonApp *app) { GSList *result; GSList *iter; result = NULL; for (iter = cinnamon_app_get_windows (app); iter; iter = iter->next) { MetaWindow *window = iter->data; int pid = meta_window_get_pid (window); /* Note in the (by far) common case, app will only have one pid, so * we'll hit the first element, so don't worry about O(N^2) here. */ if (!g_slist_find (result, GINT_TO_POINTER (pid))) result = g_slist_prepend (result, GINT_TO_POINTER (pid)); } return result; } void _cinnamon_app_handle_startup_sequence (CinnamonApp *app, SnStartupSequence *sequence) { gboolean starting = !sn_startup_sequence_get_completed (sequence); /* The Cinnamon design calls for on application launch, the app title * appears at top, and no X window is focused. So when we get * a startup-notification for this app, transition it to STARTING * if it's currently stopped, set it as our application focus, * but focus the no_focus window. */ if (starting && cinnamon_app_get_state (app) == CINNAMON_APP_STATE_STOPPED) { MetaScreen *screen = cinnamon_global_get_screen (cinnamon_global_get ()); MetaDisplay *display = meta_screen_get_display (screen); cinnamon_app_state_transition (app, CINNAMON_APP_STATE_STARTING); meta_display_focus_the_no_focus_window (display, screen, sn_startup_sequence_get_timestamp (sequence)); app->started_on_workspace = sn_startup_sequence_get_workspace (sequence); } if (!starting) { if (app->running_state && app->running_state->windows) cinnamon_app_state_transition (app, CINNAMON_APP_STATE_RUNNING); else /* application have > 1 .desktop file */ cinnamon_app_state_transition (app, CINNAMON_APP_STATE_STOPPED); } } /** * cinnamon_app_request_quit: * @app: A #CinnamonApp * * Initiate an asynchronous request to quit this application. * The application may interact with the user, and the user * might cancel the quit request from the application UI. * * This operation may not be supported for all applications. * * Returns: %TRUE if a quit request is supported for this application */ gboolean cinnamon_app_request_quit (CinnamonApp *app) { GSList *iter; if (cinnamon_app_get_state (app) != CINNAMON_APP_STATE_RUNNING) return FALSE; /* TODO - check for an XSMP connection; we could probably use that */ for (iter = app->running_state->windows; iter; iter = iter->next) { MetaWindow *win = iter->data; if (!cinnamon_window_tracker_is_window_interesting (win)) continue; meta_window_delete (win, cinnamon_global_get_current_time (cinnamon_global_get ())); } return TRUE; } static void _gather_pid_callback (GDesktopAppInfo *gapp, GPid pid, gpointer data) { CinnamonApp *app; CinnamonWindowTracker *tracker; g_return_if_fail (data != NULL); app = CINNAMON_APP (data); tracker = cinnamon_window_tracker_get_default (); _cinnamon_window_tracker_add_child_process_app (tracker, pid, app); } /** * cinnamon_app_launch: * @timestamp: Event timestamp, or 0 for current event timestamp * @uris: (element-type utf8): List of uris to pass to application * @workspace: Start on this workspace, or -1 for default * @startup_id: (out): Returned startup notification ID, or %NULL if none * @error: A #GError */ gboolean cinnamon_app_launch (CinnamonApp *app, guint timestamp, GList *uris, int workspace, char **startup_id, GError **error) { GDesktopAppInfo *gapp; GdkAppLaunchContext *context; gboolean ret; CinnamonGlobal *global; MetaScreen *screen; GdkDisplay *gdisplay; if (startup_id) *startup_id = NULL; if (app->entry == NULL) { MetaWindow *window = window_backed_app_get_window (app); /* We can't pass URIs into a window; shouldn't hit this * code path. If we do, fix the caller to disallow it. */ g_return_val_if_fail (uris == NULL, TRUE); meta_window_activate (window, timestamp); return TRUE; } global = cinnamon_global_get (); screen = cinnamon_global_get_screen (global); gdisplay = gdk_screen_get_display (cinnamon_global_get_gdk_screen (global)); if (timestamp == 0) timestamp = cinnamon_global_get_current_time (global); if (workspace < 0) workspace = meta_screen_get_active_workspace_index (screen); context = gdk_display_get_app_launch_context (gdisplay); gdk_app_launch_context_set_timestamp (context, timestamp); gdk_app_launch_context_set_desktop (context, workspace); gapp = gmenu_tree_entry_get_app_info (app->entry); ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris, G_APP_LAUNCH_CONTEXT (context), G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, _gather_pid_callback, app, error); g_object_unref (context); return ret; } /** * cinnamon_app_get_app_info: * @app: a #CinnamonApp * * Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window */ GDesktopAppInfo * cinnamon_app_get_app_info (CinnamonApp *app) { if (app->entry) return gmenu_tree_entry_get_app_info (app->entry); return NULL; } /** * cinnamon_app_get_tree_entry: * @app: a #CinnamonApp * * Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window */ GMenuTreeEntry * cinnamon_app_get_tree_entry (CinnamonApp *app) { return app->entry; } static void create_running_state (CinnamonApp *app) { MetaScreen *screen; g_assert (app->running_state == NULL); screen = cinnamon_global_get_screen (cinnamon_global_get ()); app->running_state = g_slice_new0 (CinnamonAppRunningState); app->running_state->refcount = 1; app->running_state->workspace_switch_id = g_signal_connect (screen, "workspace-switched", G_CALLBACK(cinnamon_app_on_ws_switch), app); } static void unref_running_state (CinnamonAppRunningState *state) { MetaScreen *screen; state->refcount--; if (state->refcount > 0) return; screen = cinnamon_global_get_screen (cinnamon_global_get ()); g_signal_handler_disconnect (screen, state->workspace_switch_id); g_slice_free (CinnamonAppRunningState, state); } static char * trim_exec_line (const char *str) { const char *start, *end, *pos; if (str == NULL) return NULL; end = strchr (str, ' '); if (end == NULL) end = str + strlen (str); start = str; while ((pos = strchr (start, '/')) && pos < end) start = ++pos; return g_strndup (start, end - start); } static void cinnamon_app_init_search_data (CinnamonApp *app) { const char *name; const char *exec; const char *comment; char *normalized_exec; GDesktopAppInfo *appinfo; appinfo = gmenu_tree_entry_get_app_info (app->entry); name = g_app_info_get_name (G_APP_INFO (appinfo)); app->casefolded_name = cinnamon_util_normalize_and_casefold (name); comment = g_app_info_get_description (G_APP_INFO (appinfo)); app->casefolded_description = cinnamon_util_normalize_and_casefold (comment); exec = g_app_info_get_executable (G_APP_INFO (appinfo)); normalized_exec = cinnamon_util_normalize_and_casefold (exec); app->casefolded_exec = trim_exec_line (normalized_exec); g_free (normalized_exec); } /** * cinnamon_app_compare_by_name: * @app: * @other: * * Order two applications by name. * * Returns: -1, 0, or 1; suitable for use as a comparison function for e.g. g_slist_sort() */ int cinnamon_app_compare_by_name (CinnamonApp *app, CinnamonApp *other) { return strcmp (app->name_collation_key, other->name_collation_key); } static CinnamonAppSearchMatch _cinnamon_app_match_search_terms (CinnamonApp *app, GSList *terms) { GSList *iter; CinnamonAppSearchMatch match; if (G_UNLIKELY (!app->casefolded_name)) cinnamon_app_init_search_data (app); match = MATCH_NONE; for (iter = terms; iter; iter = iter->next) { CinnamonAppSearchMatch current_match; const char *term = iter->data; const char *p; current_match = MATCH_NONE; p = strstr (app->casefolded_name, term); if (p != NULL) { if (p == app->casefolded_name || *(p - 1) == ' ') current_match = MATCH_PREFIX; else current_match = MATCH_SUBSTRING; } if (app->casefolded_exec) { p = strstr (app->casefolded_exec, term); if (p != NULL) { if (p == app->casefolded_exec || *(p - 1) == '-') current_match = MATCH_PREFIX; else if (current_match < MATCH_PREFIX) current_match = MATCH_SUBSTRING; } } if (app->casefolded_description && current_match < MATCH_PREFIX) { /* Only do substring matches, as prefix matches are not meaningful * enough for descriptions */ p = strstr (app->casefolded_description, term); if (p != NULL) current_match = MATCH_SUBSTRING; } if (current_match == MATCH_NONE) return current_match; if (current_match > match) match = current_match; } return match; } void _cinnamon_app_do_match (CinnamonApp *app, GSList *terms, GSList **prefix_results, GSList **substring_results) { CinnamonAppSearchMatch match; GAppInfo *appinfo; g_assert (app != NULL); /* Skip window-backed apps */ appinfo = (GAppInfo*)cinnamon_app_get_app_info (app); if (appinfo == NULL) return; /* Skip not-visible apps */ if (!g_app_info_should_show (appinfo)) return; match = _cinnamon_app_match_search_terms (app, terms); switch (match) { case MATCH_NONE: break; case MATCH_PREFIX: *prefix_results = g_slist_prepend (*prefix_results, app); break; case MATCH_SUBSTRING: *substring_results = g_slist_prepend (*substring_results, app); break; } } static void cinnamon_app_init (CinnamonApp *self) { self->state = CINNAMON_APP_STATE_STOPPED; } static void cinnamon_app_dispose (GObject *object) { CinnamonApp *app = CINNAMON_APP (object); if (app->entry) { gmenu_tree_item_unref (app->entry); app->entry = NULL; } if (app->running_state) { while (app->running_state->windows) _cinnamon_app_remove_window (app, app->running_state->windows->data); } G_OBJECT_CLASS(cinnamon_app_parent_class)->dispose (object); } static void cinnamon_app_finalize (GObject *object) { CinnamonApp *app = CINNAMON_APP (object); g_free (app->window_id_string); g_free (app->casefolded_name); g_free (app->name_collation_key); g_free (app->casefolded_description); g_free (app->casefolded_exec); G_OBJECT_CLASS(cinnamon_app_parent_class)->finalize (object); } static void cinnamon_app_class_init(CinnamonAppClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = cinnamon_app_get_property; gobject_class->dispose = cinnamon_app_dispose; gobject_class->finalize = cinnamon_app_finalize; cinnamon_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed", CINNAMON_TYPE_APP, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * CinnamonApp:state: * * The high-level state of the application, effectively whether it's * running or not, or transitioning between those states. */ g_object_class_install_property (gobject_class, PROP_STATE, g_param_spec_enum ("state", "State", "Application state", CINNAMON_TYPE_APP_STATE, CINNAMON_APP_STATE_STOPPED, G_PARAM_READABLE)); } Cinnamon-2.8.6/src/cinnamon-recorder-src.c0000664000175000017500000002036412625666430017361 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #define GST_USE_UNSTABLE_API #include #include "cinnamon-recorder-src.h" struct _CinnamonRecorderSrc { GstPushSrc parent; GMutex *mutex; GstClock *clock; GstClockTime last_frame_time; GstCaps *caps; GAsyncQueue *queue; gboolean closed; guint memory_used; guint memory_used_update_idle; }; struct _CinnamonRecorderSrcClass { GstPushSrcClass parent_class; }; enum { PROP_0, PROP_CAPS, PROP_MEMORY_USED }; /* Special marker value once the source is closed */ #define RECORDER_QUEUE_END ((GstBuffer *)1) G_DEFINE_TYPE(CinnamonRecorderSrc, cinnamon_recorder_src, GST_TYPE_PUSH_SRC); static void cinnamon_recorder_src_init (CinnamonRecorderSrc *src) { gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (src), TRUE); src->clock = gst_system_clock_obtain (); src->last_frame_time = 0; src->queue = g_async_queue_new (); src->mutex = g_mutex_new (); } static gboolean cinnamon_recorder_src_memory_used_update_idle (gpointer data) { CinnamonRecorderSrc *src = data; g_mutex_lock (src->mutex); src->memory_used_update_idle = 0; g_mutex_unlock (src->mutex); g_object_notify (G_OBJECT (src), "memory-used"); return FALSE; } /* The memory_used property is used to monitor buffer usage, * so we marshal notification back to the main loop thread. */ static void cinnamon_recorder_src_update_memory_used (CinnamonRecorderSrc *src, int delta) { g_mutex_lock (src->mutex); src->memory_used += delta; if (src->memory_used_update_idle == 0) src->memory_used_update_idle = g_idle_add (cinnamon_recorder_src_memory_used_update_idle, src); g_mutex_unlock (src->mutex); } /* The create() virtual function is responsible for returning the next buffer. * We just pop buffers off of the queue and block if necessary. */ static GstFlowReturn cinnamon_recorder_src_create (GstPushSrc *push_src, GstBuffer **buffer_out) { CinnamonRecorderSrc *src = CINNAMON_RECORDER_SRC (push_src); GstBuffer *buffer; if (src->closed) return GST_FLOW_EOS; buffer = g_async_queue_pop (src->queue); if (src->last_frame_time == 0) src->last_frame_time = gst_clock_get_time (GST_CLOCK (src->clock)); if (buffer == RECORDER_QUEUE_END) { /* Returning UNEXPECTED here will cause a EOS message to be sent */ src->closed = TRUE; return GST_FLOW_EOS; } cinnamon_recorder_src_update_memory_used (src, - (int)(gst_buffer_get_size(buffer) / 1024)); *buffer_out = buffer; GST_BUFFER_DURATION(*buffer_out) = GST_CLOCK_DIFF (src->last_frame_time, gst_clock_get_time (GST_CLOCK (src->clock))); src->last_frame_time = gst_clock_get_time (GST_CLOCK (src->clock)); return GST_FLOW_OK; } static void cinnamon_recorder_src_set_caps (CinnamonRecorderSrc *src, const GstCaps *caps) { if (caps == src->caps) return; if (src->caps != NULL) { gst_caps_unref (src->caps); src->caps = NULL; } if (caps) { /* The capabilities will be negotated with the downstream element * and set on the pad when the first buffer is pushed. */ src->caps = gst_caps_copy (caps); } else src->caps = NULL; } static void cinnamon_recorder_src_finalize (GObject *object) { CinnamonRecorderSrc *src = CINNAMON_RECORDER_SRC (object); if (src->memory_used_update_idle) { g_source_remove (src->memory_used_update_idle); src->memory_used_update_idle = 0; } cinnamon_recorder_src_set_caps (src, NULL); g_async_queue_unref (src->queue); g_mutex_free (src->mutex); gst_object_unref (src->clock); G_OBJECT_CLASS (cinnamon_recorder_src_parent_class)->finalize (object); } static void cinnamon_recorder_src_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonRecorderSrc *src = CINNAMON_RECORDER_SRC (object); switch (prop_id) { case PROP_CAPS: cinnamon_recorder_src_set_caps (src, gst_value_get_caps (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_recorder_src_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonRecorderSrc *src = CINNAMON_RECORDER_SRC (object); switch (prop_id) { case PROP_CAPS: gst_value_set_caps (value, src->caps); break; case PROP_MEMORY_USED: g_mutex_lock (src->mutex); g_value_set_uint (value, src->memory_used); g_mutex_unlock (src->mutex); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_recorder_src_class_init (CinnamonRecorderSrcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS (klass); static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); object_class->finalize = cinnamon_recorder_src_finalize; object_class->set_property = cinnamon_recorder_src_set_property; object_class->get_property = cinnamon_recorder_src_get_property; push_src_class->create = cinnamon_recorder_src_create; g_object_class_install_property (object_class, PROP_CAPS, g_param_spec_boxed ("caps", "Caps", "Fixed GstCaps for the source", GST_TYPE_CAPS, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_MEMORY_USED, g_param_spec_uint ("memory-used", "Memory Used", "Memory currently used by the queue (in kB)", 0, G_MAXUINT, 0, G_PARAM_READABLE)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_template)); gst_element_class_set_details_simple (element_class, "CinnamonRecorderSrc", "Generic/Src", "Feed screen capture data to a pipeline", "Owen Taylor "); } /** * cinnamon_recorder_src_add_buffer: * * Adds a buffer to the internal queue to be pushed out at the next opportunity. * There is no flow control, so arbitrary amounts of memory may be used by * the buffers on the queue. The buffer contents must match the #GstCaps * set in the :caps property. */ void cinnamon_recorder_src_add_buffer (CinnamonRecorderSrc *src, GstBuffer *buffer) { g_return_if_fail (CINNAMON_IS_RECORDER_SRC (src)); g_return_if_fail (src->caps != NULL); gst_base_src_set_caps (GST_BASE_SRC (src), src->caps); cinnamon_recorder_src_update_memory_used (src, (int)(gst_buffer_get_size(buffer) / 1024)); g_async_queue_push (src->queue, gst_buffer_ref (buffer)); } /** * cinnamon_recorder_src_close: * * Indicates the end of the input stream. Once all previously added buffers have * been pushed out an end-of-stream message will be sent. */ void cinnamon_recorder_src_close (CinnamonRecorderSrc *src) { /* We can't send a message to the source immediately or buffers that haven't * been pushed yet will be discarded. Instead stick a marker onto our own * queue to send an event once everything has been pushed. */ g_async_queue_push (src->queue, RECORDER_QUEUE_END); } static gboolean plugin_init (GstPlugin *plugin) { gst_element_register(plugin, "cinnamonrecordersrc", GST_RANK_NONE, CINNAMON_TYPE_RECORDER_SRC); return TRUE; } /** * cinnamon_recorder_src_register: * Registers a plugin holding our single element to use privately in * this application. Can safely be called multiple times. */ void cinnamon_recorder_src_register (void) { static gboolean registered = FALSE; if (registered) return; gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR, "cinnamonrecorder", "Plugin for CinnamonRecorder", plugin_init, "0.1", "LGPL", "cinnamon", "cinnamon", "http://cinnamon.linuxmint.com"); registered = TRUE; } Cinnamon-2.8.6/src/cinnamon-window-tracker.h0000664000175000017500000000514412625666430017733 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_WINDOW_TRACKER_H__ #define __CINNAMON_WINDOW_TRACKER_H__ #include #include #include #include "cinnamon-app.h" #include "cinnamon-app-system.h" G_BEGIN_DECLS typedef struct _CinnamonWindowTracker CinnamonWindowTracker; typedef struct _CinnamonWindowTrackerClass CinnamonWindowTrackerClass; typedef struct _CinnamonWindowTrackerPrivate CinnamonWindowTrackerPrivate; #define CINNAMON_TYPE_WINDOW_TRACKER (cinnamon_window_tracker_get_type ()) #define CINNAMON_WINDOW_TRACKER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_WINDOW_TRACKER, CinnamonWindowTracker)) #define CINNAMON_WINDOW_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_WINDOW_TRACKER, CinnamonWindowTrackerClass)) #define CINNAMON_IS_WINDOW_TRACKER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_WINDOW_TRACKER)) #define CINNAMON_IS_WINDOW_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_WINDOW_TRACKER)) #define CINNAMON_WINDOW_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_WINDOW_TRACKER, CinnamonWindowTrackerClass)) struct _CinnamonWindowTrackerClass { GObjectClass parent_class; }; GType cinnamon_window_tracker_get_type (void) G_GNUC_CONST; CinnamonWindowTracker* cinnamon_window_tracker_get_default(void); CinnamonApp *cinnamon_window_tracker_get_window_app (CinnamonWindowTracker *tracker, MetaWindow *metawin); CinnamonApp *cinnamon_window_tracker_get_app_from_pid (CinnamonWindowTracker *tracker, int pid); gboolean cinnamon_window_tracker_is_window_interesting (MetaWindow *window); const char *_cinnamon_window_tracker_get_app_context (CinnamonWindowTracker *tracker, CinnamonApp *app); GSList *cinnamon_window_tracker_get_startup_sequences (CinnamonWindowTracker *tracker); /* Hidden typedef for SnStartupSequence */ typedef struct _CinnamonStartupSequence CinnamonStartupSequence; #define CINNAMON_TYPE_STARTUP_SEQUENCE (cinnamon_startup_sequence_get_type ()) GType cinnamon_startup_sequence_get_type (void); const char *cinnamon_startup_sequence_get_id (CinnamonStartupSequence *sequence); CinnamonApp *cinnamon_startup_sequence_get_app (CinnamonStartupSequence *sequence); const char *cinnamon_startup_sequence_get_name (CinnamonStartupSequence *sequence); gboolean cinnamon_startup_sequence_get_completed (CinnamonStartupSequence *sequence); ClutterActor *cinnamon_startup_sequence_create_icon (CinnamonStartupSequence *sequence, guint size); G_END_DECLS #endif /* __CINNAMON_WINDOW_TRACKER_H__ */ Cinnamon-2.8.6/src/cinnamon-screenshot.h0000664000175000017500000000536712625666430017157 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_SCREENSHOT_H__ #define __CINNAMON_SCREENSHOT_H__ /** * SECTION:cinnamon-screenshot * @short_description: Grabs screenshots of areas and/or windows * * The #CinnamonScreenshot object is used to take screenshots of screen * areas or windows and write them out as png files. * */ typedef struct _CinnamonScreenshot CinnamonScreenshot; typedef struct _CinnamonScreenshotClass CinnamonScreenshotClass; #define CINNAMON_TYPE_SCREENSHOT (cinnamon_screenshot_get_type ()) #define CINNAMON_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshot)) #define CINNAMON_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshotClass)) #define CINNAMON_IS_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_SCREENSHOT)) #define CINNAMON_IS_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_SCREENSHOT)) #define CINNAMON_SCREENSHOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshotClass)) GType cinnamon_screenshot_get_type (void) G_GNUC_CONST; CinnamonScreenshot *cinnamon_screenshot_new (void); typedef void (*CinnamonScreenshotCallback) (CinnamonScreenshot *screenshot, gboolean success, cairo_rectangle_int_t *screenshot_area); void cinnamon_screenshot_screenshot_area (CinnamonScreenshot *screenshot, gboolean include_cursor, int x, int y, int width, int height, const char *filename, CinnamonScreenshotCallback callback); void cinnamon_screenshot_screenshot_window (CinnamonScreenshot *screenshot, gboolean include_frame, gboolean include_cursor, const char *filename, CinnamonScreenshotCallback callback); void cinnamon_screenshot_screenshot (CinnamonScreenshot *screenshot, gboolean include_cursor, const char *filename, CinnamonScreenshotCallback callback); #endif /* ___CINNAMON_SCREENSHOT_H__ */ Cinnamon-2.8.6/src/cinnamon-mount-operation.h0000664000175000017500000000504412625666430020132 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Author: Cosimo Cecchi * */ #ifndef __CINNAMON_MOUNT_OPERATION_H__ #define __CINNAMON_MOUNT_OPERATION_H__ #include G_BEGIN_DECLS #define CINNAMON_TYPE_MOUNT_OPERATION (cinnamon_mount_operation_get_type ()) #define CINNAMON_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CINNAMON_TYPE_MOUNT_OPERATION, CinnamonMountOperation)) #define CINNAMON_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CINNAMON_TYPE_MOUNT_OPERATION, CinnamonMountOperationClass)) #define CINNAMON_IS_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CINNAMON_TYPE_MOUNT_OPERATION)) #define CINNAMON_IS_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CINNAMON_TYPE_MOUNT_OPERATION)) #define CINNAMON_MOUNT_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CINNAMON_TYPE_MOUNT_OPERATION, CinnamonMountOperationClass)) typedef struct _CinnamonMountOperation CinnamonMountOperation; typedef struct _CinnamonMountOperationClass CinnamonMountOperationClass; typedef struct _CinnamonMountOperationPrivate CinnamonMountOperationPrivate; struct _CinnamonMountOperation { GMountOperation parent_instance; CinnamonMountOperationPrivate *priv; }; struct _CinnamonMountOperationClass { GMountOperationClass parent_class; }; GType cinnamon_mount_operation_get_type (void); GMountOperation *cinnamon_mount_operation_new (void); GArray * cinnamon_mount_operation_get_show_processes_pids (CinnamonMountOperation *self); gchar ** cinnamon_mount_operation_get_show_processes_choices (CinnamonMountOperation *self); gchar * cinnamon_mount_operation_get_show_processes_message (CinnamonMountOperation *self); G_END_DECLS #endif /* __CINNAMON_MOUNT_OPERATION_H__ */ Cinnamon-2.8.6/src/cinnamon-stack.c0000664000175000017500000001452212625666430016073 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:cinnamon-stack * @short_description: Pure "Z-axis" container class * * A #CinnamonStack draws its children on top of each other, * aligned to the top left. It will be sized in width/height * according to the largest such dimension of its children, and * all children will be allocated that size. This differs * from #ClutterGroup which allocates its children their natural * size, even if that would overflow the size allocated to the stack. */ #include "config.h" #include "cinnamon-stack.h" G_DEFINE_TYPE (CinnamonStack, cinnamon_stack, ST_TYPE_CONTAINER); static void cinnamon_stack_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (cinnamon_stack_parent_class)->paint (actor); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void cinnamon_stack_pick (ClutterActor *actor, const ClutterColor *pick) { /* Chain up so we get a bounding box painted (if we are reactive) */ CLUTTER_ACTOR_CLASS (cinnamon_stack_parent_class)->pick (actor, pick); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void cinnamon_stack_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox content_box; GList *children, *iter; CLUTTER_ACTOR_CLASS (cinnamon_stack_parent_class)->allocate (self, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); children = st_container_get_children_list (ST_CONTAINER (self)); for (iter = children; iter; iter = iter->next) { ClutterActor *actor = CLUTTER_ACTOR (iter->data); ClutterActorBox child_box = content_box; clutter_actor_allocate (actor, &child_box, flags); } } static void cinnamon_stack_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { CinnamonStack *stack = CINNAMON_STACK (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gboolean first = TRUE; float min = 0, natural = 0; GList *children; GList *iter; st_theme_node_adjust_for_width (theme_node, &for_width); children = st_container_get_children_list (ST_CONTAINER (stack)); for (iter = children; iter; iter = iter->next) { ClutterActor *child = iter->data; float child_min, child_natural; clutter_actor_get_preferred_height (child, for_width, &child_min, &child_natural); if (first) { first = FALSE; min = child_min; natural = child_natural; } else { if (child_min > min) min = child_min; if (child_natural > natural) natural = child_natural; } } if (min_height_p) *min_height_p = min; if (natural_height_p) *natural_height_p = natural; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void cinnamon_stack_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { CinnamonStack *stack = CINNAMON_STACK (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gboolean first = TRUE; float min = 0, natural = 0; GList *iter; GList *children; st_theme_node_adjust_for_height (theme_node, &for_height); children = st_container_get_children_list (ST_CONTAINER (stack)); for (iter = children; iter; iter = iter->next) { ClutterActor *child = iter->data; float child_min, child_natural; clutter_actor_get_preferred_width (child, for_height, &child_min, &child_natural); if (first) { first = FALSE; min = child_min; natural = child_natural; } else { if (child_min > min) min = child_min; if (child_natural > natural) natural = child_natural; } } if (min_width_p) *min_width_p = min; if (natural_width_p) *natural_width_p = natural; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static gboolean cinnamon_stack_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction) { ClutterActor *top_actor; GList *children; /* If the stack is itself focusable, then focus into or out of * it, as appropriate. */ if (st_widget_get_can_focus (widget)) { if (from && clutter_actor_contains (CLUTTER_ACTOR (widget), from)) return FALSE; clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget)); return TRUE; } /* Otherwise, navigate into its top-most child only */ children = st_container_get_children_list (ST_CONTAINER (widget)); if (!children) return FALSE; top_actor = g_list_last (children)->data; if (ST_IS_WIDGET (top_actor)) return st_widget_navigate_focus (ST_WIDGET (top_actor), from, direction, FALSE); else return FALSE; } static void cinnamon_stack_class_init (CinnamonStackClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); actor_class->paint = cinnamon_stack_paint; actor_class->pick = cinnamon_stack_pick; actor_class->get_preferred_width = cinnamon_stack_get_preferred_width; actor_class->get_preferred_height = cinnamon_stack_get_preferred_height; actor_class->allocate = cinnamon_stack_allocate; widget_class->navigate_focus = cinnamon_stack_navigate_focus; } static void cinnamon_stack_init (CinnamonStack *actor) { } Cinnamon-2.8.6/src/cinnamon-recorder.c0000664000175000017500000015443512625666430016603 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include #include #include #define GST_USE_UNSTABLE_API #include #include "cinnamon-recorder-src.h" #include "cinnamon-recorder.h" #include #include #include "st.h" typedef enum { RECORDER_STATE_CLOSED, RECORDER_STATE_PAUSED, RECORDER_STATE_RECORDING } RecorderState; typedef struct _RecorderPipeline RecorderPipeline; struct _CinnamonRecorderClass { GObjectClass parent_class; }; struct _CinnamonRecorder { GObject parent; /* A "maximum" amount of memory to use for buffering. This is used * to alert the user that they are filling up memory rather than * any that actually affects recording. (In kB) */ guint memory_target; guint memory_used; /* Current memory used. (In kB) */ RecorderState state; char *unique; /* The unique string we are using for this recording */ int count; /* How many times the recording has been started */ ClutterStage *stage; int stage_width; int stage_height; gboolean have_pointer; int pointer_x; int pointer_y; guint height_adjust; // Y adjustment from bottom panel, if any gboolean have_xfixes; int xfixes_event_base; CoglHandle recording_icon; /* icon shown while playing */ cairo_surface_t *cursor_image; int cursor_hot_x; int cursor_hot_y; gboolean only_paint; /* Used to temporarily suppress recording */ int framerate; char *pipeline_description; char *filename; gboolean filename_has_count; /* %c used: handle pausing differently */ /* We might have multiple pipelines that are finishing encoding * to go along with the current pipeline where we are recording. */ RecorderPipeline *current_pipeline; /* current pipeline */ GSList *pipelines; /* all pipelines */ GstClockTime start_time; /* When we started recording (adjusted for pauses) */ GstClockTime pause_time; /* When the pipeline was paused */ /* GSource IDs for different timeouts and idles */ guint redraw_timeout; guint redraw_idle; guint update_memory_used_timeout; guint update_pointer_timeout; guint repaint_hook_id; }; struct _RecorderPipeline { CinnamonRecorder *recorder; GstElement *pipeline; GstElement *src; int outfile; }; static void recorder_set_stage (CinnamonRecorder *recorder, ClutterStage *stage); static void recorder_set_framerate (CinnamonRecorder *recorder, int framerate); static void recorder_set_pipeline (CinnamonRecorder *recorder, const char *pipeline); static void recorder_set_filename (CinnamonRecorder *recorder, const char *filename); static void recorder_pipeline_set_caps (RecorderPipeline *pipeline); static void recorder_pipeline_closed (RecorderPipeline *pipeline); enum { PROP_0, PROP_STAGE, PROP_FRAMERATE, PROP_PIPELINE, PROP_FILENAME }; G_DEFINE_TYPE(CinnamonRecorder, cinnamon_recorder, G_TYPE_OBJECT); /* The number of frames per second we configure for the GStreamer pipeline. * (the number of frames we actually write into the GStreamer pipeline is * based entirely on how fast clutter is drawing.) Using 60fps seems high * but the observed smoothness is a lot better than for 30fps when encoding * as theora for a minimal size increase. This may be an artifact of the * encoding process. */ #define DEFAULT_FRAMES_PER_SECOND 15 /* The time (in milliseconds) between querying the server for the cursor * position. */ #define UPDATE_POINTER_TIME 100 /* The time we wait (in milliseconds) before redrawing when the memory used * changes. */ #define UPDATE_MEMORY_USED_DELAY 500 /* Maximum time between frames, in milliseconds. If we don't send data * for a long period of time, then when we send the next frame, a lot * of work can be created for the encoder to do, so we want to force a * periodic redraw when nothing happen. */ #define MAXIMUM_PAUSE_TIME 1000 /* The default pipeline. videorate is used to give a constant stream of * frames to theora even if there is a pause because nothing is moving. * (Theora does have some support for frames at non-uniform times, but * things seem to break down if there are large gaps.) */ #define DEFAULT_PIPELINE "vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux" /* The default filename pattern. Example cinnamon-20090311b-2.webm */ #define DEFAULT_FILENAME "cinnamon-%d%u-%c.webm" /* If we can find the amount of memory on the machine, we use half * of that for memory_target, otherwise, we use this value, in kB. */ #define DEFAULT_MEMORY_TARGET (512*1024) #define PANEL_HEIGHT_KEY "panel-bottom-height" #define SCHEMA_CINNAMON "org.cinnamon" /* Create an emblem to show at the lower-left corner of the stage while * recording. The emblem is drawn *after* we record the frame so doesn't * show up in the frame. */ static CoglHandle create_recording_icon (void) { cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 32, 32); cairo_t *cr; cairo_pattern_t *pat; CoglHandle texture; cr = cairo_create (surface); /* clear to transparent */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); /* radial "glow" */ pat = cairo_pattern_create_radial (16, 16, 6, 16, 16, 14); cairo_pattern_add_color_stop_rgba (pat, 0.0, 1, 0, 0, 1); /* opaque red */ cairo_pattern_add_color_stop_rgba (pat, 1.0, 1, 0, 0, 0); /* transparent red */ cairo_set_source (cr, pat); cairo_paint (cr); cairo_pattern_destroy (pat); /* red circle */ cairo_arc (cr, 16, 16, 8, 0, 2 * M_PI); cairo_set_source_rgb (cr, 1, 0, 0); cairo_fill (cr); cairo_destroy (cr); texture = st_cogl_texture_new_from_data_wrapper (32, 32, COGL_TEXTURE_NONE, CLUTTER_CAIRO_FORMAT_ARGB32, COGL_PIXEL_FORMAT_ANY, cairo_image_surface_get_stride (surface), cairo_image_surface_get_data (surface)); cairo_surface_destroy (surface); return texture; } static guint get_memory_target (void) { FILE *f; /* Really simple "get amount of memory on the machine" if it * doesn't work, you just get the default memory target. */ f = fopen("/proc/meminfo", "r"); if (!f) return DEFAULT_MEMORY_TARGET; while (!feof(f)) { gchar line_buffer[1024]; guint mem_total; if (fscanf(f, "MemTotal: %u", &mem_total) == 1) { fclose(f); return mem_total / 2; } /* Skip to the next line and discard what we read */ if (fgets(line_buffer, sizeof(line_buffer), f) == NULL) break; } fclose(f); return DEFAULT_MEMORY_TARGET; } /* * Used to force full stage redraws during recording to avoid artifacts * * Note: That this will cause the stage to be repainted on * every animation frame even if the frame wouldn't normally cause any new * drawing */ static gboolean recorder_repaint_hook (gpointer data) { ClutterActor *stage = data; clutter_actor_queue_redraw (stage); return TRUE; } static void cinnamon_recorder_init (CinnamonRecorder *recorder) { /* Calling gst_init() is a no-op if GStreamer was previously initialized */ gst_init (NULL, NULL); cinnamon_recorder_src_register (); // Get bottom panel height from settings so we don't overlap it // with our recording indicators. // GVariant *variant = NULL; // GSettings *desktop_settings; // desktop_settings = g_settings_new (SCHEMA_CINNAMON); // variant = g_settings_get_value (desktop_settings, PANEL_HEIGHT_KEY); // g_variant_get (variant, "i", &recorder->height_adjust); // g_variant_unref (variant); // g_object_unref (desktop_settings); recorder->recording_icon = create_recording_icon (); recorder->memory_target = get_memory_target(); recorder->state = RECORDER_STATE_CLOSED; recorder->framerate = DEFAULT_FRAMES_PER_SECOND; } static void cinnamon_recorder_finalize (GObject *object) { CinnamonRecorder *recorder = CINNAMON_RECORDER (object); GSList *l; for (l = recorder->pipelines; l; l = l->next) { RecorderPipeline *pipeline = l->data; /* Remove the back-reference. The pipeline will be freed * when it finishes. (Or when the process exits, but that's * out of our control.) */ pipeline->recorder = NULL; } if (recorder->update_memory_used_timeout) { g_source_remove (recorder->update_memory_used_timeout); recorder->update_memory_used_timeout = 0; } if (recorder->cursor_image) cairo_surface_destroy (recorder->cursor_image); recorder_set_stage (recorder, NULL); recorder_set_pipeline (recorder, NULL); recorder_set_filename (recorder, NULL); cogl_handle_unref (recorder->recording_icon); G_OBJECT_CLASS (cinnamon_recorder_parent_class)->finalize (object); } static void recorder_on_stage_destroy (ClutterActor *actor, CinnamonRecorder *recorder) { recorder_set_stage (recorder, NULL); } /* Add together the memory used by all pipelines; both the * currently recording pipeline and pipelines finishing * recording asynchronously. */ static void recorder_update_memory_used (CinnamonRecorder *recorder, gboolean repaint) { guint memory_used = 0; GSList *l; for (l = recorder->pipelines; l; l = l->next) { RecorderPipeline *pipeline = l->data; guint pipeline_memory_used; g_object_get (pipeline->src, "memory-used", &pipeline_memory_used, NULL); memory_used += pipeline_memory_used; } if (memory_used != recorder->memory_used) { recorder->memory_used = memory_used; if (repaint) { /* In other cases we just queue a redraw even if we only need * to repaint and not redraw a frame, but having changes in * memory usage cause frames to be painted and memory used * seems like a bad idea. */ recorder->only_paint = TRUE; clutter_stage_ensure_redraw (recorder->stage); recorder->only_paint = FALSE; } } } /* Timeout used to avoid not drawing for more than MAXIMUM_PAUSE_TIME */ static gboolean recorder_redraw_timeout (gpointer data) { CinnamonRecorder *recorder = data; recorder->redraw_timeout = 0; clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage)); return FALSE; } static void recorder_add_redraw_timeout (CinnamonRecorder *recorder) { if (recorder->redraw_timeout == 0) { recorder->redraw_timeout = g_timeout_add (MAXIMUM_PAUSE_TIME, recorder_redraw_timeout, recorder); } } static void recorder_remove_redraw_timeout (CinnamonRecorder *recorder) { if (recorder->redraw_timeout != 0) { g_source_remove (recorder->redraw_timeout); recorder->redraw_timeout = 0; } } static void recorder_fetch_cursor_image (CinnamonRecorder *recorder) { XFixesCursorImage *cursor_image; guchar *data; int stride; int i, j; if (!recorder->have_xfixes) return; cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); if (!cursor_image) return; recorder->cursor_hot_x = cursor_image->xhot; recorder->cursor_hot_y = cursor_image->yhot; recorder->cursor_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height); /* The pixel data (in typical Xlib breakage) is longs even on * 64-bit platforms, so we have to data-convert there. For simplicity, * just do it always */ data = cairo_image_surface_get_data (recorder->cursor_image); stride = cairo_image_surface_get_stride (recorder->cursor_image); for (i = 0; i < cursor_image->height; i++) for (j = 0; j < cursor_image->width; j++) *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j]; cairo_surface_mark_dirty (recorder->cursor_image); XFree (cursor_image); } /* Overlay the cursor image on the frame. We draw the cursor image * into the host-memory buffer after we've captured the frame. An * alternate approach would be to turn off the cursor while recording * and draw the cursor ourselves with GL, but then we'd need to figure * out what the cursor looks like, or hard-code a non-system cursor. */ static void recorder_draw_cursor (CinnamonRecorder *recorder, GstBuffer *buffer) { GstMapInfo info; cairo_surface_t *surface; cairo_t *cr; /* We don't show a cursor unless the hot spot is in the frame; this * means that sometimes we aren't going to draw a cursor even when * there is a little bit overlapping within the stage */ if (recorder->pointer_x < 0 || recorder->pointer_y < 0 || recorder->pointer_x >= recorder->stage_width || recorder->pointer_y >= recorder->stage_height) return; if (!recorder->cursor_image) recorder_fetch_cursor_image (recorder); if (!recorder->cursor_image) return; gst_buffer_map (buffer, &info, GST_MAP_WRITE); surface = cairo_image_surface_create_for_data (info.data, CAIRO_FORMAT_ARGB32, recorder->stage_width, recorder->stage_height, recorder->stage_width * 4); cr = cairo_create (surface); cairo_set_source_surface (cr, recorder->cursor_image, recorder->pointer_x - recorder->cursor_hot_x, recorder->pointer_y - recorder->cursor_hot_y); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (surface); gst_buffer_unmap (buffer, &info); } /* Draw an overlay indicating how much of the target memory is used * for buffering frames. */ static void recorder_draw_buffer_meter (CinnamonRecorder *recorder) { int fill_level; recorder_update_memory_used (recorder, FALSE); /* As the buffer gets more full, we go from green, to yellow, to red */ if (recorder->memory_used > (recorder->memory_target * 3) / 4) cogl_set_source_color4f (1, 0, 0, 1); else if (recorder->memory_used > recorder->memory_target / 2) cogl_set_source_color4f (1, 1, 0, 1); else cogl_set_source_color4f (0, 1, 0, 1); fill_level = MIN (60, (recorder->memory_used * 60) / recorder->memory_target); /* A hollow rectangle filled from the left to fill_level */ cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 10, recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 9); cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 9, recorder->stage_width - (63 - fill_level), recorder->stage_height - recorder->height_adjust - 3); cogl_rectangle (recorder->stage_width - 3, recorder->stage_height - recorder->height_adjust - 9, recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 3); cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 3, recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 2); } /* We want to time-stamp each frame based on the actual time it was * recorded. We probably should use the pipeline clock rather than * gettimeofday(): that would be needed to get sync'ed audio correct. * I'm not immediately sure how to handle the adjustment we currently * do when pausing recording - is pausing the pipeline enough? */ static GstClockTime get_wall_time (void) { GTimeVal tv; g_get_current_time (&tv); return tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL; } /* Retrieve a frame and feed it into the pipeline */ static void recorder_record_frame (CinnamonRecorder *recorder) { GstBuffer *buffer; guint8 *data; guint size; size = recorder->stage_width * recorder->stage_height * 4; data = g_malloc (size); buffer = gst_buffer_new(); gst_buffer_insert_memory (buffer, -1, gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); GST_BUFFER_PTS(buffer) = get_wall_time() - recorder->start_time; cogl_read_pixels (0, 0, recorder->stage_width, recorder->stage_height, COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); recorder_draw_cursor (recorder, buffer); cinnamon_recorder_src_add_buffer (CINNAMON_RECORDER_SRC (recorder->current_pipeline->src), buffer); gst_buffer_unref (buffer); /* Reset the timeout that we used to avoid an overlong pause in the stream */ recorder_remove_redraw_timeout (recorder); recorder_add_redraw_timeout (recorder); } /* We hook in by recording each frame right after the stage is painted * by clutter before glSwapBuffers() makes it visible to the user. */ static void recorder_on_stage_paint (ClutterActor *actor, CinnamonRecorder *recorder) { if (recorder->state == RECORDER_STATE_RECORDING) { if (!recorder->only_paint) recorder_record_frame (recorder); cogl_set_source_texture (recorder->recording_icon); cogl_rectangle (recorder->stage_width - 32, recorder->stage_height - recorder->height_adjust - 42, recorder->stage_width, recorder->stage_height - recorder->height_adjust - 10); } if (recorder->state == RECORDER_STATE_RECORDING || recorder->memory_used != 0) recorder_draw_buffer_meter (recorder); } static void recorder_update_size (CinnamonRecorder *recorder) { ClutterActorBox allocation; clutter_actor_get_allocation_box (CLUTTER_ACTOR (recorder->stage), &allocation); recorder->stage_width = (int)(0.5 + allocation.x2 - allocation.x1); recorder->stage_height = (int)(0.5 + allocation.y2 - allocation.y1); } static void recorder_on_stage_notify_size (GObject *object, GParamSpec *pspec, CinnamonRecorder *recorder) { recorder_update_size (recorder); /* This breaks the recording but tweaking the GStreamer pipeline a bit * might make it work, at least if the codec can handle a stream where * the frame size changes in the middle. */ if (recorder->current_pipeline) recorder_pipeline_set_caps (recorder->current_pipeline); } static gboolean recorder_idle_redraw (gpointer data) { CinnamonRecorder *recorder = data; recorder->redraw_idle = 0; clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage)); return FALSE; } static void recorder_queue_redraw (CinnamonRecorder *recorder) { /* If we just queue a redraw on every mouse motion (for example), we * starve Clutter, which operates at a very low priority. So * we need to queue a "low priority redraw" after timeline updates */ if (recorder->state == RECORDER_STATE_RECORDING && recorder->redraw_idle == 0) recorder->redraw_idle = g_idle_add_full (CLUTTER_PRIORITY_REDRAW + 1, recorder_idle_redraw, recorder, NULL); } /* We use an event filter on the stage to get the XFixesCursorNotifyEvent * and also to track cursor position (when the cursor is over the stage's * input area); tracking cursor position here rather than with ClutterEvent * allows us to avoid worrying about event propagation and competing * signal handlers. */ static ClutterX11FilterReturn recorder_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data) { CinnamonRecorder *recorder = data; if (xev->xany.window != clutter_x11_get_stage_window (recorder->stage)) return CLUTTER_X11_FILTER_CONTINUE; if (xev->xany.type == recorder->xfixes_event_base + XFixesCursorNotify) { XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev; if (notify_event->subtype == XFixesDisplayCursorNotify) { if (recorder->cursor_image) { cairo_surface_destroy (recorder->cursor_image); recorder->cursor_image = NULL; } recorder_queue_redraw (recorder); } } else if (xev->xany.type == MotionNotify) { recorder->pointer_x = xev->xmotion.x; recorder->pointer_y = xev->xmotion.y; recorder_queue_redraw (recorder); } /* We want to track whether the pointer is over the stage * window itself, and not in a child window. A "virtual" * crossing is one that goes directly from ancestor to child. */ else if (xev->xany.type == EnterNotify && (xev->xcrossing.detail != NotifyVirtual && xev->xcrossing.detail != NotifyNonlinearVirtual)) { recorder->have_pointer = TRUE; recorder->pointer_x = xev->xcrossing.x; recorder->pointer_y = xev->xcrossing.y; recorder_queue_redraw (recorder); } else if (xev->xany.type == LeaveNotify && (xev->xcrossing.detail != NotifyVirtual && xev->xcrossing.detail != NotifyNonlinearVirtual)) { recorder->have_pointer = FALSE; recorder->pointer_x = xev->xcrossing.x; recorder->pointer_y = xev->xcrossing.y; recorder_queue_redraw (recorder); } return CLUTTER_X11_FILTER_CONTINUE; } /* We optimize out querying the server for the pointer position if the * pointer is in the input area of the ClutterStage. We track changes to * that with Enter/Leave events, but we need to 100% accurate about the * initial condition, which is a little involved. */ static void recorder_get_initial_cursor_position (CinnamonRecorder *recorder) { Display *xdisplay = clutter_x11_get_default_display (); Window xwindow = clutter_x11_get_stage_window (recorder->stage); XWindowAttributes xwa; Window root, child, parent; Window *children; guint n_children; int root_x,root_y; int window_x, window_y; guint mask; XGrabServer(xdisplay); XGetWindowAttributes (xdisplay, xwindow, &xwa); XQueryTree (xdisplay, xwindow, &root, &parent, &children, &n_children); XFree (children); if (xwa.map_state == IsViewable && XQueryPointer (xdisplay, parent, &root, &child, &root_x, &root_y, &window_x, &window_y, &mask) && child == xwindow) { /* The point of this call is not actually to translate the coordinates - * we could do that ourselves using xwa.{x,y} - but rather to see if * the pointer is in a child of the window, which we count as "not in * window", because we aren't guaranteed to get pointer events. */ XTranslateCoordinates(xdisplay, parent, xwindow, window_x, window_y, &window_x, &window_y, &child); if (child == None) { recorder->have_pointer = TRUE; recorder->pointer_x = window_x; recorder->pointer_y = window_y; } } else recorder->have_pointer = FALSE; XUngrabServer(xdisplay); XFlush(xdisplay); /* While we are at it, add mouse events to the event mask; they will * be there for the stage windows that Clutter creates by default, but * maybe this stage was created differently. Since we've already * retrieved the event mask, it's almost free. */ XSelectInput(xdisplay, xwindow, xwa.your_event_mask | EnterWindowMask | LeaveWindowMask | PointerMotionMask); } /* When the cursor is not over the stage's input area, we query for the * pointer position in a timeout. */ static void recorder_update_pointer (CinnamonRecorder *recorder) { Display *xdisplay = clutter_x11_get_default_display (); Window xwindow = clutter_x11_get_stage_window (recorder->stage); Window root, child; int root_x,root_y; int window_x, window_y; guint mask; if (recorder->have_pointer) return; if (XQueryPointer (xdisplay, xwindow, &root, &child, &root_x, &root_y, &window_x, &window_y, &mask)) { if (window_x != recorder->pointer_x || window_y != recorder->pointer_y) { recorder->pointer_x = window_x; recorder->pointer_y = window_y; recorder_queue_redraw (recorder); } } } static gboolean recorder_update_pointer_timeout (gpointer data) { recorder_update_pointer (data); return TRUE; } static void recorder_add_update_pointer_timeout (CinnamonRecorder *recorder) { if (!recorder->update_pointer_timeout) recorder->update_pointer_timeout = g_timeout_add (UPDATE_POINTER_TIME, recorder_update_pointer_timeout, recorder); } static void recorder_remove_update_pointer_timeout (CinnamonRecorder *recorder) { if (recorder->update_pointer_timeout) { g_source_remove (recorder->update_pointer_timeout); recorder->update_pointer_timeout = 0; } } static void recorder_set_stage (CinnamonRecorder *recorder, ClutterStage *stage) { if (recorder->stage == stage) return; if (recorder->current_pipeline) cinnamon_recorder_close (recorder); if (recorder->stage) { g_signal_handlers_disconnect_by_func (recorder->stage, (void *)recorder_on_stage_destroy, recorder); g_signal_handlers_disconnect_by_func (recorder->stage, (void *)recorder_on_stage_paint, recorder); g_signal_handlers_disconnect_by_func (recorder->stage, (void *)recorder_on_stage_notify_size, recorder); clutter_x11_remove_filter (recorder_event_filter, recorder); /* We don't don't deselect for cursor changes in case someone else just * happened to be selecting for cursor events on the same window; sending * us the events is close to free in any case. */ if (recorder->redraw_idle) { g_source_remove (recorder->redraw_idle); recorder->redraw_idle = 0; } } recorder->stage = stage; if (recorder->stage) { int error_base; recorder->stage = stage; g_signal_connect (recorder->stage, "destroy", G_CALLBACK (recorder_on_stage_destroy), recorder); g_signal_connect_after (recorder->stage, "paint", G_CALLBACK (recorder_on_stage_paint), recorder); g_signal_connect (recorder->stage, "notify::width", G_CALLBACK (recorder_on_stage_notify_size), recorder); g_signal_connect (recorder->stage, "notify::width", G_CALLBACK (recorder_on_stage_notify_size), recorder); clutter_x11_add_filter (recorder_event_filter, recorder); recorder_update_size (recorder); recorder->have_xfixes = XFixesQueryExtension (clutter_x11_get_default_display (), &recorder->xfixes_event_base, &error_base); if (recorder->have_xfixes) XFixesSelectCursorInput (clutter_x11_get_default_display (), clutter_x11_get_stage_window (stage), XFixesDisplayCursorNotifyMask); clutter_stage_ensure_current (stage); recorder_get_initial_cursor_position (recorder); } } static void recorder_set_framerate (CinnamonRecorder *recorder, int framerate) { if (framerate == recorder->framerate) return; if (recorder->current_pipeline) cinnamon_recorder_close (recorder); recorder->framerate = framerate; g_object_notify (G_OBJECT (recorder), "framerate"); } static void recorder_set_pipeline (CinnamonRecorder *recorder, const char *pipeline) { if (pipeline == recorder->pipeline_description || (pipeline && recorder->pipeline_description && strcmp (recorder->pipeline_description, pipeline) == 0)) return; if (recorder->current_pipeline) cinnamon_recorder_close (recorder); if (recorder->pipeline_description) g_free (recorder->pipeline_description); recorder->pipeline_description = g_strdup (pipeline); g_object_notify (G_OBJECT (recorder), "pipeline"); } static void recorder_set_filename (CinnamonRecorder *recorder, const char *filename) { if (filename == recorder->filename || (filename && recorder->filename && strcmp (recorder->filename, filename) == 0)) return; if (recorder->current_pipeline) cinnamon_recorder_close (recorder); if (recorder->filename) g_free (recorder->filename); recorder->filename = g_strdup (filename); g_object_notify (G_OBJECT (recorder), "filename"); } static void cinnamon_recorder_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonRecorder *recorder = CINNAMON_RECORDER (object); switch (prop_id) { case PROP_STAGE: recorder_set_stage (recorder, g_value_get_object (value)); break; case PROP_FRAMERATE: recorder_set_framerate (recorder, g_value_get_int (value)); break; case PROP_PIPELINE: recorder_set_pipeline (recorder, g_value_get_string (value)); break; case PROP_FILENAME: recorder_set_filename (recorder, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_recorder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonRecorder *recorder = CINNAMON_RECORDER (object); switch (prop_id) { case PROP_STAGE: g_value_set_object (value, G_OBJECT (recorder->stage)); break; case PROP_FRAMERATE: g_value_set_int (value, recorder->framerate); break; case PROP_PIPELINE: g_value_set_string (value, recorder->pipeline_description); break; case PROP_FILENAME: g_value_set_string (value, recorder->filename); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_recorder_class_init (CinnamonRecorderClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = cinnamon_recorder_finalize; gobject_class->get_property = cinnamon_recorder_get_property; gobject_class->set_property = cinnamon_recorder_set_property; g_object_class_install_property (gobject_class, PROP_STAGE, g_param_spec_object ("stage", "Stage", "Stage to record", CLUTTER_TYPE_STAGE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FRAMERATE, g_param_spec_int ("framerate", "Framerate", "Framerate used for resulting video in frames-per-second", 0, G_MAXINT, DEFAULT_FRAMES_PER_SECOND, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_PIPELINE, g_param_spec_string ("pipeline", "Pipeline", "GStreamer pipeline description to encode recordings", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILENAME, g_param_spec_string ("filename", "Filename", "The filename template to use for output files", NULL, G_PARAM_READWRITE)); } /* Sets the GstCaps (video format, in this case) on the stream */ static void recorder_pipeline_set_caps (RecorderPipeline *pipeline) { GstCaps *caps; /* The data is always native-endian xRGB; videoconvert * doesn't support little-endian xRGB, but does support * big-endian BGRx. */ caps = gst_caps_new_simple ("video/x-raw", #if G_BYTE_ORDER == G_LITTLE_ENDIAN "format", G_TYPE_STRING, "BGRx", #else "format", G_TYPE_STRING, "xRGB", #endif "bpp", G_TYPE_INT, 32, "depth", G_TYPE_INT, 24, "framerate", GST_TYPE_FRACTION, pipeline->recorder->framerate, 1, "width", G_TYPE_INT, pipeline->recorder->stage_width, "height", G_TYPE_INT, pipeline->recorder->stage_height, NULL); g_object_set (pipeline->src, "caps", caps, NULL); gst_caps_unref (caps); } /* Augments the supplied pipeline with the source elements: the actual * CinnamonRecorderSrc element where we inject frames then additional elements * to convert the output into something palatable. */ static gboolean recorder_pipeline_add_source (RecorderPipeline *pipeline) { GstPad *sink_pad = NULL, *src_pad = NULL; gboolean result = FALSE; GstElement *videoconvert; sink_pad = gst_bin_find_unlinked_pad (GST_BIN (pipeline->pipeline), GST_PAD_SINK); if (sink_pad == NULL) { g_warning("CinnamonRecorder: pipeline has no unlinked sink pad"); goto out; } pipeline->src = gst_element_factory_make ("cinnamonrecordersrc", NULL); if (pipeline->src == NULL) { g_warning ("Can't create recorder source element"); goto out; } gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->src); recorder_pipeline_set_caps (pipeline); /* The videoconvert element is a generic converter; it will convert * our supplied fixed format data into whatever the encoder wants */ videoconvert = gst_element_factory_make ("videoconvert", NULL); if (!videoconvert) { g_warning("Can't create videoconvert element"); goto out; } gst_bin_add (GST_BIN (pipeline->pipeline), videoconvert); gst_element_link_many (pipeline->src, videoconvert, NULL); src_pad = gst_element_get_static_pad (videoconvert, "src"); if (!src_pad) { g_warning("CinnamonRecorder: can't get src pad to link into pipeline"); goto out; } if (gst_pad_link (src_pad, sink_pad) != GST_PAD_LINK_OK) { g_warning("CinnamonRecorder: can't link to sink pad"); goto out; } result = TRUE; out: if (sink_pad) gst_object_unref (sink_pad); if (src_pad) gst_object_unref (src_pad); return result; } /* Counts '', 'a', ..., 'z', 'aa', ..., 'az', 'ba', ... */ static void increment_unique (GString *unique) { int i; for (i = unique->len - 1; i >= 0; i--) { if (unique->str[i] != 'z') { unique->str[i]++; return; } else unique->str[i] = 'a'; } g_string_prepend_c (unique, 'a'); } static char * get_absolute_path (char *maybe_relative) { char *path; if (g_path_is_absolute (maybe_relative)) path = g_strdup (maybe_relative); else { char *cwd = g_get_current_dir (); path = g_build_filename (cwd, maybe_relative, NULL); g_free (cwd); } return path; } /* Open a file for writing. Opening the file ourselves and using fdsink has * the advantage over filesink of being able to use O_EXCL when we want to * avoid overwriting* an existing file. Returns -1 if the file couldn't * be opened. */ static int recorder_open_outfile (CinnamonRecorder *recorder) { GString *unique = g_string_new (NULL); /* add to filename to make it unique */ const char *pattern; int flags; int outfile = -1; recorder->count++; pattern = recorder->filename; if (!pattern) pattern = DEFAULT_FILENAME; while (TRUE) { GString *filename = g_string_new (NULL); const char *p; for (p = pattern; *p; p++) { if (*p == '%') { switch (*(p + 1)) { case '%': case '\0': g_string_append_c (filename, '%'); break; case 'c': { /* Count distinguishing multiple files created in session */ g_string_append_printf (filename, "%d", recorder->count); recorder->filename_has_count = TRUE; } break; case 'd': { /* Appends date as YYYYMMDD */ GDate date; GTimeVal now; g_get_current_time (&now); g_date_clear (&date, 1); g_date_set_time_val (&date, &now); g_string_append_printf (filename, "%04d%02d%02d", g_date_get_year (&date), g_date_get_month (&date), g_date_get_day (&date)); } break; case 'u': if (recorder->unique) g_string_append (filename, recorder->unique); else g_string_append (filename, unique->str); break; default: g_warning ("Unknown escape %%%c in filename", *p); goto out; } p++; } else g_string_append_c (filename, *p); } /* If a filename is explicitly specified without %u then we assume the user * is fine with over-writing the old contents; putting %u in the default * should avoid problems with malicious symlinks. */ flags = O_WRONLY | O_CREAT | O_TRUNC; if (recorder->filename_has_count) flags |= O_EXCL; outfile = open (filename->str, flags, 0666); if (outfile != -1) { char *path = get_absolute_path (filename->str); g_printerr ("Recording to %s\n", path); g_free (path); g_string_free (filename, TRUE); goto out; } if (outfile == -1 && (errno != EEXIST || !recorder->filename_has_count)) { g_warning ("Cannot open output file '%s': %s", filename->str, g_strerror (errno)); g_string_free (filename, TRUE); goto out; } if (recorder->unique) { /* We've already picked a unique string based on count=1, and now we had a collision * for a subsequent count. */ g_warning ("Name collision with existing file for '%s'", filename->str); g_string_free (filename, TRUE); goto out; } g_string_free (filename, TRUE); increment_unique (unique); } out: if (outfile != -1) recorder->unique = g_string_free (unique, FALSE); else g_string_free (unique, TRUE); return outfile; } /* Augments the supplied pipeline with a sink element to write to the output * file, if necessary. */ static gboolean recorder_pipeline_add_sink (RecorderPipeline *pipeline) { GstPad *sink_pad = NULL, *src_pad = NULL; GstElement *fdsink; gboolean result = FALSE; src_pad = gst_bin_find_unlinked_pad (GST_BIN (pipeline->pipeline), GST_PAD_SRC); if (src_pad == NULL) { /* Nothing to do - assume that we were given a complete pipeline */ return TRUE; } pipeline->outfile = recorder_open_outfile (pipeline->recorder); if (pipeline->outfile == -1) goto out; fdsink = gst_element_factory_make ("fdsink", NULL); if (fdsink == NULL) { g_warning("Can't create fdsink element"); goto out; } gst_bin_add (GST_BIN (pipeline->pipeline), fdsink); g_object_set (fdsink, "fd", pipeline->outfile, NULL); sink_pad = gst_element_get_static_pad (fdsink, "sink"); if (!sink_pad) { g_warning("CinnamonRecorder: can't get sink pad to link pipeline output"); goto out; } if (gst_pad_link (src_pad, sink_pad) != GST_PAD_LINK_OK) { g_warning("CinnamonRecorder: can't link to sink pad"); goto out; } result = TRUE; out: if (src_pad) gst_object_unref (src_pad); if (sink_pad) gst_object_unref (sink_pad); return result; } static gboolean recorder_update_memory_used_timeout (gpointer data) { CinnamonRecorder *recorder = data; recorder->update_memory_used_timeout = 0; recorder_update_memory_used (recorder, TRUE); return FALSE; } /* We throttle down the frequency which we recompute memory usage * and draw the buffer indicator to avoid cutting into performance. */ static void recorder_pipeline_on_memory_used_changed (CinnamonRecorderSrc *src, GParamSpec *spec, RecorderPipeline *pipeline) { CinnamonRecorder *recorder = pipeline->recorder; if (!recorder) return; if (recorder->update_memory_used_timeout == 0) recorder->update_memory_used_timeout = g_timeout_add (UPDATE_MEMORY_USED_DELAY, recorder_update_memory_used_timeout, recorder); } static void recorder_pipeline_free (RecorderPipeline *pipeline) { if (pipeline->pipeline != NULL) gst_object_unref (pipeline->pipeline); if (pipeline->outfile != -1) close (pipeline->outfile); g_free (pipeline); } /* Function gets called on pipeline-global events; we use it to * know when the pipeline is finished. */ static gboolean recorder_pipeline_bus_watch (GstBus *bus, GstMessage *message, gpointer data) { RecorderPipeline *pipeline = data; switch (message->type) { case GST_MESSAGE_EOS: recorder_pipeline_closed (pipeline); return FALSE; /* remove watch */ case GST_MESSAGE_ERROR: { GError *error; gst_message_parse_error (message, &error, NULL); g_warning ("Error in recording pipeline: %s\n", error->message); g_error_free (error); recorder_pipeline_closed (pipeline); return FALSE; /* remove watch */ } default: break; } /* Leave the watch in place */ return TRUE; } /* Clean up when the pipeline is finished */ static void recorder_pipeline_closed (RecorderPipeline *pipeline) { g_signal_handlers_disconnect_by_func (pipeline->src, (gpointer) recorder_pipeline_on_memory_used_changed, pipeline); gst_element_set_state (pipeline->pipeline, GST_STATE_NULL); if (pipeline->recorder) { CinnamonRecorder *recorder = pipeline->recorder; if (pipeline == recorder->current_pipeline) { /* Error case; force a close */ recorder->current_pipeline = NULL; cinnamon_recorder_close (recorder); } recorder->pipelines = g_slist_remove (recorder->pipelines, pipeline); } recorder_pipeline_free (pipeline); } /* * Replaces '%T' in the passed pipeline with the thread count, * the maximum possible value is 64 (limit of what vp8enc supports) * * It is assumes that %T occurs only once. */ static char* substitute_thread_count (const char *pipeline) { char *tmp; int n_threads; GString *result; tmp = strstr (pipeline, "%T"); if (!tmp) return g_strdup (pipeline); #ifdef _SC_NPROCESSORS_ONLN { int n_processors = sysconf (_SC_NPROCESSORS_ONLN); /* includes hyper-threading */ n_threads = MIN (MAX (1, n_processors - 1), 64); } #else n_threads = 3; #endif result = g_string_new (NULL); g_string_append_len (result, pipeline, tmp - pipeline); g_string_append_printf (result, "%d", n_threads); g_string_append (result, tmp + 2); return g_string_free (result, FALSE);; } static gboolean recorder_open_pipeline (CinnamonRecorder *recorder) { RecorderPipeline *pipeline; const char *pipeline_description; char *parsed_pipeline; GError *error = NULL; GstBus *bus; pipeline = g_new0(RecorderPipeline, 1); pipeline->recorder = recorder; pipeline->outfile = - 1; pipeline_description = recorder->pipeline_description; if (!pipeline_description) pipeline_description = DEFAULT_PIPELINE; parsed_pipeline = substitute_thread_count (pipeline_description); pipeline->pipeline = gst_parse_launch_full (parsed_pipeline, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &error); g_free (parsed_pipeline); if (pipeline->pipeline == NULL) { g_warning ("CinnamonRecorder: failed to parse pipeline: %s", error->message); g_error_free (error); goto error; } if (!recorder_pipeline_add_source (pipeline)) goto error; if (!recorder_pipeline_add_sink (pipeline)) goto error; gst_element_set_state (pipeline->pipeline, GST_STATE_PLAYING); bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline->pipeline)); gst_bus_add_watch (bus, recorder_pipeline_bus_watch, pipeline); gst_object_unref (bus); g_signal_connect (pipeline->src, "notify::memory-used", G_CALLBACK (recorder_pipeline_on_memory_used_changed), pipeline); recorder->current_pipeline = pipeline; recorder->pipelines = g_slist_prepend (recorder->pipelines, pipeline); return TRUE; error: recorder_pipeline_free (pipeline); return FALSE; } static void recorder_close_pipeline (CinnamonRecorder *recorder) { if (recorder->current_pipeline != NULL) { /* This will send an EOS (end-of-stream) message after the last frame * is written. The bus watch for the pipeline will get it and do * final cleanup */ cinnamon_recorder_src_close (CINNAMON_RECORDER_SRC (recorder->current_pipeline->src)); recorder->current_pipeline = NULL; recorder->filename_has_count = FALSE; } } /** * cinnamon_recorder_new: * @stage: The #ClutterStage * * Create a new #CinnamonRecorder to record movies of a #ClutterStage * * Return value: The newly created #CinnamonRecorder object */ CinnamonRecorder * cinnamon_recorder_new (ClutterStage *stage) { return g_object_new (CINNAMON_TYPE_RECORDER, "stage", stage, NULL); } /** * cinnamon_recorder_set_framerate: * @recorder: the #CinnamonRecorder * @framerate: Framerate used for resulting video in frames-per-second. * * Sets the number of frames per second we configure for the GStreamer pipeline. * * The default value is 15. */ void cinnamon_recorder_set_framerate (CinnamonRecorder *recorder, int framerate) { g_return_if_fail (CINNAMON_IS_RECORDER (recorder)); recorder_set_framerate (recorder, framerate); } /** * cinnamon_recorder_set_filename: * @recorder: the #CinnamonRecorder * @filename: the filename template to use for output files, * or %NULL for the defalt value. * * Sets the filename that will be used when creating output * files. This is only used if the configured pipeline has an * unconnected source pad (as the default pipeline does). If * the pipeline is complete, then the filename is unused. The * provided string is used as a template.It can contain * the following escapes: * * %d: The current date as YYYYYMMDD * %u: A string added to make the filename unique. * '', 'a', 'b', ... 'aa', 'ab', .. * %c: A counter that is updated (opening a new file) each * time the recording stream is paused. * %%: A literal percent * * The default value is 'cinnamon-%d%u-%c.ogg'. */ void cinnamon_recorder_set_filename (CinnamonRecorder *recorder, const char *filename) { g_return_if_fail (CINNAMON_IS_RECORDER (recorder)); recorder_set_filename (recorder, filename); } /** * cinnamon_recorder_set_pipeline: * @recorder: the #CinnamonRecorder * @pipeline: (allow-none): the GStreamer pipeline used to encode recordings * or %NULL for the default value. * * Sets the GStreamer pipeline used to encode recordings. * It follows the syntax used for gst-launch. The pipeline * should have an unconnected sink pad where the recorded * video is recorded. It will normally have a unconnected * source pad; output from that pad will be written into the * output file. (See cinnamon_recorder_set_filename().) However * the pipeline can also take care of its own output - this * might be used to send the output to an icecast server * via shout2send or similar. * * The default value is 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' */ void cinnamon_recorder_set_pipeline (CinnamonRecorder *recorder, const char *pipeline) { g_return_if_fail (CINNAMON_IS_RECORDER (recorder)); recorder_set_pipeline (recorder, pipeline); } /** * cinnamon_recorder_record: * @recorder: the #CinnamonRecorder * * Starts recording, or continues a recording that was previously * paused. Starting the recording may fail if the output file * cannot be opened, or if the output stream cannot be created * for other reasons. In that case a warning is printed to * stderr. There is no way currently to get details on how * recording failed to start. * * An extra reference count is added to the recorder if recording * is succesfully started; the recording object will not be freed * until recording is stopped even if the creator no longer holds * a reference. Recording is automatically stopped if the stage * is destroyed. * * Return value: %TRUE if recording was succesfully started */ gboolean cinnamon_recorder_record (CinnamonRecorder *recorder) { g_return_val_if_fail (CINNAMON_IS_RECORDER (recorder), FALSE); g_return_val_if_fail (recorder->stage != NULL, FALSE); g_return_val_if_fail (recorder->state != RECORDER_STATE_RECORDING, FALSE); if (recorder->current_pipeline) { /* Adjust the start time so that the times in the stream ignore the * pause */ recorder->start_time = recorder->start_time + (get_wall_time() - recorder->pause_time); } else { if (!recorder_open_pipeline (recorder)) return FALSE; recorder->start_time = get_wall_time(); } recorder->state = RECORDER_STATE_RECORDING; recorder_add_update_pointer_timeout (recorder); /* Set up repaint hook */ recorder->repaint_hook_id = clutter_threads_add_repaint_func(recorder_repaint_hook, recorder->stage, NULL); /* Record an initial frame and also redraw with the indicator */ clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage)); /* We keep a ref while recording to let a caller start a recording then * drop their reference to the recorder */ g_object_ref (recorder); return TRUE; } /** * cinnamon_recorder_pause: * @recorder: the #CinnamonRecorder * * Temporarily stop recording. If the specified filename includes * the %c escape, then the stream is closed and a new stream with * an incremented counter will be created. Otherwise the stream * is paused and will be continued when cinnamon_recorder_record() * is next called. */ void cinnamon_recorder_pause (CinnamonRecorder *recorder) { g_return_if_fail (CINNAMON_IS_RECORDER (recorder)); g_return_if_fail (recorder->state == RECORDER_STATE_RECORDING); recorder_remove_update_pointer_timeout (recorder); /* We want to record one more frame since some time may have * elapsed since the last frame */ clutter_actor_paint (CLUTTER_ACTOR (recorder->stage)); if (recorder->filename_has_count) recorder_close_pipeline (recorder); recorder->state = RECORDER_STATE_PAUSED; recorder->pause_time = get_wall_time(); /* Queue a redraw to remove the recording indicator */ clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage)); if (recorder->repaint_hook_id != 0) { clutter_threads_remove_repaint_func (recorder->repaint_hook_id); recorder->repaint_hook_id = 0; } } /** * cinnamon_recorder_close: * @recorder: the #CinnamonRecorder * * Stops recording. It's possible to call cinnamon_recorder_record() * again to reopen a new recording stream, but unless change the * recording filename, this may result in the old recording being * overwritten. */ void cinnamon_recorder_close (CinnamonRecorder *recorder) { g_return_if_fail (CINNAMON_IS_RECORDER (recorder)); g_return_if_fail (recorder->state != RECORDER_STATE_CLOSED); if (recorder->state == RECORDER_STATE_RECORDING) cinnamon_recorder_pause (recorder); recorder_remove_update_pointer_timeout (recorder); recorder_remove_redraw_timeout (recorder); recorder_close_pipeline (recorder); recorder->state = RECORDER_STATE_CLOSED; recorder->count = 0; g_free (recorder->unique); recorder->unique = NULL; /* Release the refcount we took when we started recording */ g_object_unref (recorder); } /** * cinnamon_recorder_is_recording: * * Determine if recording is currently in progress. (The recorder * is not paused or closed.) * * Return value: %TRUE if the recorder is currently recording. */ gboolean cinnamon_recorder_is_recording (CinnamonRecorder *recorder) { g_return_val_if_fail (CINNAMON_IS_RECORDER (recorder), FALSE); return recorder->state == RECORDER_STATE_RECORDING; } Cinnamon-2.8.6/src/Makefile-hotplug-sniffer.am0000664000175000017500000000121712625666430020165 0ustar fabiofabioservice_in_files += hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in pkglibexec_PROGRAMS += cinnamon-hotplug-sniffer cinnamon_hotplug_sniffer_SOURCES = \ hotplug-sniffer/hotplug-mimetypes.h \ hotplug-sniffer/cinnamon-mime-sniffer.h \ hotplug-sniffer/cinnamon-mime-sniffer.c \ hotplug-sniffer/hotplug-sniffer.c \ $(NULL) cinnamon_hotplug_sniffer_CFLAGS = \ -I$(top_srcdir)/src \ -DG_DISABLE_DEPRECATED \ $(CINNAMON_HOTPLUG_SNIFFER_CFLAGS) \ $(NULL) cinnamon_hotplug_sniffer_LDADD = \ $(CINNAMON_HOTPLUG_SNIFFER_LIBS) \ $(NULL) EXTRA_DIST += \ hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in \ $(NULL) Cinnamon-2.8.6/src/cinnamon-xfixes-cursor.c0000664000175000017500000003042012625666430017602 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include #include #include "cinnamon-xfixes-cursor.h" #include "st.h" #include #include /** * SECTION:CinnamonXFixesCursor * short_description: Capture/manipulate system mouse cursor. * * The #CinnamonXFixesCursor object uses the XFixes extension to show/hide the * the system mouse pointer, to grab its image as it changes, and emit a * notification when its image changes. */ struct _CinnamonXFixesCursorClass { GObjectClass parent_class; }; struct _CinnamonXFixesCursor { GObject parent; ClutterStage *stage; gboolean have_xfixes; int xfixes_event_base; gboolean is_showing; CoglHandle *cursor_sprite; int cursor_hot_x; int cursor_hot_y; }; static void xfixes_cursor_show (CinnamonXFixesCursor *xfixes_cursor); static void xfixes_cursor_hide (CinnamonXFixesCursor *xfixes_cursor); static void xfixes_cursor_set_stage (CinnamonXFixesCursor *xfixes_cursor, ClutterStage *stage); static void xfixes_cursor_reset_image (CinnamonXFixesCursor *xfixes_cursor); enum { PROP_0, PROP_STAGE, }; G_DEFINE_TYPE(CinnamonXFixesCursor, cinnamon_xfixes_cursor, G_TYPE_OBJECT); enum { CURSOR_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void cinnamon_xfixes_cursor_init (CinnamonXFixesCursor *xfixes_cursor) { // (JS) Best (?) that can be assumed since XFixes doesn't provide a way of // detecting if the system mouse cursor is showing or not. xfixes_cursor->is_showing = TRUE; } static void cinnamon_xfixes_cursor_finalize (GObject *object) { CinnamonXFixesCursor *xfixes_cursor = CINNAMON_XFIXES_CURSOR (object); // Make sure the system cursor is showing before leaving the stage. xfixes_cursor_show (xfixes_cursor); xfixes_cursor_set_stage (xfixes_cursor, NULL); if (xfixes_cursor->cursor_sprite != NULL) cogl_handle_unref (xfixes_cursor->cursor_sprite); G_OBJECT_CLASS (cinnamon_xfixes_cursor_parent_class)->finalize (object); } static void xfixes_cursor_on_stage_destroy (ClutterActor *actor, CinnamonXFixesCursor *xfixes_cursor) { xfixes_cursor_set_stage (xfixes_cursor, NULL); } static ClutterX11FilterReturn xfixes_cursor_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data) { CinnamonXFixesCursor *xfixes_cursor = data; if (xev->xany.window != clutter_x11_get_stage_window (xfixes_cursor->stage)) return CLUTTER_X11_FILTER_CONTINUE; if (xev->xany.type == xfixes_cursor->xfixes_event_base + XFixesCursorNotify) { XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev; if (notify_event->subtype == XFixesDisplayCursorNotify) xfixes_cursor_reset_image (xfixes_cursor); } return CLUTTER_X11_FILTER_CONTINUE; } static void xfixes_cursor_set_stage (CinnamonXFixesCursor *xfixes_cursor, ClutterStage *stage) { if (xfixes_cursor->stage == stage) return; if (xfixes_cursor->stage) { g_signal_handlers_disconnect_by_func (xfixes_cursor->stage, (void *)xfixes_cursor_on_stage_destroy, xfixes_cursor); clutter_x11_remove_filter (xfixes_cursor_event_filter, xfixes_cursor); } xfixes_cursor->stage = stage; if (xfixes_cursor->stage) { int error_base; xfixes_cursor->stage = stage; g_signal_connect (xfixes_cursor->stage, "destroy", G_CALLBACK (xfixes_cursor_on_stage_destroy), xfixes_cursor); clutter_x11_add_filter (xfixes_cursor_event_filter, xfixes_cursor); xfixes_cursor->have_xfixes = XFixesQueryExtension (clutter_x11_get_default_display (), &xfixes_cursor->xfixes_event_base, &error_base); if (xfixes_cursor->have_xfixes) XFixesSelectCursorInput (clutter_x11_get_default_display (), clutter_x11_get_stage_window (stage), XFixesDisplayCursorNotifyMask); xfixes_cursor_reset_image (xfixes_cursor); } } static void xfixes_cursor_show (CinnamonXFixesCursor *xfixes_cursor) { int minor, major; Display *xdisplay; Window xwindow; if (xfixes_cursor->is_showing == TRUE) return; if (!xfixes_cursor->have_xfixes || !xfixes_cursor->stage) return; xdisplay = clutter_x11_get_default_display (); xwindow = clutter_x11_get_stage_window (xfixes_cursor->stage); XFixesQueryVersion (xdisplay, &major, &minor); if (major >= 4) { XFixesShowCursor (xdisplay, xwindow); xfixes_cursor->is_showing = TRUE; } } static void xfixes_cursor_hide (CinnamonXFixesCursor *xfixes_cursor) { int minor, major; Display *xdisplay; Window xwindow; if (xfixes_cursor->is_showing == FALSE) return; if (!xfixes_cursor->have_xfixes || !xfixes_cursor->stage) return; xdisplay = clutter_x11_get_default_display (); xwindow = clutter_x11_get_stage_window (xfixes_cursor->stage); XFixesQueryVersion (xdisplay, &major, &minor); if (major >= 4) { XFixesHideCursor (xdisplay, xwindow); xfixes_cursor->is_showing = FALSE; } } static void xfixes_cursor_reset_image (CinnamonXFixesCursor *xfixes_cursor) { XFixesCursorImage *cursor_image; CoglHandle sprite = COGL_INVALID_HANDLE; guint8 *cursor_data; gboolean free_cursor_data; if (!xfixes_cursor->have_xfixes) return; cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); if (!cursor_image) return; /* Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit * quantities as arrays of long; we need to convert on 64 bit */ if (sizeof(long) == 4) { cursor_data = (guint8 *)cursor_image->pixels; free_cursor_data = FALSE; } else { int i, j; guint32 *cursor_words; gulong *p; guint32 *q; cursor_words = g_new (guint32, cursor_image->width * cursor_image->height); cursor_data = (guint8 *)cursor_words; p = cursor_image->pixels; q = cursor_words; for (j = 0; j < cursor_image->height; j++) for (i = 0; i < cursor_image->width; i++) *(q++) = *(p++); free_cursor_data = TRUE; } sprite = st_cogl_texture_new_from_data_wrapper (cursor_image->width, cursor_image->height, COGL_TEXTURE_NONE, CLUTTER_CAIRO_FORMAT_ARGB32, COGL_PIXEL_FORMAT_ANY, cursor_image->width * 4, cursor_data); if (free_cursor_data) g_free (cursor_data); if (sprite != COGL_INVALID_HANDLE) { if (xfixes_cursor->cursor_sprite != NULL) cogl_handle_unref (xfixes_cursor->cursor_sprite); xfixes_cursor->cursor_sprite = sprite; xfixes_cursor->cursor_hot_x = cursor_image->xhot; xfixes_cursor->cursor_hot_y = cursor_image->yhot; g_signal_emit (xfixes_cursor, signals[CURSOR_CHANGED], 0); } XFree (cursor_image); } static void cinnamon_xfixes_cursor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonXFixesCursor *xfixes_cursor = CINNAMON_XFIXES_CURSOR (object); switch (prop_id) { case PROP_STAGE: xfixes_cursor_set_stage (xfixes_cursor, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_xfixes_cursor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonXFixesCursor *xfixes_cursor = CINNAMON_XFIXES_CURSOR (object); switch (prop_id) { case PROP_STAGE: g_value_set_object (value, G_OBJECT (xfixes_cursor->stage)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_xfixes_cursor_class_init (CinnamonXFixesCursorClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = cinnamon_xfixes_cursor_finalize; signals[CURSOR_CHANGED] = g_signal_new ("cursor-change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gobject_class->get_property = cinnamon_xfixes_cursor_get_property; gobject_class->set_property = cinnamon_xfixes_cursor_set_property; g_object_class_install_property (gobject_class, PROP_STAGE, g_param_spec_object ("stage", "Stage", "Stage for mouse cursor", CLUTTER_TYPE_STAGE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } /** * cinnamon_xfixes_cursor_get_for_stage: * @stage: (transfer none): The #ClutterStage to get the cursor for * * Return value: (transfer none): A #CinnamonXFixesCursor instance */ CinnamonXFixesCursor * cinnamon_xfixes_cursor_get_for_stage (ClutterStage *stage) { CinnamonXFixesCursor *instance; static GQuark xfixes_cursor_quark; if (G_UNLIKELY (xfixes_cursor_quark == 0)) xfixes_cursor_quark = g_quark_from_static_string ("cinnamon-xfixes-cursor"); instance = g_object_get_qdata (G_OBJECT (stage), xfixes_cursor_quark); if (instance == NULL) { instance = g_object_new (CINNAMON_TYPE_XFIXES_CURSOR, "stage", stage, NULL); g_object_set_qdata (G_OBJECT (stage), xfixes_cursor_quark, instance); } return instance; } /** * cinnamon_xfixes_cursor_hide: * @xfixes_cursor: the #CinnamonXFixesCursor * * Hide the system mouse cursor. */ void cinnamon_xfixes_cursor_hide (CinnamonXFixesCursor *xfixes_cursor) { g_return_if_fail (CINNAMON_IS_XFIXES_CURSOR (xfixes_cursor)); xfixes_cursor_hide (xfixes_cursor); } /** * cinnamon_xfixes_cursor_show: * @xfixes_cursor: the #CinnamonXFixesCursor * * Show the system mouse cursor to show */ void cinnamon_xfixes_cursor_show (CinnamonXFixesCursor *xfixes_cursor) { g_return_if_fail (CINNAMON_IS_XFIXES_CURSOR (xfixes_cursor)); xfixes_cursor_show (xfixes_cursor); } /** * cinnamon_xfixes_cursor_update_texture_image: * @xfixes_cursor: the #CinnamonXFixesCursor * @texture: ClutterTexture to update with the current sprite image. */ void cinnamon_xfixes_cursor_update_texture_image (CinnamonXFixesCursor *xfixes_cursor, ClutterTexture *texture) { CoglHandle *old_sprite; g_return_if_fail (CINNAMON_IS_XFIXES_CURSOR (xfixes_cursor)); if (texture == NULL) return; old_sprite = clutter_texture_get_cogl_texture (texture); if (xfixes_cursor->cursor_sprite == old_sprite) return; clutter_texture_set_cogl_texture (texture, xfixes_cursor->cursor_sprite); } /** * cinnamon_xfixes_cursor_get_hot_x: * @xfixes_cursor: the #CinnamonXFixesCursor * * Returns: the current mouse cursor's hot x-coordinate. */ int cinnamon_xfixes_cursor_get_hot_x (CinnamonXFixesCursor *xfixes_cursor) { g_return_val_if_fail (CINNAMON_IS_XFIXES_CURSOR (xfixes_cursor), 0); return xfixes_cursor->cursor_hot_x; } /** * cinnamon_xfixes_cursor_get_hot_y: * @xfixes_cursor: the #CinnamonXFixesCursor * * Returns: the current mouse cursor's hot y-coordinate. */ int cinnamon_xfixes_cursor_get_hot_y (CinnamonXFixesCursor *xfixes_cursor) { g_return_val_if_fail (CINNAMON_IS_XFIXES_CURSOR (xfixes_cursor), 0); return xfixes_cursor->cursor_hot_y; } Cinnamon-2.8.6/src/cinnamon-perf-log.h0000664000175000017500000000730712625666430016511 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_PERF_LOG_H__ #define __CINNAMON_PERF_LOG_H__ #include #include G_BEGIN_DECLS typedef struct _CinnamonPerfLog CinnamonPerfLog; typedef struct _CinnamonPerfLogClass CinnamonPerfLogClass; #define CINNAMON_TYPE_PERF_LOG (cinnamon_perf_log_get_type ()) #define CINNAMON_PERF_LOG(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_PERF_LOG, CinnamonPerfLog)) #define CINNAMON_PERF_LOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_PERF_LOG, CinnamonPerfLogClass)) #define CINNAMON_IS_PERF_LOG(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_PERF_LOG)) #define CINNAMON_IS_PERF_LOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_PERF_LOG)) #define CINNAMON_PERF_LOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_PERF_LOG, CinnamonPerfLogClass)) GType cinnamon_perf_log_get_type (void) G_GNUC_CONST; CinnamonPerfLog *cinnamon_perf_log_get_default (void); void cinnamon_perf_log_set_enabled (CinnamonPerfLog *perf_log, gboolean enabled); void cinnamon_perf_log_define_event (CinnamonPerfLog *perf_log, const char *name, const char *description, const char *signature); void cinnamon_perf_log_event (CinnamonPerfLog *perf_log, const char *name); void cinnamon_perf_log_event_i (CinnamonPerfLog *perf_log, const char *name, gint32 arg); void cinnamon_perf_log_event_x (CinnamonPerfLog *perf_log, const char *name, gint64 arg); void cinnamon_perf_log_event_s (CinnamonPerfLog *perf_log, const char *name, const char *arg); void cinnamon_perf_log_define_statistic (CinnamonPerfLog *perf_log, const char *name, const char *description, const char *signature); void cinnamon_perf_log_update_statistic_i (CinnamonPerfLog *perf_log, const char *name, int value); void cinnamon_perf_log_update_statistic_x (CinnamonPerfLog *perf_log, const char *name, gint64 value); typedef void (*CinnamonPerfStatisticsCallback) (CinnamonPerfLog *perf_log, gpointer data); void cinnamon_perf_log_add_statistics_callback (CinnamonPerfLog *perf_log, CinnamonPerfStatisticsCallback callback, gpointer user_data, GDestroyNotify notify); void cinnamon_perf_log_collect_statistics (CinnamonPerfLog *perf_log); typedef void (*CinnamonPerfReplayFunction) (gint64 time, const char *name, const char *signature, GValue *arg, gpointer user_data); void cinnamon_perf_log_replay (CinnamonPerfLog *perf_log, CinnamonPerfReplayFunction replay_function, gpointer user_data); gboolean cinnamon_perf_log_dump_events (CinnamonPerfLog *perf_log, GOutputStream *out, GError **error); gboolean cinnamon_perf_log_dump_log (CinnamonPerfLog *perf_log, GOutputStream *out, GError **error); G_END_DECLS #endif /* __CINNAMON_PERF_LOG_H__ */ Cinnamon-2.8.6/src/cinnamon-doc-system.c0000664000175000017500000002713212625666430017056 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-doc-system.h" #include "cinnamon-global.h" /** * SECTION:cinnamon-doc-system * @short_description: Track recently used documents * * Wraps #GtkRecentManager, caching recently used document information, and adds * APIs for asynchronous queries. */ enum { CHANGED, DELETED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _CinnamonDocSystemPrivate { GtkRecentManager *manager; GHashTable *infos_by_uri; GSList *infos_by_timestamp; guint idle_recent_changed_id; GHashTable *deleted_infos; guint idle_emit_deleted_id; }; G_DEFINE_TYPE(CinnamonDocSystem, cinnamon_doc_system, G_TYPE_OBJECT); /** * cinnamon_doc_system_get_all: * @system: A #CinnamonDocSystem * * Returns the currently cached set of recent files. Recent files are read initially * from the underlying #GtkRecentManager, and updated when it changes. * This function does not perform I/O. * * Returns: (transfer none) (element-type GtkRecentInfo): Cached recent file infos */ GSList * cinnamon_doc_system_get_all (CinnamonDocSystem *self) { return self->priv->infos_by_timestamp; } /** * cinnamon_doc_system_lookup_by_uri: * @system: A #CinnamonDocSystem * @uri: Url * * Returns: (transfer none): Recent file info corresponding to given @uri */ GtkRecentInfo * cinnamon_doc_system_lookup_by_uri (CinnamonDocSystem *self, const char *uri) { return g_hash_table_lookup (self->priv->infos_by_uri, uri); } static gboolean cinnamon_doc_system_idle_emit_deleted (gpointer data) { CinnamonDocSystem *self = CINNAMON_DOC_SYSTEM (data); GHashTableIter iter; gpointer key, value; self->priv->idle_emit_deleted_id = 0; g_hash_table_iter_init (&iter, self->priv->deleted_infos); while (g_hash_table_iter_next (&iter, &key, &value)) { GtkRecentInfo *info = key; g_signal_emit (self, signals[DELETED], 0, info); } g_signal_emit (self, signals[CHANGED], 0); return FALSE; } typedef struct { CinnamonDocSystem *self; GtkRecentInfo *info; } CinnamonDocSystemRecentQueryData; static void on_recent_file_query_result (GObject *source, GAsyncResult *result, gpointer user_data) { CinnamonDocSystemRecentQueryData *data = user_data; CinnamonDocSystem *self = data->self; GError *error = NULL; GFileInfo *fileinfo; fileinfo = g_file_query_info_finish (G_FILE (source), result, &error); if (fileinfo) g_object_unref (fileinfo); /* This is a strict error check; we don't want to cause recent files to * vanish for anything potentially transient. */ if (error != NULL && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) { self->priv->infos_by_timestamp = g_slist_remove (self->priv->infos_by_timestamp, data->info); g_hash_table_remove (self->priv->infos_by_uri, gtk_recent_info_get_uri (data->info)); g_hash_table_insert (self->priv->deleted_infos, gtk_recent_info_ref (data->info), NULL); if (self->priv->idle_emit_deleted_id == 0) self->priv->idle_emit_deleted_id = g_timeout_add (0, cinnamon_doc_system_idle_emit_deleted, self); } g_clear_error (&error); gtk_recent_info_unref (data->info); g_free (data); } /** * cinnamon_doc_system_queue_existence_check: * @system: A #CinnamonDocSystem * @n_items: Count of items to check for existence, starting from most recent * * Asynchronously start a check of a number of recent file for existence; * any deleted files will be emitted from the #CinnamonDocSystem::deleted * signal. Note that this function ignores non-local files; they * will simply always appear to exist (until they are removed from * the recent file list manually). * * The intent of this function is to be called after a #CinnamonDocSystem::changed * signal has been emitted, and a display has shown a subset of those files. */ void cinnamon_doc_system_queue_existence_check (CinnamonDocSystem *self, guint n_items) { GSList *iter; guint i; for (i = 0, iter = self->priv->infos_by_timestamp; i < n_items && iter; i++, iter = iter->next) { GtkRecentInfo *info = iter->data; const char *uri; GFile *file; CinnamonDocSystemRecentQueryData *data; if (!gtk_recent_info_is_local (info)) continue; data = g_new0 (CinnamonDocSystemRecentQueryData, 1); data->self = self; data->info = gtk_recent_info_ref (info); uri = gtk_recent_info_get_uri (info); file = g_file_new_for_uri (uri); g_file_query_info_async (file, "standard::type", G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, NULL, on_recent_file_query_result, data); g_object_unref (file); } } static int sort_infos_by_timestamp_descending (gconstpointer a, gconstpointer b) { GtkRecentInfo *info_a = (GtkRecentInfo*)a; GtkRecentInfo *info_b = (GtkRecentInfo*)b; time_t modified_a, modified_b; modified_a = gtk_recent_info_get_modified (info_a); modified_b = gtk_recent_info_get_modified (info_b); return modified_b - modified_a; } static gboolean idle_handle_recent_changed (gpointer data) { CinnamonDocSystem *self = CINNAMON_DOC_SYSTEM (data); GList *items, *iter; self->priv->idle_recent_changed_id = 0; g_hash_table_remove_all (self->priv->deleted_infos); g_hash_table_remove_all (self->priv->infos_by_uri); g_slist_free (self->priv->infos_by_timestamp); self->priv->infos_by_timestamp = NULL; items = gtk_recent_manager_get_items (self->priv->manager); for (iter = items; iter; iter = iter->next) { GtkRecentInfo *info = iter->data; const char *uri = gtk_recent_info_get_uri (info); /* uri is owned by the info */ g_hash_table_insert (self->priv->infos_by_uri, (char*) uri, info); self->priv->infos_by_timestamp = g_slist_prepend (self->priv->infos_by_timestamp, info); } g_list_free (items); self->priv->infos_by_timestamp = g_slist_sort (self->priv->infos_by_timestamp, sort_infos_by_timestamp_descending); g_signal_emit (self, signals[CHANGED], 0); return FALSE; } static void cinnamon_doc_system_on_recent_changed (GtkRecentManager *manager, CinnamonDocSystem *self) { if (self->priv->idle_recent_changed_id != 0) return; self->priv->idle_recent_changed_id = g_timeout_add (0, idle_handle_recent_changed, self); } /** * cinnamon_doc_system_open: * @system: A #CinnamonDocSystem * @info: A #GtkRecentInfo * @workspace: Open on this workspace, or -1 for default * * Launch the default application associated with the mime type of * @info, using its uri. */ void cinnamon_doc_system_open (CinnamonDocSystem *system, GtkRecentInfo *info, int workspace) { GFile *file; GAppInfo *app_info; gboolean needs_uri; GAppLaunchContext *context; context = cinnamon_global_create_app_launch_context (cinnamon_global_get ()); if (workspace != -1) gdk_app_launch_context_set_desktop ((GdkAppLaunchContext *)context, workspace); file = g_file_new_for_uri (gtk_recent_info_get_uri (info)); needs_uri = g_file_get_path (file) == NULL; g_object_unref (file); app_info = g_app_info_get_default_for_type (gtk_recent_info_get_mime_type (info), needs_uri); if (app_info != NULL) { GList *uris; uris = g_list_prepend (NULL, (gpointer)gtk_recent_info_get_uri (info)); g_app_info_launch_uris (app_info, uris, context, NULL); g_list_free (uris); } else { char *app_name; const char *app_exec; char *app_exec_quoted; guint count; time_t time; app_name = gtk_recent_info_last_application (info); if (gtk_recent_info_get_application_info (info, app_name, &app_exec, &count, &time)) { GRegex *regex; /* TODO: Change this once better support for creating GAppInfo is added to GtkRecentInfo, as right now this relies on the fact that the file uri is already a part of appExec, so we don't supply any files to app_info.launch(). The 'command line' passed to create_from_command_line is allowed to contain '%' macros that are expanded to file name / icon name, etc, so we need to escape % as %% */ regex = g_regex_new ("%", 0, 0, NULL); app_exec_quoted = g_regex_replace (regex, app_exec, -1, 0, "%%", 0, NULL); g_regex_unref (regex); app_info = g_app_info_create_from_commandline (app_exec_quoted, NULL, 0, NULL); g_free (app_exec_quoted); /* The point of passing an app launch context to launch() is mostly to get startup notification and associated benefits like the app appearing on the right desktop; but it doesn't really work for now because with the way we create the appInfo we aren't reading the application's desktop file, and thus don't find the StartupNotify=true in it. So, despite passing the app launch context, no startup notification occurs. */ g_app_info_launch (app_info, NULL, context, NULL); } g_free (app_name); } g_object_unref (context); } static void cinnamon_doc_system_class_init(CinnamonDocSystemClass *klass) { GObjectClass *gobject_class = (GObjectClass *)klass; signals[CHANGED] = g_signal_new ("changed", CINNAMON_TYPE_DOC_SYSTEM, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[DELETED] = g_signal_new ("deleted", CINNAMON_TYPE_DOC_SYSTEM, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GTK_TYPE_RECENT_INFO); g_type_class_add_private (gobject_class, sizeof (CinnamonDocSystemPrivate)); } static void cinnamon_doc_system_init (CinnamonDocSystem *self) { CinnamonDocSystemPrivate *priv; self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CINNAMON_TYPE_DOC_SYSTEM, CinnamonDocSystemPrivate); self->priv->manager = gtk_recent_manager_get_default (); self->priv->deleted_infos = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)gtk_recent_info_unref, NULL); self->priv->infos_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)gtk_recent_info_unref); GList *items, *iter; self->priv->infos_by_timestamp = NULL; items = gtk_recent_manager_get_items (self->priv->manager); for (iter = items; iter; iter = iter->next) { GtkRecentInfo *info = iter->data; const char *uri = gtk_recent_info_get_uri (info); /* uri is owned by the info */ g_hash_table_insert (self->priv->infos_by_uri, (char*) uri, info); self->priv->infos_by_timestamp = g_slist_prepend (self->priv->infos_by_timestamp, info); } g_list_free (items); self->priv->infos_by_timestamp = g_slist_sort (self->priv->infos_by_timestamp, sort_infos_by_timestamp_descending); g_signal_connect (self->priv->manager, "changed", G_CALLBACK(cinnamon_doc_system_on_recent_changed), self); } /** * cinnamon_doc_system_get_default: * * Return Value: (transfer none): The global #CinnamonDocSystem singleton */ CinnamonDocSystem * cinnamon_doc_system_get_default () { static CinnamonDocSystem *instance = NULL; if (instance == NULL) instance = g_object_new (CINNAMON_TYPE_DOC_SYSTEM, NULL); return instance; } Cinnamon-2.8.6/src/cinnamon-app-private.h0000664000175000017500000000173112625666430017221 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_APP_PRIVATE_H__ #define __CINNAMON_APP_PRIVATE_H__ #include "cinnamon-app.h" #include "cinnamon-app-system.h" #define SN_API_NOT_YET_FROZEN 1 #include G_BEGIN_DECLS CinnamonApp* _cinnamon_app_new_for_window (MetaWindow *window); CinnamonApp* _cinnamon_app_new (GMenuTreeEntry *entry); void _cinnamon_app_set_entry (CinnamonApp *app, GMenuTreeEntry *entry); void _cinnamon_app_handle_startup_sequence (CinnamonApp *app, SnStartupSequence *sequence); void _cinnamon_app_add_window (CinnamonApp *app, MetaWindow *window); void _cinnamon_app_remove_window (CinnamonApp *app, MetaWindow *window); void _cinnamon_app_do_match (CinnamonApp *app, GSList *terms, GSList **prefix_results, GSList **substring_results); G_END_DECLS #endif /* __CINNAMON_APP_PRIVATE_H__ */ Cinnamon-2.8.6/src/cinnamon-js.cpp0000664000175000017500000000271212625666430015740 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-js.h" #include #include /* Memory report bits */ #ifdef HAVE_MALLINFO #include #endif /** * cinnamon_js_get_memory_info: (skip) * @ptr: pointer to GjsContext from global * @last_gc: The last garbage collection time (passed from global) * @meminfo: (out caller-allocates): Output location for memory information * * Load process-global data about memory usage. */ void cinnamon_js_get_memory_info (gpointer ptr, gint64 last_gc, CinnamonJSMemoryInfo *meminfo) { GjsContext *gjs_context = GJS_CONTEXT (ptr); JSContext *context; gint64 now; memset (meminfo, 0, sizeof (*meminfo)); #ifdef HAVE_MALLINFO { struct mallinfo info = mallinfo (); meminfo->glibc_uordblks = info.uordblks; } #endif context = (JSContext *) gjs_context_get_native_context (gjs_context); meminfo->js_bytes = JS_GetGCParameter (JS_GetRuntime (context), JSGC_BYTES); meminfo->gjs_boxed = (unsigned int) gjs_counter_boxed.value; meminfo->gjs_gobject = (unsigned int) gjs_counter_object.value; meminfo->gjs_function = (unsigned int) gjs_counter_function.value; meminfo->gjs_closure = (unsigned int) gjs_counter_closure.value; now = g_get_monotonic_time (); meminfo->last_gc_seconds_ago = (now - last_gc) / G_TIME_SPAN_SECOND; } Cinnamon-2.8.6/src/gvc/0000775000175000017500000000000012625666430013575 5ustar fabiofabioCinnamon-2.8.6/src/gvc/gvc-pulseaudio-fake.h0000664000175000017500000000217212625666430017603 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_PULSEAUDIO_FAKE_H #define __GVC_PULSEAUDIO_FAKE_H #ifdef WITH_INTROSPECTION #ifndef PA_API_VERSION #define pa_channel_position_t int #define pa_volume_t guint32 #define pa_context gpointer #endif /* PA_API_VERSION */ #endif /* WITH_INTROSPECTION */ #endif /* __GVC_PULSEAUDIO_FAKE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-source.c0000664000175000017500000001353712625666430017151 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-source.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourcePrivate)) struct GvcMixerSourcePrivate { gpointer dummy; }; static void gvc_mixer_source_class_init (GvcMixerSourceClass *klass); static void gvc_mixer_source_init (GvcMixerSource *mixer_source); static void gvc_mixer_source_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); /* set the volume */ cv = gvc_channel_map_get_cvolume (map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_volume_by_index (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_source_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_mute_by_index (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static gboolean gvc_mixer_source_change_port (GvcMixerStream *stream, const char *port) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_port_by_index (context, index, port, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_port_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_source_class_init (GvcMixerSourceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_source_finalize; stream_class->push_volume = gvc_mixer_source_push_volume; stream_class->change_is_muted = gvc_mixer_source_change_is_muted; stream_class->change_port = gvc_mixer_source_change_port; g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate)); } static void gvc_mixer_source_init (GvcMixerSource *source) { source->priv = GVC_MIXER_SOURCE_GET_PRIVATE (source); } static void gvc_mixer_source_finalize (GObject *object) { GvcMixerSource *mixer_source; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SOURCE (object)); mixer_source = GVC_MIXER_SOURCE (object); g_return_if_fail (mixer_source->priv != NULL); G_OBJECT_CLASS (gvc_mixer_source_parent_class)->finalize (object); } /** * gvc_mixer_source_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_source_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SOURCE, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-sink-input.h0000664000175000017500000000432612625666430017753 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_SINK_INPUT_H #define __GVC_MIXER_SINK_INPUT_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SINK_INPUT (gvc_mixer_sink_input_get_type ()) #define GVC_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInput)) #define GVC_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass)) #define GVC_IS_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK_INPUT)) #define GVC_IS_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK_INPUT)) #define GVC_MIXER_SINK_INPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass)) typedef struct GvcMixerSinkInputPrivate GvcMixerSinkInputPrivate; typedef struct { GvcMixerStream parent; GvcMixerSinkInputPrivate *priv; } GvcMixerSinkInput; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSinkInputClass; GType gvc_mixer_sink_input_get_type (void); GvcMixerStream * gvc_mixer_sink_input_new (pa_context *context, guint index, GvcChannelMap *map); G_END_DECLS #endif /* __GVC_MIXER_SINK_INPUT_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-stream.c0000664000175000017500000010506612625666430017143 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-stream.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamPrivate)) static guint32 stream_serial = 1; struct GvcMixerStreamPrivate { pa_context *pa_context; guint id; guint index; gint card_index; GvcChannelMap *channel_map; char *name; char *description; char *application_id; char *icon_name; char *sysfs_path; gboolean is_muted; gboolean can_decibel; gboolean is_event_stream; gboolean is_virtual; pa_volume_t base_volume; pa_operation *change_volume_op; char *port; char *human_port; GList *ports; }; enum { PROP_0, PROP_ID, PROP_PA_CONTEXT, PROP_CHANNEL_MAP, PROP_INDEX, PROP_NAME, PROP_DESCRIPTION, PROP_APPLICATION_ID, PROP_ICON_NAME, PROP_SYSFS_PATH, PROP_VOLUME, PROP_DECIBEL, PROP_IS_MUTED, PROP_CAN_DECIBEL, PROP_IS_EVENT_STREAM, PROP_IS_VIRTUAL, PROP_CARD_INDEX, PROP_PORT, }; static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass); static void gvc_mixer_stream_init (GvcMixerStream *mixer_stream); static void gvc_mixer_stream_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (GvcMixerStream, gvc_mixer_stream, G_TYPE_OBJECT) static guint32 get_next_stream_serial (void) { guint32 serial; serial = stream_serial++; if ((gint32)stream_serial < 0) { stream_serial = 1; } return serial; } pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->pa_context; } guint gvc_mixer_stream_get_index (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->index; } guint gvc_mixer_stream_get_id (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->id; } const GvcChannelMap * gvc_mixer_stream_get_channel_map (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->channel_map; } /** * gvc_mixer_stream_get_volume: * @stream: * * Returns: (type guint32) (transfer none): */ pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]; } gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return pa_sw_volume_to_dB( (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]); } /** * gvc_mixer_stream_set_volume: * @stream: * @volume: (type guint32): * * Returns: */ gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream, pa_volume_t volume) { pa_cvolume cv; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map); pa_cvolume_scale(&cv, volume); if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) { gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE); g_object_notify (G_OBJECT (stream), "volume"); return TRUE; } return FALSE; } gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream, gdouble db) { pa_cvolume cv; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map); pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db)); if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) { gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE); g_object_notify (G_OBJECT (stream), "volume"); } return TRUE; } gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_muted; } gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->can_decibel; } gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream, gboolean is_muted) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (is_muted != stream->priv->is_muted) { stream->priv->is_muted = is_muted; g_object_notify (G_OBJECT (stream), "is-muted"); } return TRUE; } gboolean gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream, gboolean can_decibel) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (can_decibel != stream->priv->can_decibel) { stream->priv->can_decibel = can_decibel; g_object_notify (G_OBJECT (stream), "can-decibel"); } return TRUE; } const char * gvc_mixer_stream_get_name (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->name; } const char * gvc_mixer_stream_get_description (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->description; } gboolean gvc_mixer_stream_set_name (GvcMixerStream *stream, const char *name) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->name); stream->priv->name = g_strdup (name); g_object_notify (G_OBJECT (stream), "name"); return TRUE; } gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream, const char *description) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->description); stream->priv->description = g_strdup (description); g_object_notify (G_OBJECT (stream), "description"); return TRUE; } gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_event_stream; } gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream, gboolean is_event_stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->is_event_stream = is_event_stream; g_object_notify (G_OBJECT (stream), "is-event-stream"); return TRUE; } gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_virtual; } gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream, gboolean is_virtual) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->is_virtual = is_virtual; g_object_notify (G_OBJECT (stream), "is-virtual"); return TRUE; } const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->application_id; } gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream, const char *application_id) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->application_id); stream->priv->application_id = g_strdup (application_id); g_object_notify (G_OBJECT (stream), "application-id"); return TRUE; } static void on_channel_map_volume_changed (GvcChannelMap *channel_map, gboolean set, GvcMixerStream *stream) { if (set == TRUE) gvc_mixer_stream_push_volume (stream); g_object_notify (G_OBJECT (stream), "volume"); } static gboolean gvc_mixer_stream_set_channel_map (GvcMixerStream *stream, GvcChannelMap *channel_map) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (channel_map != NULL) { g_object_ref (channel_map); } if (stream->priv->channel_map != NULL) { g_signal_handlers_disconnect_by_func (stream->priv->channel_map, on_channel_map_volume_changed, stream); g_object_unref (stream->priv->channel_map); } stream->priv->channel_map = channel_map; if (stream->priv->channel_map != NULL) { g_signal_connect (stream->priv->channel_map, "volume-changed", G_CALLBACK (on_channel_map_volume_changed), stream); g_object_notify (G_OBJECT (stream), "channel-map"); } return TRUE; } const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->icon_name; } const char * gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->sysfs_path; } /** * gvc_mixer_stream_get_gicon: * @stream: a #GvcMixerStream * * Returns: (transfer full): a new #GIcon */ GIcon * gvc_mixer_stream_get_gicon (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); if (stream->priv->icon_name == NULL) return NULL; return g_themed_icon_new_with_default_fallbacks (stream->priv->icon_name); } gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream, const char *icon_name) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->icon_name); stream->priv->icon_name = g_strdup (icon_name); g_object_notify (G_OBJECT (stream), "icon-name"); return TRUE; } gboolean gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream, const char *sysfs_path) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->sysfs_path); stream->priv->sysfs_path = g_strdup (sysfs_path); g_object_notify (G_OBJECT (stream), "sysfs-path"); return TRUE; } /** * gvc_mixer_stream_get_base_volume: * @stream: * * Returns: (type guint32) (transfer none): */ pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->base_volume; } /** * gvc_mixer_stream_set_base_volume: * @stream: * @base_volume: (type guint32): * * Returns: */ gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream, pa_volume_t base_volume) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->base_volume = base_volume; return TRUE; } const GvcMixerStreamPort * gvc_mixer_stream_get_port (GvcMixerStream *stream) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); g_return_val_if_fail (stream->priv->ports != NULL, NULL); for (l = stream->priv->ports; l != NULL; l = l->next) { GvcMixerStreamPort *p = l->data; if (g_strcmp0 (stream->priv->port, p->port) == 0) { return p; } } g_assert_not_reached (); return NULL; } gboolean gvc_mixer_stream_set_port (GvcMixerStream *stream, const char *port) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_return_val_if_fail (stream->priv->ports != NULL, FALSE); g_free (stream->priv->port); stream->priv->port = g_strdup (port); g_free (stream->priv->human_port); stream->priv->human_port = NULL; for (l = stream->priv->ports; l != NULL; l = l->next) { GvcMixerStreamPort *p = l->data; if (g_str_equal (stream->priv->port, p->port)) { stream->priv->human_port = g_strdup (p->human_port); break; } } g_object_notify (G_OBJECT (stream), "port"); return TRUE; } gboolean gvc_mixer_stream_change_port (GvcMixerStream *stream, const char *port) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port); } /** * gvc_mixer_stream_get_ports: * * Return value: (transfer none) (element-type GvcMixerStreamPort): */ const GList * gvc_mixer_stream_get_ports (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->ports; } static int sort_ports (GvcMixerStreamPort *a, GvcMixerStreamPort *b) { if (a->priority == b->priority) return 0; if (a->priority > b->priority) return 1; return -1; } /** * gvc_mixer_stream_set_ports: * @ports: (transfer full) (element-type GvcMixerStreamPort): */ gboolean gvc_mixer_stream_set_ports (GvcMixerStream *stream, GList *ports) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_return_val_if_fail (stream->priv->ports == NULL, FALSE); stream->priv->ports = g_list_sort (ports, (GCompareFunc) sort_ports); return TRUE; } gint gvc_mixer_stream_get_card_index (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), PA_INVALID_INDEX); return stream->priv->card_index; } gboolean gvc_mixer_stream_set_card_index (GvcMixerStream *stream, gint card_index) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->card_index = card_index; g_object_notify (G_OBJECT (stream), "card-index"); return TRUE; } static void gvc_mixer_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerStream *self = GVC_MIXER_STREAM (object); switch (prop_id) { case PROP_PA_CONTEXT: self->priv->pa_context = g_value_get_pointer (value); break; case PROP_INDEX: self->priv->index = g_value_get_ulong (value); break; case PROP_ID: self->priv->id = g_value_get_ulong (value); break; case PROP_CHANNEL_MAP: gvc_mixer_stream_set_channel_map (self, g_value_get_object (value)); break; case PROP_NAME: gvc_mixer_stream_set_name (self, g_value_get_string (value)); break; case PROP_DESCRIPTION: gvc_mixer_stream_set_description (self, g_value_get_string (value)); break; case PROP_APPLICATION_ID: gvc_mixer_stream_set_application_id (self, g_value_get_string (value)); break; case PROP_ICON_NAME: gvc_mixer_stream_set_icon_name (self, g_value_get_string (value)); break; case PROP_SYSFS_PATH: gvc_mixer_stream_set_sysfs_path (self, g_value_get_string (value)); break; case PROP_VOLUME: gvc_mixer_stream_set_volume (self, g_value_get_ulong (value)); break; case PROP_DECIBEL: gvc_mixer_stream_set_decibel (self, g_value_get_double (value)); break; case PROP_IS_MUTED: gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value)); break; case PROP_IS_EVENT_STREAM: gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value)); break; case PROP_IS_VIRTUAL: gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value)); break; case PROP_CAN_DECIBEL: gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value)); break; case PROP_PORT: gvc_mixer_stream_set_port (self, g_value_get_string (value)); break; case PROP_CARD_INDEX: self->priv->card_index = g_value_get_long (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerStream *self = GVC_MIXER_STREAM (object); switch (prop_id) { case PROP_PA_CONTEXT: g_value_set_pointer (value, self->priv->pa_context); break; case PROP_INDEX: g_value_set_ulong (value, self->priv->index); break; case PROP_ID: g_value_set_ulong (value, self->priv->id); break; case PROP_CHANNEL_MAP: g_value_set_object (value, self->priv->channel_map); break; case PROP_NAME: g_value_set_string (value, self->priv->name); break; case PROP_DESCRIPTION: g_value_set_string (value, self->priv->description); break; case PROP_APPLICATION_ID: g_value_set_string (value, self->priv->application_id); break; case PROP_ICON_NAME: g_value_set_string (value, self->priv->icon_name); break; case PROP_SYSFS_PATH: g_value_set_string (value, self->priv->sysfs_path); break; case PROP_VOLUME: g_value_set_ulong (value, pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))); break; case PROP_DECIBEL: g_value_set_double (value, pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)))); break; case PROP_IS_MUTED: g_value_set_boolean (value, self->priv->is_muted); break; case PROP_IS_EVENT_STREAM: g_value_set_boolean (value, self->priv->is_event_stream); break; case PROP_IS_VIRTUAL: g_value_set_boolean (value, self->priv->is_virtual); break; case PROP_CAN_DECIBEL: g_value_set_boolean (value, self->priv->can_decibel); break; case PROP_PORT: g_value_set_string (value, self->priv->port); break; case PROP_CARD_INDEX: g_value_set_long (value, self->priv->card_index); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_stream_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerStream *self; object = G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_STREAM (object); self->priv->id = get_next_stream_serial (); return object; } static gboolean gvc_mixer_stream_real_change_port (GvcMixerStream *stream, const char *port) { return FALSE; } static gboolean gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op) { return FALSE; } static gboolean gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { return FALSE; } gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream) { pa_operation *op; gboolean ret; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (stream->priv->is_event_stream != FALSE) return TRUE; g_debug ("Pushing new volume to stream '%s' (%s)", stream->priv->description, stream->priv->name); ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op); if (ret) { if (stream->priv->change_volume_op != NULL) pa_operation_unref (stream->priv->change_volume_op); stream->priv->change_volume_op = op; } return ret; } gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { gboolean ret; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_is_muted (stream, is_muted); return ret; } gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream) { if (stream->priv->change_volume_op == NULL) return FALSE; if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING)) return TRUE; pa_operation_unref(stream->priv->change_volume_op); stream->priv->change_volume_op = NULL; return FALSE; } static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gvc_mixer_stream_constructor; gobject_class->finalize = gvc_mixer_stream_finalize; gobject_class->set_property = gvc_mixer_stream_set_property; gobject_class->get_property = gvc_mixer_stream_get_property; klass->push_volume = gvc_mixer_stream_real_push_volume; klass->change_port = gvc_mixer_stream_real_change_port; klass->change_is_muted = gvc_mixer_stream_real_change_is_muted; g_object_class_install_property (gobject_class, PROP_INDEX, g_param_spec_ulong ("index", "Index", "The index for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_ID, g_param_spec_ulong ("id", "id", "The id for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_CHANNEL_MAP, g_param_spec_object ("channel-map", "channel map", "The channel map for this stream", GVC_TYPE_CHANNEL_MAP, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PA_CONTEXT, g_param_spec_pointer ("pa-context", "PulseAudio context", "The PulseAudio context for this stream", G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_VOLUME, g_param_spec_ulong ("volume", "Volume", "The volume for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_DECIBEL, g_param_spec_double ("decibel", "Decibel", "The decibel level for this stream", -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_DESCRIPTION, g_param_spec_string ("description", "Description", "Description to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_APPLICATION_ID, g_param_spec_string ("application-id", "Application identifier", "Application identifier for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ICON_NAME, g_param_spec_string ("icon-name", "Icon Name", "Name of icon to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_SYSFS_PATH, g_param_spec_string ("sysfs-path", "Sysfs path", "Sysfs path for the device associated with this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_MUTED, g_param_spec_boolean ("is-muted", "is muted", "Whether stream is muted", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_CAN_DECIBEL, g_param_spec_boolean ("can-decibel", "can decibel", "Whether stream volume can be converted to decibel units", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_EVENT_STREAM, g_param_spec_boolean ("is-event-stream", "is event stream", "Whether stream's role is to play an event", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_VIRTUAL, g_param_spec_boolean ("is-virtual", "is virtual stream", "Whether the stream is virtual", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PORT, g_param_spec_string ("port", "Port", "The name of the current port for this stream", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_CARD_INDEX, g_param_spec_long ("card-index", "Card index", "The index of the card for this stream", PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate)); } static void gvc_mixer_stream_init (GvcMixerStream *stream) { stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream); } static void free_port (GvcMixerStreamPort *p) { g_free (p->port); g_free (p->human_port); g_free (p); } static void gvc_mixer_stream_finalize (GObject *object) { GvcMixerStream *mixer_stream; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_STREAM (object)); mixer_stream = GVC_MIXER_STREAM (object); g_return_if_fail (mixer_stream->priv != NULL); g_object_unref (mixer_stream->priv->channel_map); mixer_stream->priv->channel_map = NULL; g_free (mixer_stream->priv->name); mixer_stream->priv->name = NULL; g_free (mixer_stream->priv->description); mixer_stream->priv->description = NULL; g_free (mixer_stream->priv->application_id); mixer_stream->priv->application_id = NULL; g_free (mixer_stream->priv->icon_name); mixer_stream->priv->icon_name = NULL; g_free (mixer_stream->priv->sysfs_path); mixer_stream->priv->sysfs_path = NULL; g_free (mixer_stream->priv->port); mixer_stream->priv->port = NULL; g_free (mixer_stream->priv->human_port); mixer_stream->priv->human_port = NULL; g_list_foreach (mixer_stream->priv->ports, (GFunc) free_port, NULL); g_list_free (mixer_stream->priv->ports); mixer_stream->priv->ports = NULL; if (mixer_stream->priv->change_volume_op) { pa_operation_unref(mixer_stream->priv->change_volume_op); mixer_stream->priv->change_volume_op = NULL; } G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-ui-device.c0000664000175000017500000006021012625666430017511 0ustar fabiofabio/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * gvc-mixer-ui-device.c * Copyright (C) Conor Curran 2011 * Copyright (C) 2012 David Henningsson, Canonical Ltd. * * gvc-mixer-ui-device.c 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 2 of the License, or * (at your option) any later version. * * gvc-mixer-ui-device.c 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 "gvc-mixer-ui-device.h" #include "gvc-mixer-card.h" #define GVC_MIXER_UI_DEVICE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevicePrivate)) struct GvcMixerUIDevicePrivate { gchar *first_line_desc; gchar *second_line_desc; GvcMixerCard *card; gchar *port_name; gint stream_id; guint id; gboolean port_available; /* These two lists contain pointers to GvcMixerCardProfile objects. Those objects are owned by GvcMixerCard. * * TODO: Do we want to add a weak reference to the GvcMixerCard for this reason? */ GList *supported_profiles; /* all profiles supported by this port.*/ GList *profiles; /* profiles to be added to combobox, subset of supported_profiles. */ GvcMixerUIDeviceDirection type; gboolean disable_profile_swapping; gchar *user_preferred_profile; }; enum { PROP_0, PROP_DESC_LINE_1, PROP_DESC_LINE_2, PROP_CARD, PROP_PORT_NAME, PROP_STREAM_ID, PROP_UI_DEVICE_TYPE, PROP_PORT_AVAILABLE, }; static void gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass); static void gvc_mixer_ui_device_init (GvcMixerUIDevice *device); static void gvc_mixer_ui_device_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerUIDevice, gvc_mixer_ui_device, G_TYPE_OBJECT); static guint32 get_next_output_serial (void) { static guint32 output_serial = 1; guint32 serial; serial = output_serial++; if ((gint32)output_serial < 0) output_serial = 1; return serial; } static void gvc_mixer_ui_device_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object); switch (property_id) { case PROP_DESC_LINE_1: g_value_set_string (value, self->priv->first_line_desc); break; case PROP_DESC_LINE_2: g_value_set_string (value, self->priv->second_line_desc); break; case PROP_CARD: g_value_set_pointer (value, self->priv->card); break; case PROP_PORT_NAME: g_value_set_string (value, self->priv->port_name); break; case PROP_STREAM_ID: g_value_set_int (value, self->priv->stream_id); break; case PROP_UI_DEVICE_TYPE: g_value_set_uint (value, (guint)self->priv->type); break; case PROP_PORT_AVAILABLE: g_value_set_boolean (value, self->priv->port_available); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gvc_mixer_ui_device_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object); switch (property_id) { case PROP_DESC_LINE_1: g_free (self->priv->first_line_desc); self->priv->first_line_desc = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - 1st line: %s\n", self->priv->first_line_desc); break; case PROP_DESC_LINE_2: g_free (self->priv->second_line_desc); self->priv->second_line_desc = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - 2nd line: %s\n", self->priv->second_line_desc); break; case PROP_CARD: self->priv->card = g_value_get_pointer (value); g_debug ("gvc-mixer-output-set-property - card: %p\n", self->priv->card); break; case PROP_PORT_NAME: g_free (self->priv->port_name); self->priv->port_name = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - card port name: %s\n", self->priv->port_name); break; case PROP_STREAM_ID: self->priv->stream_id = g_value_get_int (value); g_debug ("gvc-mixer-output-set-property - sink/source id: %i\n", self->priv->stream_id); break; case PROP_UI_DEVICE_TYPE: self->priv->type = (GvcMixerUIDeviceDirection) g_value_get_uint (value); break; case PROP_PORT_AVAILABLE: self->priv->port_available = g_value_get_boolean (value); g_debug ("gvc-mixer-output-set-property - port available %i, value passed in %i \n", self->priv->port_available, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gvc_mixer_ui_device_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerUIDevice *self; object = G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_UI_DEVICE (object); self->priv->id = get_next_output_serial (); self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID; return object; } static void gvc_mixer_ui_device_init (GvcMixerUIDevice *device) { device->priv = GVC_MIXER_UI_DEVICE_GET_PRIVATE (device); } static void char_clear_pointer (gchar *pointer) { if (pointer) { g_free (pointer); pointer = NULL; } } static void list_clear_pointer (GList *pointer) { if (pointer) { g_list_free (pointer); pointer = NULL; } } static void gvc_mixer_ui_device_dispose (GObject *object) { GvcMixerUIDevice *device; g_return_if_fail (object != NULL); g_return_if_fail (GVC_MIXER_UI_DEVICE (object)); device = GVC_MIXER_UI_DEVICE (object); char_clear_pointer (device->priv->port_name); char_clear_pointer (device->priv->first_line_desc); char_clear_pointer (device->priv->second_line_desc); list_clear_pointer (device->priv->profiles); list_clear_pointer (device->priv->supported_profiles); char_clear_pointer (device->priv->user_preferred_profile); G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->dispose (object); } static void gvc_mixer_ui_device_finalize (GObject *object) { G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->finalize (object); } static void gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass) { GObjectClass* object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->constructor = gvc_mixer_ui_device_constructor; object_class->dispose = gvc_mixer_ui_device_dispose; object_class->finalize = gvc_mixer_ui_device_finalize; object_class->set_property = gvc_mixer_ui_device_set_property; object_class->get_property = gvc_mixer_ui_device_get_property; pspec = g_param_spec_string ("description", "Description construct prop", "Set first line description", "no-name-set", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DESC_LINE_1, pspec); pspec = g_param_spec_string ("origin", "origin construct prop", "Set second line description name", "no-name-set", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DESC_LINE_2, pspec); pspec = g_param_spec_pointer ("card", "Card from pulse", "Set/Get card", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CARD, pspec); pspec = g_param_spec_string ("port-name", "port-name construct prop", "Set port-name", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PORT_NAME, pspec); pspec = g_param_spec_int ("stream-id", "stream id assigned by gvc-stream", "Set/Get stream id", -1, G_MAXINT, GVC_MIXER_UI_DEVICE_INVALID, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_STREAM_ID, pspec); pspec = g_param_spec_uint ("type", "ui-device type", "determine whether its an input and output", 0, 1, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_UI_DEVICE_TYPE, pspec); pspec = g_param_spec_boolean ("port-available", "available", "determine whether this port is available", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PORT_AVAILABLE, pspec); g_type_class_add_private (klass, sizeof (GvcMixerUIDevicePrivate)); } /* Removes the part of the string that starts with skip_prefix * ie. corresponding to the other direction. * Normally either "input:" or "output:" * * Example: if given the input string "output:hdmi-stereo+input:analog-stereo" and * skip_prefix "input:", the resulting string is "output:hdmi-stereo". * * The returned string must be freed with g_free(). */ static gchar * get_profile_canonical_name (const gchar *profile_name, const gchar *skip_prefix) { gchar *result = NULL; gchar **s; int i; /* optimisation for the simple case. */ if (strstr (profile_name, skip_prefix) == NULL) return g_strdup (profile_name); s = g_strsplit (profile_name, "+", 0); for (i = 0; i < g_strv_length (s); i++) { if (g_str_has_prefix (s[i], skip_prefix)) continue; if (result == NULL) result = g_strdup (s[i]); else { gchar *c = g_strdup_printf("%s+%s", result, s[i]); g_free(result); result = c; } } g_strfreev(s); if (!result) return g_strdup("off"); return result; } const gchar * gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device, const gchar *profile) { gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:"; gchar *target_cname = get_profile_canonical_name (profile, skip_prefix); GList *l; gchar *result = NULL; for (l = device->priv->profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); if (strcmp (canonical_name, target_cname) == 0) result = p->profile; g_free (canonical_name); } g_free (target_cname); g_debug ("Matching profile for '%s' is '%s'", profile, result ? result : "(null)"); return result; } static void add_canonical_names_of_profiles (GvcMixerUIDevice *device, const GList *in_profiles, GHashTable *added_profiles, const gchar *skip_prefix, gboolean only_canonical) { const GList *l; for (l = in_profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); g_debug ("The canonical name for '%s' is '%s'", p->profile, canonical_name); /* Have we already added the canonical version of this profile? */ if (g_hash_table_contains (added_profiles, canonical_name)) { g_free (canonical_name); continue; } if (only_canonical && strcmp (p->profile, canonical_name) != 0) { g_free (canonical_name); continue; } g_free (canonical_name); g_debug ("Adding profile to combobox: '%s' - '%s'", p->profile, p->human_profile); g_hash_table_insert (added_profiles, g_strdup (p->profile), p); device->priv->profiles = g_list_append (device->priv->profiles, p); } } /** * gvc_mixer_ui_device_set_profiles: * @in_profiles: a list of GvcMixerCardProfile * * Assigns value to * - device->priv->profiles (profiles to be added to combobox) * - device->priv->supported_profiles (all profiles of this port) * - device->priv->disable_profile_swapping (whether to show the combobox) * * This method attempts to reduce the list of profiles visible to the user by figuring out * from the context of that device (whether it's an input or an output) what profiles * actually provide an alternative. * * It does this by the following. * - It ignores off profiles. * - It takes the canonical name of the profile. That name is what you get when you * ignore the other direction. * - In the first iteration, it only adds the names of canonical profiles - i e * when the other side is turned off. * - Normally the first iteration covers all cases, but sometimes (e g bluetooth) * it doesn't, so add other profiles whose canonical name isn't already added * in a second iteration. */ void gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device, const GList *in_profiles) { GHashTable *added_profiles; gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:"; g_debug ("Set profiles for '%s'", gvc_mixer_ui_device_get_description(device)); if (in_profiles == NULL) return; device->priv->supported_profiles = g_list_copy ((GList*) in_profiles); added_profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); /* Run two iterations: First, add profiles which are canonical themselves, * Second, add profiles for which the canonical name is not added already. */ add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, TRUE); add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, FALSE); /* TODO: Consider adding the "Off" profile here */ device->priv->disable_profile_swapping = g_hash_table_size (added_profiles) <= 1; g_hash_table_destroy (added_profiles); } /** * gvc_mixer_ui_device_get_best_profile: * @selected: The selected profile or its canonical name or %NULL for any profile * @current: The currently selected profile * * Returns: (transfer none): a profile name, valid as long as the UI device profiles are. */ const gchar * gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device, const gchar *selected, const gchar *current) { GList *candidates, *l; const gchar *result; gchar *skip_prefix; gchar *canonical_name_selected; if (device->priv->type == UIDeviceInput) skip_prefix = "output:"; else skip_prefix = "input:"; /* First make a list of profiles acceptable to switch to */ canonical_name_selected = NULL; if (selected) canonical_name_selected = get_profile_canonical_name (selected, skip_prefix); candidates = NULL; for (l = device->priv->supported_profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); if (!canonical_name_selected || strcmp (canonical_name, canonical_name_selected) == 0) { candidates = g_list_append (candidates, p); g_debug ("Candidate for profile switching: '%s'", p->profile); } g_free (canonical_name); } if (!candidates) { g_warning ("No suitable profile candidates for '%s'", selected ? selected : "(null)"); g_free (canonical_name_selected); return current; } /* 1) Maybe we can skip profile switching altogether? */ result = NULL; for (l = candidates; (result == NULL) && (l != NULL); l = l->next) { GvcMixerCardProfile* p = l->data; if (strcmp (current, p->profile) == 0) result = p->profile; } /* 2) Try to keep the other side unchanged if possible */ if (result == NULL) { guint prio = 0; gchar *skip_prefix_reverse = device->priv->type == UIDeviceInput ? "input:" : "output:"; gchar *current_reverse = get_profile_canonical_name (current, skip_prefix_reverse); for (l = candidates; l != NULL; l = l->next) { gchar *p_reverse; GvcMixerCardProfile* p = l->data; p_reverse = get_profile_canonical_name (p->profile, skip_prefix_reverse); g_debug ("Comparing '%s' (from '%s') with '%s', prio %d", p_reverse, p->profile, current_reverse, p->priority); if (strcmp (p_reverse, current_reverse) == 0 && (!result || p->priority > prio)) { result = p->profile; prio = p->priority; } g_free (p_reverse); } g_free (current_reverse); } /* 3) All right, let's just pick the profile with highest priority. * TODO: We could consider asking a GUI question if this stops streams * in the other direction */ if (result == NULL) { guint prio = 0; for (l = candidates; l != NULL; l = l->next) { GvcMixerCardProfile* p = l->data; if ((p->priority > prio) || !result) { result = p->profile; prio = p->priority; } } } g_list_free (candidates); g_free (canonical_name_selected); return result; } const gchar * gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device) { GvcMixerCardProfile *profile; g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); if (device->priv->card == NULL) { g_warning ("Device did not have an appropriate card"); return NULL; } profile = gvc_mixer_card_get_profile (device->priv->card); return gvc_mixer_ui_device_get_matching_profile (device, profile->profile); } gboolean gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return device->priv->disable_profile_swapping; } GList* gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->profiles; } GList* gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->supported_profiles; } guint gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0); return device->priv->id; } gint gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0); return device->priv->stream_id; } void gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *self) { g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (self)); self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID; } const gchar * gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->first_line_desc; } const gchar * gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->second_line_desc; } const gchar* gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->user_preferred_profile; } const gchar * gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device) { GList *last; GvcMixerCardProfile *profile; g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); last = g_list_last (device->priv->supported_profiles); profile = last->data; return profile->profile; } void gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device, const gchar *profile) { g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device)); g_free (device->priv->user_preferred_profile); device->priv->user_preferred_profile = g_strdup (profile); } const gchar * gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->port_name; } gboolean gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return (device->priv->port_name != NULL); } gboolean gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return (device->priv->type == UIDeviceOutput); } Cinnamon-2.8.6/src/gvc/gvc-channel-map-private.h0000664000175000017500000000306112625666430020356 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_CHANNEL_MAP_PRIVATE_H #define __GVC_CHANNEL_MAP_PRIVATE_H #include #include G_BEGIN_DECLS GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map); const pa_channel_map * gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map); void gvc_channel_map_volume_changed (GvcChannelMap *map, const pa_cvolume *cv, gboolean set); const pa_cvolume * gvc_channel_map_get_cvolume (const GvcChannelMap *map); G_END_DECLS #endif /* __GVC_CHANNEL_MAP_PRIVATE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-stream.h0000664000175000017500000001537012625666430017146 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_STREAM_H #define __GVC_MIXER_STREAM_H #include #include "gvc-pulseaudio-fake.h" #include "gvc-channel-map.h" #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_STREAM (gvc_mixer_stream_get_type ()) #define GVC_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStream)) #define GVC_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass)) #define GVC_IS_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_STREAM)) #define GVC_IS_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_STREAM)) #define GVC_MIXER_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass)) typedef struct GvcMixerStreamPrivate GvcMixerStreamPrivate; typedef struct { GObject parent; GvcMixerStreamPrivate *priv; } GvcMixerStream; typedef struct { GObjectClass parent_class; /* vtable */ gboolean (*push_volume) (GvcMixerStream *stream, gpointer *operation); gboolean (*change_is_muted) (GvcMixerStream *stream, gboolean is_muted); gboolean (*change_port) (GvcMixerStream *stream, const char *port); } GvcMixerStreamClass; typedef struct { char *port; char *human_port; guint priority; gboolean available; } GvcMixerStreamPort; GType gvc_mixer_stream_get_type (void); guint gvc_mixer_stream_get_index (GvcMixerStream *stream); guint gvc_mixer_stream_get_id (GvcMixerStream *stream); const GvcChannelMap *gvc_mixer_stream_get_channel_map(GvcMixerStream *stream); const GvcMixerStreamPort *gvc_mixer_stream_get_port (GvcMixerStream *stream); const GList * gvc_mixer_stream_get_ports (GvcMixerStream *stream); gboolean gvc_mixer_stream_change_port (GvcMixerStream *stream, const char *port); pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream); gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream); gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream); pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream); gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream); gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream); gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream, gboolean is_muted); gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream); const char * gvc_mixer_stream_get_name (GvcMixerStream *stream); const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream); const char * gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream); GIcon * gvc_mixer_stream_get_gicon (GvcMixerStream *stream); const char * gvc_mixer_stream_get_description (GvcMixerStream *stream); const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream); gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream); gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream); gint gvc_mixer_stream_get_card_index (GvcMixerStream *stream); /* private */ gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream, pa_volume_t volume); gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream, gdouble db); gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream, gboolean is_muted); gboolean gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream, gboolean can_decibel); gboolean gvc_mixer_stream_set_name (GvcMixerStream *stream, const char *name); gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream, const char *description); gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream, const char *name); gboolean gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream, const char *sysfs_path); gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream, gboolean is_event_stream); gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream, gboolean is_event_stream); gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream, const char *application_id); gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream, pa_volume_t base_volume); gboolean gvc_mixer_stream_set_port (GvcMixerStream *stream, const char *port); gboolean gvc_mixer_stream_set_ports (GvcMixerStream *stream, GList *ports); gboolean gvc_mixer_stream_set_card_index (GvcMixerStream *stream, gint card_index); G_END_DECLS #endif /* __GVC_MIXER_STREAM_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-stream-private.h0000664000175000017500000000214312625666430020610 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_STREAM_PRIVATE_H #define __GVC_MIXER_STREAM_PRIVATE_H #include #include "gvc-channel-map.h" G_BEGIN_DECLS pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream); G_END_DECLS #endif /* __GVC_MIXER_STREAM_PRIVATE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-event-role.c0000664000175000017500000001701312625666430017722 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include #include "gvc-mixer-event-role.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_EVENT_ROLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRolePrivate)) struct GvcMixerEventRolePrivate { char *device; }; enum { PROP_0, PROP_DEVICE }; static void gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass); static void gvc_mixer_event_role_init (GvcMixerEventRole *mixer_event_role); static void gvc_mixer_event_role_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM) static gboolean update_settings (GvcMixerEventRole *role, gboolean is_muted, gpointer *op) { pa_operation *o; const GvcChannelMap *map; pa_context *context; pa_ext_stream_restore_info info; map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role)); info.volume = *gvc_channel_map_get_cvolume(map); info.name = "sink-input-by-media-role:event"; info.channel_map = *gvc_channel_map_get_pa_channel_map(map); info.device = role->priv->device; info.mute = is_muted; context = gvc_mixer_stream_get_pa_context (GVC_MIXER_STREAM (role)); o = pa_ext_stream_restore_write (context, PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL); if (o == NULL) { g_warning ("pa_ext_stream_restore_write() failed"); return FALSE; } if (op != NULL) *op = o; return TRUE; } static gboolean gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op) { return update_settings (GVC_MIXER_EVENT_ROLE (stream), gvc_mixer_stream_get_is_muted (stream), op); } static gboolean gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { /* Apply change straight away so that we don't get a race with * gvc_mixer_event_role_push_volume(). * See https://bugs.freedesktop.org/show_bug.cgi?id=51413 */ gvc_mixer_stream_set_is_muted (stream, is_muted); return update_settings (GVC_MIXER_EVENT_ROLE (stream), is_muted, NULL); } static gboolean gvc_mixer_event_role_set_device (GvcMixerEventRole *role, const char *device) { g_return_val_if_fail (GVC_IS_MIXER_EVENT_ROLE (role), FALSE); g_free (role->priv->device); role->priv->device = g_strdup (device); g_object_notify (G_OBJECT (role), "device"); return TRUE; } static void gvc_mixer_event_role_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object); switch (prop_id) { case PROP_DEVICE: gvc_mixer_event_role_set_device (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_event_role_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object); switch (prop_id) { case PROP_DEVICE: g_value_set_string (value, self->priv->device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_event_role_finalize; object_class->set_property = gvc_mixer_event_role_set_property; object_class->get_property = gvc_mixer_event_role_get_property; stream_class->push_volume = gvc_mixer_event_role_push_volume; stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted; g_object_class_install_property (object_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "Device", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GvcMixerEventRolePrivate)); } static void gvc_mixer_event_role_init (GvcMixerEventRole *event_role) { event_role->priv = GVC_MIXER_EVENT_ROLE_GET_PRIVATE (event_role); } static void gvc_mixer_event_role_finalize (GObject *object) { GvcMixerEventRole *mixer_event_role; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_EVENT_ROLE (object)); mixer_event_role = GVC_MIXER_EVENT_ROLE (object); g_return_if_fail (mixer_event_role->priv != NULL); g_free (mixer_event_role->priv->device); G_OBJECT_CLASS (gvc_mixer_event_role_parent_class)->finalize (object); } /** * gvc_mixer_event_role_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_event_role_new (pa_context *context, const char *device, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_EVENT_ROLE, "pa-context", context, "index", 0, "device", device, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-card.h0000664000175000017500000000774512625666430016573 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008-2009 Red Hat, Inc. * Copyright (C) Conor Curran 2011 * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_CARD_H #define __GVC_MIXER_CARD_H #include #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_CARD (gvc_mixer_card_get_type ()) #define GVC_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CARD, GvcMixerCard)) #define GVC_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CARD, GvcMixerCardClass)) #define GVC_IS_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CARD)) #define GVC_IS_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CARD)) #define GVC_MIXER_CARD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardClass)) typedef struct GvcMixerCardPrivate GvcMixerCardPrivate; typedef struct { GObject parent; GvcMixerCardPrivate *priv; } GvcMixerCard; typedef struct { GObjectClass parent_class; /* vtable */ } GvcMixerCardClass; typedef struct { char *profile; char *human_profile; char *status; guint priority; guint n_sinks, n_sources; } GvcMixerCardProfile; typedef struct { char *port; char *human_port; guint priority; gint available; gint direction; GList *profiles; } GvcMixerCardPort; GType gvc_mixer_card_get_type (void); guint gvc_mixer_card_get_id (GvcMixerCard *card); guint gvc_mixer_card_get_index (GvcMixerCard *card); const char * gvc_mixer_card_get_name (GvcMixerCard *card); const char * gvc_mixer_card_get_icon_name (GvcMixerCard *card); GvcMixerCardProfile * gvc_mixer_card_get_profile (GvcMixerCard *card); const GList * gvc_mixer_card_get_profiles (GvcMixerCard *card); const GList * gvc_mixer_card_get_ports (GvcMixerCard *card); gboolean gvc_mixer_card_change_profile (GvcMixerCard *card, const char *profile); GIcon * gvc_mixer_card_get_gicon (GvcMixerCard *card); int gvc_mixer_card_profile_compare (GvcMixerCardProfile *a, GvcMixerCardProfile *b); /* private */ gboolean gvc_mixer_card_set_name (GvcMixerCard *card, const char *name); gboolean gvc_mixer_card_set_icon_name (GvcMixerCard *card, const char *name); gboolean gvc_mixer_card_set_profile (GvcMixerCard *card, const char *profile); gboolean gvc_mixer_card_set_profiles (GvcMixerCard *card, GList *profiles); gboolean gvc_mixer_card_set_ports (GvcMixerCard *stream, GList *ports); G_END_DECLS #endif /* __GVC_MIXER_CARD_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-control.c0000664000175000017500000037257712625666430017345 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2006-2008 Lennart Poettering * Copyright (C) 2008 Sjoerd Simons * Copyright (C) 2008 William Jon McCann * Copyright (C) 2012 Conor Curran * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include #include #include "gvc-mixer-control.h" #include "gvc-mixer-sink.h" #include "gvc-mixer-source.h" #include "gvc-mixer-sink-input.h" #include "gvc-mixer-source-output.h" #include "gvc-mixer-event-role.h" #include "gvc-mixer-card.h" #include "gvc-mixer-card-private.h" #include "gvc-channel-map-private.h" #include "gvc-mixer-control-private.h" #include "gvc-mixer-ui-device.h" #define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate)) #define RECONNECT_DELAY 5 enum { PROP_0, PROP_NAME }; struct GvcMixerControlPrivate { pa_glib_mainloop *pa_mainloop; pa_mainloop_api *pa_api; pa_context *pa_context; int n_outstanding; guint reconnect_id; char *name; gboolean default_sink_is_set; guint default_sink_id; char *default_sink_name; gboolean default_source_is_set; guint default_source_id; char *default_source_name; gboolean event_sink_input_is_set; guint event_sink_input_id; GHashTable *all_streams; GHashTable *sinks; /* fixed outputs */ GHashTable *sources; /* fixed inputs */ GHashTable *sink_inputs; /* routable output streams */ GHashTable *source_outputs; /* routable input streams */ GHashTable *clients; GHashTable *cards; GvcMixerStream *new_default_sink_stream; /* new default sink stream, used in gvc_mixer_control_set_default_sink () */ GvcMixerStream *new_default_source_stream; /* new default source stream, used in gvc_mixer_control_set_default_source () */ GHashTable *ui_outputs; /* UI visible outputs */ GHashTable *ui_inputs; /* UI visible inputs */ /* When we change profile on a device that is not the server default sink, * it will jump back to the default sink set by the server to prevent the * audio setup from being 'outputless'. * * All well and good but then when we get the new stream created for the * new profile how do we know that this is the intended default or selected * device the user wishes to use. */ guint profile_swapping_device_id; GvcMixerControlState state; }; enum { STATE_CHANGED, STREAM_ADDED, STREAM_REMOVED, CARD_ADDED, CARD_REMOVED, DEFAULT_SINK_CHANGED, DEFAULT_SOURCE_CHANGED, ACTIVE_OUTPUT_UPDATE, ACTIVE_INPUT_UPDATE, OUTPUT_ADDED, INPUT_ADDED, OUTPUT_REMOVED, INPUT_REMOVED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void gvc_mixer_control_class_init (GvcMixerControlClass *klass); static void gvc_mixer_control_init (GvcMixerControl *mixer_control); static void gvc_mixer_control_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT) pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return control->priv->pa_context; } /** * gvc_mixer_control_get_event_sink_input: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_event_sink_input (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->event_sink_input_id)); return stream; } static void gvc_mixer_control_stream_restore_cb (pa_context *c, GvcMixerStream *new_stream, const pa_ext_stream_restore_info *info, GvcMixerControl *control) { pa_operation *o; pa_ext_stream_restore_info new_info; if (new_stream == NULL) return; new_info.name = info->name; new_info.channel_map = info->channel_map; new_info.volume = info->volume; new_info.mute = info->mute; new_info.device = gvc_mixer_stream_get_name (new_stream); o = pa_ext_stream_restore_write (control->priv->pa_context, PA_UPDATE_REPLACE, &new_info, 1, TRUE, NULL, NULL); if (o == NULL) { g_warning ("pa_ext_stream_restore_write() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return; } g_debug ("Changed default device for %s to %s", info->name, new_info.device); pa_operation_unref (o); } static void gvc_mixer_control_stream_restore_sink_cb (pa_context *c, const pa_ext_stream_restore_info *info, int eol, void *userdata) { GvcMixerControl *control = (GvcMixerControl *) userdata; if (eol || info == NULL || !g_str_has_prefix(info->name, "sink-input-by")) return; gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_sink_stream, info, control); } static void gvc_mixer_control_stream_restore_source_cb (pa_context *c, const pa_ext_stream_restore_info *info, int eol, void *userdata) { GvcMixerControl *control = (GvcMixerControl *) userdata; if (eol || info == NULL || !g_str_has_prefix(info->name, "source-output-by")) return; gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_source_stream, info, control); } /** * gvc_mixer_control_lookup_device_from_stream: * @control: * @stream: * Returns: (transfer none): a #GvcUIDevice or %NULL */ GvcMixerUIDevice * gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control, GvcMixerStream *stream) { GList *devices, *d; gboolean is_network_stream; const GList *ports; GvcMixerUIDevice *ret; if (GVC_IS_MIXER_SOURCE (stream)) devices = g_hash_table_get_values (control->priv->ui_inputs); else devices = g_hash_table_get_values (control->priv->ui_outputs); ret = NULL; ports = gvc_mixer_stream_get_ports (stream); is_network_stream = (ports == NULL); for (d = devices; d != NULL; d = d->next) { GvcMixerUIDevice *device = d->data; gint stream_id = G_MAXINT; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (is_network_stream && stream_id == gvc_mixer_stream_get_id (stream)) { g_debug ("lookup device from stream - %s - it is a network_stream ", gvc_mixer_ui_device_get_description (device)); ret = device; break; } else if (!is_network_stream) { const GvcMixerStreamPort *port; port = gvc_mixer_stream_get_port (stream); if (stream_id == gvc_mixer_stream_get_id (stream) && g_strcmp0 (gvc_mixer_ui_device_get_port (device), port->port) == 0) { g_debug ("lookup-device-from-stream found device: device description '%s', device port = '%s', device stream id %i AND stream port = '%s' stream id '%u' and stream description '%s'", gvc_mixer_ui_device_get_description (device), gvc_mixer_ui_device_get_port (device), stream_id, port->port, gvc_mixer_stream_get_id (stream), gvc_mixer_stream_get_description (stream)); ret = device; break; } } } g_debug ("gvc_mixer_control_lookup_device_from_stream - Could not find a device for stream '%s'",gvc_mixer_stream_get_description (stream)); g_list_free (devices); return ret; } gboolean gvc_mixer_control_set_default_sink (GvcMixerControl *control, GvcMixerStream *stream) { pa_operation *o; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_debug ("about to set default sink on server"); o = pa_context_set_default_sink (control->priv->pa_context, gvc_mixer_stream_get_name (stream), NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_default_sink() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); control->priv->new_default_sink_stream = stream; g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_sink_stream); o = pa_ext_stream_restore_read (control->priv->pa_context, gvc_mixer_control_stream_restore_sink_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); return TRUE; } gboolean gvc_mixer_control_set_default_source (GvcMixerControl *control, GvcMixerStream *stream) { GvcMixerUIDevice* input; pa_operation *o; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); o = pa_context_set_default_source (control->priv->pa_context, gvc_mixer_stream_get_name (stream), NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_default_source() failed"); return FALSE; } pa_operation_unref (o); control->priv->new_default_source_stream = stream; g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_source_stream); o = pa_ext_stream_restore_read (control->priv->pa_context, gvc_mixer_control_stream_restore_source_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); /* source change successful, update the UI. */ input = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); return TRUE; } /** * gvc_mixer_control_get_default_sink: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); if (control->priv->default_sink_is_set) { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->default_sink_id)); } else { stream = NULL; } return stream; } /** * gvc_mixer_control_get_default_source: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); if (control->priv->default_source_is_set) { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->default_source_id)); } else { stream = NULL; } return stream; } static gpointer gvc_mixer_control_lookup_id (GHashTable *hash_table, guint id) { return g_hash_table_lookup (hash_table, GUINT_TO_POINTER (id)); } /** * gvc_mixer_control_lookup_stream_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->all_streams, id); } /** * gvc_mixer_control_lookup_card_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->cards, id); } /** * gvc_mixer_control_lookup_output_id: * @control: * @id: * Returns: (transfer none): */ GvcMixerUIDevice * gvc_mixer_control_lookup_output_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->ui_outputs, id); } /** * gvc_mixer_control_lookup_input_id: * @control: * @id: * Returns: (transfer none): */ GvcMixerUIDevice * gvc_mixer_control_lookup_input_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->ui_inputs, id); } /** * gvc_mixer_control_get_stream_from_device: * @control: * @device: * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_stream_from_device (GvcMixerControl *control, GvcMixerUIDevice *device) { gint stream_id; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); stream_id = gvc_mixer_ui_device_get_stream_id (device); if (stream_id == GVC_MIXER_UI_DEVICE_INVALID) { g_debug ("gvc_mixer_control_get_stream_from_device - device has a null stream"); return NULL; } return gvc_mixer_control_lookup_stream_id (control, stream_id); } /** * gvc_mixer_control_change_profile_on_selected_device: * @control: * @device: * @profile: Can be null if any profile present on this port is okay * Returns: This method will attempt to swap the profile on the card of * the device with given profile name. If successfull it will set the * preferred profile on that device so as we know the next time the user * moves to that device it should have this profile active. */ gboolean gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control, GvcMixerUIDevice *device, const gchar *profile) { const gchar *best_profile; GvcMixerCardProfile *current_profile; GvcMixerCard *card; g_object_get (G_OBJECT (device), "card", &card, NULL); current_profile = gvc_mixer_card_get_profile (card); if (current_profile) best_profile = gvc_mixer_ui_device_get_best_profile (device, profile, current_profile->profile); else best_profile = profile; g_assert (best_profile); g_debug ("Selected '%s', moving to profile '%s' on card '%s' on stream id %i", profile ? profile : "(any)", best_profile, gvc_mixer_card_get_name (card), gvc_mixer_ui_device_get_stream_id (device)); g_debug ("default sink name = %s and default sink id %u", control->priv->default_sink_name, control->priv->default_sink_id); control->priv->profile_swapping_device_id = gvc_mixer_ui_device_get_id (device); if (gvc_mixer_card_change_profile (card, best_profile)) { gvc_mixer_ui_device_set_user_preferred_profile (device, best_profile); return TRUE; } return FALSE; } /** * gvc_mixer_control_change_output: * @control: * @output: * This method is called from the UI when the user selects a previously unselected device. * - Firstly it queries the stream from the device. * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources) * In the scenario of a NULL stream on the device * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device. * - It then caches this device in control->priv->cached_desired_output_id so that when the update_sink triggered * from when we attempt to change profile we will know exactly what device to highlight on that stream. * - It attempts to swap the profile on the card from that device and returns. * - Next, it handles network or bluetooth streams that only require their stream to be made the default. * - Next it deals with port changes so if the stream's active port is not the same as the port on the device * it will attempt to change the port on that stream to be same as the device. If this fails it will return. * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active output device. */ void gvc_mixer_control_change_output (GvcMixerControl *control, GvcMixerUIDevice* output) { GvcMixerStream *stream; GvcMixerStream *default_stream; const GvcMixerStreamPort *active_port; const gchar *output_port; g_debug ("control change output"); stream = gvc_mixer_control_get_stream_from_device (control, output); if (stream == NULL) { gvc_mixer_control_change_profile_on_selected_device (control, output, NULL); return; } /* Handle a network sink as a portless or cardless device */ if (!gvc_mixer_ui_device_has_ports (output)) { g_debug ("Did we try to move to a software/bluetooth sink ?"); if (gvc_mixer_control_set_default_sink (control, stream)) { /* sink change was successful, update the UI.*/ g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } else { g_warning ("Failed to set default sink with stream from output %s", gvc_mixer_ui_device_get_description (output)); } return; } active_port = gvc_mixer_stream_get_port (stream); output_port = gvc_mixer_ui_device_get_port (output); /* First ensure the correct port is active on the sink */ if (g_strcmp0 (active_port->port, output_port) != 0) { g_debug ("Port change, switch to = %s", output_port); if (gvc_mixer_stream_change_port (stream, output_port) == FALSE) { g_warning ("Could not change port !"); return; } } default_stream = gvc_mixer_control_get_default_sink (control); /* Finally if we are not on the correct stream, swap over. */ if (stream != default_stream) { GvcMixerUIDevice* output; g_debug ("Attempting to swap over to stream %s ", gvc_mixer_stream_get_description (stream)); if (gvc_mixer_control_set_default_sink (control, stream)) { output = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } else { /* If the move failed for some reason reset the UI. */ output = gvc_mixer_control_lookup_device_from_stream (control, default_stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } } } /** * gvc_mixer_control_change_input: * @control: * @input: * This method is called from the UI when the user selects a previously unselected device. * - Firstly it queries the stream from the device. * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources) * In the scenario of a NULL stream on the device * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device. * - It then caches this device in control->priv->cached_desired_input_id so that when the update_source triggered * from when we attempt to change profile we will know exactly what device to highlight on that stream. * - It attempts to swap the profile on the card from that device and returns. * - Next, it handles network or bluetooth streams that only require their stream to be made the default. * - Next it deals with port changes so if the stream's active port is not the same as the port on the device * it will attempt to change the port on that stream to be same as the device. If this fails it will return. * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active input device. */ void gvc_mixer_control_change_input (GvcMixerControl *control, GvcMixerUIDevice* input) { GvcMixerStream *stream; GvcMixerStream *default_stream; const GvcMixerStreamPort *active_port; const gchar *input_port; stream = gvc_mixer_control_get_stream_from_device (control, input); if (stream == NULL) { gvc_mixer_control_change_profile_on_selected_device (control, input, NULL); return; } /* Handle a network sink as a portless/cardless device */ if (!gvc_mixer_ui_device_has_ports (input)) { g_debug ("Did we try to move to a software/bluetooth source ?"); if (! gvc_mixer_control_set_default_source (control, stream)) { g_warning ("Failed to set default source with stream from input %s", gvc_mixer_ui_device_get_description (input)); } return; } active_port = gvc_mixer_stream_get_port (stream); input_port = gvc_mixer_ui_device_get_port (input); /* First ensure the correct port is active on the sink */ if (g_strcmp0 (active_port->port, input_port) != 0) { g_debug ("Port change, switch to = %s", input_port); if (gvc_mixer_stream_change_port (stream, input_port) == FALSE) { g_warning ("Could not change port!"); return; } } default_stream = gvc_mixer_control_get_default_source (control); /* Finally if we are not on the correct stream, swap over. */ if (stream != default_stream) { g_debug ("change-input - attempting to swap over to stream %s", gvc_mixer_stream_get_description (stream)); gvc_mixer_control_set_default_source (control, stream); } } static void listify_hash_values_hfunc (gpointer key, gpointer value, gpointer user_data) { GSList **list = user_data; *list = g_slist_prepend (*list, value); } static int gvc_name_collate (const char *namea, const char *nameb) { if (nameb == NULL && namea == NULL) return 0; if (nameb == NULL) return 1; if (namea == NULL) return -1; return g_utf8_collate (namea, nameb); } static int gvc_card_collate (GvcMixerCard *a, GvcMixerCard *b) { const char *namea; const char *nameb; g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0); g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0); namea = gvc_mixer_card_get_name (a); nameb = gvc_mixer_card_get_name (b); return gvc_name_collate (namea, nameb); } /** * gvc_mixer_control_get_cards: * @control: * * Returns: (transfer container) (element-type Gvc.MixerCard): */ GSList * gvc_mixer_control_get_cards (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->cards, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_card_collate); } static int gvc_stream_collate (GvcMixerStream *a, GvcMixerStream *b) { const char *namea; const char *nameb; g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0); g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0); namea = gvc_mixer_stream_get_name (a); nameb = gvc_mixer_stream_get_name (b); return gvc_name_collate (namea, nameb); } /** * gvc_mixer_control_get_streams: * @control: * * Returns: (transfer container) (element-type Gvc.MixerStream): */ GSList * gvc_mixer_control_get_streams (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->all_streams, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sinks: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSink): */ GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sinks, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sources: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSource): */ GSList * gvc_mixer_control_get_sources (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sources, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sink_inputs: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSinkInput): */ GSList * gvc_mixer_control_get_sink_inputs (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sink_inputs, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_source_outputs: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSourceOutput): */ GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->source_outputs, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } static void dec_outstanding (GvcMixerControl *control) { if (control->priv->n_outstanding <= 0) { return; } if (--control->priv->n_outstanding <= 0) { control->priv->state = GVC_STATE_READY; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY); } } GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); return control->priv->state; } static void on_default_source_port_notify (GObject *object, GParamSpec *pspec, GvcMixerControl *control) { char *port; GvcMixerUIDevice *input; g_object_get (object, "port", &port, NULL); input = gvc_mixer_control_lookup_device_from_stream (control, GVC_MIXER_STREAM (object)); g_debug ("on_default_source_port_notify - moved to port '%s' which SHOULD ?? correspond to output '%s'", port, gvc_mixer_ui_device_get_description (input)); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); g_free (port); } static void _set_default_source (GvcMixerControl *control, GvcMixerStream *stream) { guint new_id; if (stream == NULL) { control->priv->default_source_id = 0; control->priv->default_source_is_set = FALSE; g_signal_emit (control, signals[DEFAULT_SOURCE_CHANGED], 0, PA_INVALID_INDEX); return; } new_id = gvc_mixer_stream_get_id (stream); if (control->priv->default_source_id != new_id) { GvcMixerUIDevice *input; control->priv->default_source_id = new_id; control->priv->default_source_is_set = TRUE; g_signal_emit (control, signals[DEFAULT_SOURCE_CHANGED], 0, new_id); if (control->priv->default_source_is_set) { g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control), on_default_source_port_notify, control); } g_signal_connect (stream, "notify::port", G_CALLBACK (on_default_source_port_notify), control); input = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); } } static void on_default_sink_port_notify (GObject *object, GParamSpec *pspec, GvcMixerControl *control) { char *port; GvcMixerUIDevice *output; g_object_get (object, "port", &port, NULL); output = gvc_mixer_control_lookup_device_from_stream (control, GVC_MIXER_STREAM (object)); if (output != NULL) { g_debug ("on_default_sink_port_notify - moved to port %s - which SHOULD correspond to output %s", port, gvc_mixer_ui_device_get_description (output)); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } g_free (port); } static void _set_default_sink (GvcMixerControl *control, GvcMixerStream *stream) { guint new_id; if (stream == NULL) { /* Don't tell front-ends about an unset default * sink if it's already unset */ if (control->priv->default_sink_is_set == FALSE) return; control->priv->default_sink_id = 0; control->priv->default_sink_is_set = FALSE; g_signal_emit (control, signals[DEFAULT_SINK_CHANGED], 0, PA_INVALID_INDEX); return; } new_id = gvc_mixer_stream_get_id (stream); if (control->priv->default_sink_id != new_id) { GvcMixerUIDevice *output; if (control->priv->default_sink_is_set) { g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control), on_default_sink_port_notify, control); } control->priv->default_sink_id = new_id; control->priv->default_sink_is_set = TRUE; g_signal_emit (control, signals[DEFAULT_SINK_CHANGED], 0, new_id); g_signal_connect (stream, "notify::port", G_CALLBACK (on_default_sink_port_notify), control); output = gvc_mixer_control_lookup_device_from_stream (control, stream); g_debug ("active_sink change"); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } } static gboolean _stream_has_name (gpointer key, GvcMixerStream *stream, const char *name) { const char *t_name; t_name = gvc_mixer_stream_get_name (stream); if (t_name != NULL && name != NULL && strcmp (t_name, name) == 0) { return TRUE; } return FALSE; } static GvcMixerStream * find_stream_for_name (GvcMixerControl *control, const char *name) { GvcMixerStream *stream; stream = g_hash_table_find (control->priv->all_streams, (GHRFunc)_stream_has_name, (char *)name); return stream; } static void update_default_source_from_name (GvcMixerControl *control, const char *name) { gboolean changed = FALSE; if ((control->priv->default_source_name == NULL && name != NULL) || (control->priv->default_source_name != NULL && name == NULL) || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) { changed = TRUE; } if (changed) { GvcMixerStream *stream; g_free (control->priv->default_source_name); control->priv->default_source_name = g_strdup (name); stream = find_stream_for_name (control, name); _set_default_source (control, stream); } } static void update_default_sink_from_name (GvcMixerControl *control, const char *name) { gboolean changed = FALSE; if ((control->priv->default_sink_name == NULL && name != NULL) || (control->priv->default_sink_name != NULL && name == NULL) || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) { changed = TRUE; } if (changed) { GvcMixerStream *stream; g_free (control->priv->default_sink_name); control->priv->default_sink_name = g_strdup (name); stream = find_stream_for_name (control, name); _set_default_sink (control, stream); } } static void update_server (GvcMixerControl *control, const pa_server_info *info) { if (info->default_source_name != NULL) { update_default_source_from_name (control, info->default_source_name); } if (info->default_sink_name != NULL) { g_debug ("update server"); update_default_sink_from_name (control, info->default_sink_name); } } static void remove_stream (GvcMixerControl *control, GvcMixerStream *stream) { guint id; g_object_ref (stream); id = gvc_mixer_stream_get_id (stream); if (id == control->priv->default_sink_id) { _set_default_sink (control, NULL); } else if (id == control->priv->default_source_id) { _set_default_source (control, NULL); } g_hash_table_remove (control->priv->all_streams, GUINT_TO_POINTER (id)); g_signal_emit (G_OBJECT (control), signals[STREAM_REMOVED], 0, gvc_mixer_stream_get_id (stream)); g_object_unref (stream); } static void add_stream (GvcMixerControl *control, GvcMixerStream *stream) { g_hash_table_insert (control->priv->all_streams, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)), stream); g_signal_emit (G_OBJECT (control), signals[STREAM_ADDED], 0, gvc_mixer_stream_get_id (stream)); } /* This method will match individual stream ports against its corresponding device * It does this by: * - iterates through our devices and finds the one where the card-id on the device is the same as the card-id on the stream * and the port-name on the device is the same as the streamport-name. * This should always find a match and is used exclusively by sync_devices(). */ static gboolean match_stream_with_devices (GvcMixerControl *control, GvcMixerStreamPort *stream_port, GvcMixerStream *stream) { GList *devices, *d; guint stream_card_id; guint stream_id; gboolean in_possession = FALSE; stream_id = gvc_mixer_stream_get_id (stream); stream_card_id = gvc_mixer_stream_get_card_index (stream); devices = g_hash_table_get_values (GVC_IS_MIXER_SOURCE (stream) ? control->priv->ui_inputs : control->priv->ui_outputs); for (d = devices; d != NULL; d = d->next) { GvcMixerUIDevice *device; gint device_stream_id; gchar *device_port_name; gchar *origin; gchar *description; GvcMixerCard *card; gint card_id; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &device_stream_id, "card", &card, "origin", &origin, "description", &description, "port-name", &device_port_name, NULL); card_id = gvc_mixer_card_get_index (card); g_debug ("Attempt to match_stream update_with_existing_outputs - Try description : '%s', origin : '%s', device port name : '%s', card : %p, AGAINST stream port: '%s', sink card id %i", description, origin, device_port_name, card, stream_port->port, stream_card_id); if (stream_card_id == card_id && g_strcmp0 (device_port_name, stream_port->port) == 0) { g_debug ("Match device with stream: We have a match with description: '%s', origin: '%s', cached already with device id %u, so set stream id to %i", description, origin, gvc_mixer_ui_device_get_id (device), stream_id); g_object_set (G_OBJECT (device), "stream-id", (gint)stream_id, NULL); in_possession = TRUE; } g_free (device_port_name); g_free (origin); g_free (description); if (in_possession == TRUE) break; } g_list_free (devices); return in_possession; } /* * This method attempts to match a sink or source with its relevant UI device. * GvcMixerStream can represent both a sink or source. * Using static card port introspection implies that we know beforehand what * outputs and inputs are available to the user. * But that does not mean that all of these inputs and outputs are available to be used. * For instance we might be able to see that there is a HDMI port available but if * we are on the default analog stereo output profile there is no valid sink for * that HDMI device. We first need to change profile and when update_sink() is called * only then can we match the new hdmi sink with its corresponding device. * * Firstly it checks to see if the incoming stream has no ports. * - If a stream has no ports but has a valid card ID (bluetooth), it will attempt * to match the device with the stream using the card id. * - If a stream has no ports and no valid card id, it goes ahead and makes a new * device (software/network devices are only detectable at the sink/source level) * If the stream has ports it will match each port against the stream using match_stream_with_devices(). * * This method should always find a match. */ static void sync_devices (GvcMixerControl *control, GvcMixerStream* stream) { /* Go through ports to see what outputs can be created. */ const GList *stream_ports; const GList *n = NULL; gboolean is_output = !GVC_IS_MIXER_SOURCE (stream); gint stream_port_count = 0; stream_ports = gvc_mixer_stream_get_ports (stream); if (stream_ports == NULL) { GvcMixerUIDevice *device; /* Bluetooth, no ports but a valid card */ if (gvc_mixer_stream_get_card_index (stream) != PA_INVALID_INDEX) { GList *devices, *d; gboolean in_possession = FALSE; devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *card; gint card_id; device = d->data; g_object_get (G_OBJECT (device), "card", &card, NULL); card_id = gvc_mixer_card_get_index (card); g_debug ("sync devices, device description - '%s', device card id - %i, stream description - %s, stream card id - %i", gvc_mixer_ui_device_get_description (device), card_id, gvc_mixer_stream_get_description (stream), gvc_mixer_stream_get_card_index (stream)); if (card_id == gvc_mixer_stream_get_card_index (stream)) { in_possession = TRUE; break; } } g_list_free (devices); if (!in_possession) { g_warning ("Couldn't match the portless stream (with card) - '%s' is it an input ? -> %i, streams card id -> %i", gvc_mixer_stream_get_description (stream), GVC_IS_MIXER_SOURCE (stream), gvc_mixer_stream_get_card_index (stream)); return; } g_object_set (G_OBJECT (device), "stream-id", (gint)gvc_mixer_stream_get_id (stream), "description", gvc_mixer_stream_get_description (stream), "origin", "", /*Leave it empty for these special cases*/ "port-name", NULL, "port-available", TRUE, NULL); } else { /* Network sink/source has no ports and no card. */ GObject *object; object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "stream-id", (gint)gvc_mixer_stream_get_id (stream), "description", gvc_mixer_stream_get_description (stream), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", TRUE, NULL); device = GVC_MIXER_UI_DEVICE (object); g_hash_table_insert (is_output ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)), g_object_ref (device)); } g_signal_emit (G_OBJECT (control), signals[is_output ? OUTPUT_ADDED : INPUT_ADDED], 0, gvc_mixer_ui_device_get_id (device)); return; } /* Go ahead and make sure to match each port against a previously created device */ for (n = stream_ports; n != NULL; n = n->next) { GvcMixerStreamPort *stream_port; stream_port = n->data; stream_port_count ++; if (match_stream_with_devices (control, stream_port, stream)) continue; g_warning ("Sync_devices: Failed to match stream id: %u, description: '%s', origin: '%s'", gvc_mixer_stream_get_id (stream), stream_port->human_port, gvc_mixer_stream_get_description (stream)); } } static void set_icon_name_from_proplist (GvcMixerStream *stream, pa_proplist *l, const char *default_icon_name) { const char *t; if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) { if (strcmp (t, "video") == 0 || strcmp (t, "phone") == 0) { goto finish; } if (strcmp (t, "music") == 0) { t = "audio"; goto finish; } if (strcmp (t, "game") == 0) { t = "applications-games"; goto finish; } if (strcmp (t, "event") == 0) { t = "dialog-information"; goto finish; } } t = default_icon_name; finish: gvc_mixer_stream_set_icon_name (stream, t); } /* * Called when anything changes with a sink. */ static void update_sink (GvcMixerControl *control, const pa_sink_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; GvcChannelMap *map; char map_buff[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map); #if 1 g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'", info->index, info->name, info->description, map_buff); #endif map = NULL; is_new = FALSE; stream = g_hash_table_lookup (control->priv->sinks, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GList *list = NULL; guint i; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_sink_new (control->priv->pa_context, info->index, map); for (i = 0; i < info->n_ports; i++) { GvcMixerStreamPort *port; port = g_new0 (GvcMixerStreamPort, 1); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; port->available = info->ports[i]->available != PA_PORT_AVAILABLE_NO; list = g_list_prepend (list, port); } gvc_mixer_stream_set_ports (stream, list); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, info->name); gvc_mixer_stream_set_card_index (stream, info->card); gvc_mixer_stream_set_description (stream, info->description); set_icon_name_from_proplist (stream, info->proplist, "audio-card"); gvc_mixer_stream_set_sysfs_path (stream, pa_proplist_gets (info->proplist, "sysfs.path")); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME)); gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume); /* Messy I know but to set the port everytime regardless of whether it has changed will cost us a * port change notify signal which causes the frontend to resync. * Only update the UI when something has changed. */ if (info->active_port != NULL) { if (is_new) gvc_mixer_stream_set_port (stream, info->active_port->name); else { const GvcMixerStreamPort *active_port; active_port = gvc_mixer_stream_get_port (stream); if (active_port == NULL || g_strcmp0 (active_port->port, info->active_port->name) != 0) { g_debug ("update sink - apparently a port update"); gvc_mixer_stream_set_port (stream, info->active_port->name); } } } if (is_new) { g_debug ("update sink - is new"); g_hash_table_insert (control->priv->sinks, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); /* Always sink on a new stream to able to assign the right stream id * to the appropriate outputs (multiple potential outputs per stream). */ sync_devices (control, stream); } /* * When we change profile on a device that is not the server default sink, * it will jump back to the default sink set by the server to prevent the audio setup from being 'outputless'. * All well and good but then when we get the new stream created for the new profile how do we know * that this is the intended default or selected device the user wishes to use. * This is messy but it's the only reliable way that it can be done without ripping the whole thing apart. */ if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) { GvcMixerUIDevice *dev = NULL; dev = gvc_mixer_control_lookup_output_id (control, control->priv->profile_swapping_device_id); if (dev != NULL) { /* now check to make sure this new stream is the same stream just matched and set on the device object */ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) { g_debug ("Looks like we profile swapped on a non server default sink"); gvc_mixer_control_set_default_sink (control, stream); } } control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; } if (control->priv->default_sink_name != NULL && info->name != NULL && strcmp (control->priv->default_sink_name, info->name) == 0) { _set_default_sink (control, stream); } if (map == NULL) map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream); gvc_channel_map_volume_changed (map, &info->volume, FALSE); } static void update_source (GvcMixerControl *control, const pa_source_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; #if 1 g_debug ("Updating source: index=%u name='%s' description='%s'", info->index, info->name, info->description); #endif /* completely ignore monitors, they're not real sources */ if (info->monitor_of_sink != PA_INVALID_INDEX) { return; } is_new = FALSE; stream = g_hash_table_lookup (control->priv->sources, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GList *list = NULL; guint i; GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_source_new (control->priv->pa_context, info->index, map); for (i = 0; i < info->n_ports; i++) { GvcMixerStreamPort *port; port = g_new0 (GvcMixerStreamPort, 1); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; list = g_list_prepend (list, port); } gvc_mixer_stream_set_ports (stream, list); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, info->name); gvc_mixer_stream_set_card_index (stream, info->card); gvc_mixer_stream_set_description (stream, info->description); set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone"); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME)); gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume); g_debug ("update source"); if (info->active_port != NULL) { if (is_new) gvc_mixer_stream_set_port (stream, info->active_port->name); else { const GvcMixerStreamPort *active_port; active_port = gvc_mixer_stream_get_port (stream); if (active_port == NULL || g_strcmp0 (active_port->port, info->active_port->name) != 0) { g_debug ("update source - apparently a port update"); gvc_mixer_stream_set_port (stream, info->active_port->name); } } } if (is_new) { g_hash_table_insert (control->priv->sources, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); sync_devices (control, stream); } if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) { GvcMixerUIDevice *dev = NULL; dev = gvc_mixer_control_lookup_input_id (control, control->priv->profile_swapping_device_id); if (dev != NULL) { /* now check to make sure this new stream is the same stream just matched and set on the device object */ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) { g_debug ("Looks like we profile swapped on a non server default sink"); gvc_mixer_control_set_default_source (control, stream); } } control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; } if (control->priv->default_source_name != NULL && info->name != NULL && strcmp (control->priv->default_source_name, info->name) == 0) { _set_default_source (control, stream); } } static void set_is_event_stream_from_proplist (GvcMixerStream *stream, pa_proplist *l) { const char *t; gboolean is_event_stream; is_event_stream = FALSE; if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) { if (g_str_equal (t, "event")) is_event_stream = TRUE; } gvc_mixer_stream_set_is_event_stream (stream, is_event_stream); } static void set_application_id_from_proplist (GvcMixerStream *stream, pa_proplist *l) { const char *t; if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) { gvc_mixer_stream_set_application_id (stream, t); } } static void update_sink_input (GvcMixerControl *control, const pa_sink_input_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; const char *name; #if 0 g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u", info->index, info->name, info->client, info->sink); #endif is_new = FALSE; stream = g_hash_table_lookup (control->priv->sink_inputs, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_sink_input_new (control->priv->pa_context, info->index, map); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); name = (const char *)g_hash_table_lookup (control->priv->clients, GUINT_TO_POINTER (info->client)); gvc_mixer_stream_set_name (stream, name); gvc_mixer_stream_set_description (stream, info->name); set_application_id_from_proplist (stream, info->proplist); set_is_event_stream_from_proplist (stream, info->proplist); set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia"); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX); if (is_new) { g_hash_table_insert (control->priv->sink_inputs, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); } } static void update_source_output (GvcMixerControl *control, const pa_source_output_info *info) { GvcMixerStream *stream; gboolean is_new; const char *name; #if 1 g_debug ("Updating source output: index=%u name='%s' client=%u source=%u", info->index, info->name, info->client, info->source); #endif is_new = FALSE; stream = g_hash_table_lookup (control->priv->source_outputs, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_source_output_new (control->priv->pa_context, info->index, map); g_object_unref (map); is_new = TRUE; } name = (const char *)g_hash_table_lookup (control->priv->clients, GUINT_TO_POINTER (info->client)); gvc_mixer_stream_set_name (stream, name); gvc_mixer_stream_set_description (stream, info->name); set_application_id_from_proplist (stream, info->proplist); set_is_event_stream_from_proplist (stream, info->proplist); set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone"); if (is_new) { g_hash_table_insert (control->priv->source_outputs, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); } } static void update_client (GvcMixerControl *control, const pa_client_info *info) { #if 1 g_debug ("Updating client: index=%u name='%s'", info->index, info->name); #endif g_hash_table_insert (control->priv->clients, GUINT_TO_POINTER (info->index), g_strdup (info->name)); } static char * card_num_streams_to_status (guint sinks, guint sources) { char *sinks_str; char *sources_str; char *ret; if (sinks == 0 && sources == 0) { /* translators: * The device has been disabled */ return g_strdup (_("Disabled")); } if (sinks == 0) { sinks_str = NULL; } else { /* translators: * The number of sound outputs on a particular device */ sinks_str = g_strdup_printf (ngettext ("%u Output", "%u Outputs", sinks), sinks); } if (sources == 0) { sources_str = NULL; } else { /* translators: * The number of sound inputs on a particular device */ sources_str = g_strdup_printf (ngettext ("%u Input", "%u Inputs", sources), sources); } if (sources_str == NULL) return sinks_str; if (sinks_str == NULL) return sources_str; ret = g_strdup_printf ("%s / %s", sinks_str, sources_str); g_free (sinks_str); g_free (sources_str); return ret; } // A utility method to gather which card profiles are relevant to the port . static GList * determine_profiles_for_port (pa_card_port_info *port, GList* card_profiles) { gint i; GList *supported_profiles = NULL; GList *p; for (i = 0; i < port->n_profiles; i++) { for (p = card_profiles; p != NULL; p = p->next) { GvcMixerCardProfile *prof; prof = p->data; if (g_strcmp0 (port->profiles[i]->name, prof->profile) == 0) supported_profiles = g_list_append (supported_profiles, prof); } } g_debug ("%i profiles supported on port %s", g_list_length (supported_profiles), port->description); return g_list_sort (supported_profiles, (GCompareFunc) gvc_mixer_card_profile_compare); } static gboolean is_card_port_an_output (GvcMixerCardPort* port) { return port->direction == PA_DIRECTION_OUTPUT ? TRUE : FALSE; } /* * This method will create a ui device for the given port. */ static void create_ui_device_from_port (GvcMixerControl* control, GvcMixerCardPort* port, GvcMixerCard* card) { GvcMixerUIDeviceDirection direction; GObject *object; GvcMixerUIDevice *uidevice; gboolean available = port->available != PA_PORT_AVAILABLE_NO; direction = (is_card_port_an_output (port) == TRUE) ? UIDeviceOutput : UIDeviceInput; object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", (uint)direction, "card", card, "port-name", port->port, "description", port->human_port, "origin", gvc_mixer_card_get_name (card), "port-available", available, NULL); uidevice = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (uidevice, port->profiles); g_hash_table_insert (is_card_port_an_output (port) ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (uidevice)), g_object_ref (uidevice)); if (available) { g_signal_emit (G_OBJECT (control), signals[is_card_port_an_output (port) ? OUTPUT_ADDED : INPUT_ADDED], 0, gvc_mixer_ui_device_get_id (uidevice)); } g_debug ("create_ui_device_from_port, direction %u, description '%s', origin '%s', port available %i", direction, port->human_port, gvc_mixer_card_get_name (card), available); } /* * This method will match up GvcMixerCardPorts with existing devices. * A match is achieved if the device's card-id and the port's card-id are the same * && the device's port-name and the card-port's port member are the same. * A signal is then sent adding or removing that device from the UI depending on the availability of the port. */ static void match_card_port_with_existing_device (GvcMixerControl *control, GvcMixerCardPort *card_port, GvcMixerCard *card, gboolean available) { GList *d; GList *devices; GvcMixerUIDevice *device; gboolean is_output = is_card_port_an_output (card_port); devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *device_card; gchar *device_port_name; device = d->data; g_object_get (G_OBJECT (device), "card", &device_card, "port-name", &device_port_name, NULL); if (g_strcmp0 (card_port->port, device_port_name) == 0 && device_card == card) { g_debug ("Found the relevant device %s, update its port availability flag to %i, is_output %i", device_port_name, available, is_output); g_object_set (G_OBJECT (device), "port-available", available, NULL); g_signal_emit (G_OBJECT (control), is_output ? signals[available ? OUTPUT_ADDED : OUTPUT_REMOVED] : signals[available ? INPUT_ADDED : INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } g_free (device_port_name); } g_list_free (devices); } static void create_ui_device_from_card (GvcMixerControl *control, GvcMixerCard *card) { GObject *object; GvcMixerUIDevice *in; GvcMixerUIDevice *out; const GList *profiles; /* For now just create two devices and presume this device is multi directional * Ensure to remove both on card removal (available to false by default) */ profiles = gvc_mixer_card_get_profiles (card); g_debug ("Portless card just registered - %i", gvc_mixer_card_get_index (card)); object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", UIDeviceInput, "description", gvc_mixer_card_get_name (card), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", FALSE, "card", card, NULL); in = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (in, profiles); g_hash_table_insert (control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (in)), g_object_ref (in)); object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", UIDeviceOutput, "description", gvc_mixer_card_get_name (card), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", FALSE, "card", card, NULL); out = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (out, profiles); g_hash_table_insert (control->priv->ui_outputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (out)), g_object_ref (out)); } /* * At this point we can determine all devices available to us (besides network 'ports') * This is done by the following: * * - gvc_mixer_card and gvc_mixer_card_ports are created and relevant setters are called. * - First it checks to see if it's a portless card. Bluetooth devices are portless AFAIHS. * If so it creates two devices, an input and an output. * - If it's a 'normal' card with ports it will create a new ui-device or * synchronise port availability with the existing device cached for that port on this card. */ static void update_card (GvcMixerControl *control, const pa_card_info *info) { const GList *card_ports = NULL; const GList *m = NULL; GvcMixerCard *card; gboolean is_new = FALSE; #if 1 guint i; const char *key; void *state; g_debug ("Udpating card %s (index: %u driver: %s):", info->name, info->index, info->driver); for (i = 0; i < info->n_profiles; i++) { struct pa_card_profile_info pi = info->profiles[i]; gboolean is_default; is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0); g_debug ("\tProfile '%s': %d sources %d sinks%s", pi.name, pi.n_sources, pi.n_sinks, is_default ? " (Current)" : ""); } state = NULL; key = pa_proplist_iterate (info->proplist, &state); while (key != NULL) { g_debug ("\tProperty: '%s' = '%s'", key, pa_proplist_gets (info->proplist, key)); key = pa_proplist_iterate (info->proplist, &state); } #endif card = g_hash_table_lookup (control->priv->cards, GUINT_TO_POINTER (info->index)); if (card == NULL) { GList *profile_list = NULL; GList *port_list = NULL; for (i = 0; i < info->n_profiles; i++) { GvcMixerCardProfile *profile; struct pa_card_profile_info pi = info->profiles[i]; profile = g_new0 (GvcMixerCardProfile, 1); profile->profile = g_strdup (pi.name); profile->human_profile = g_strdup (pi.description); profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources); profile->n_sinks = pi.n_sinks; profile->n_sources = pi.n_sources; profile->priority = pi.priority; profile_list = g_list_prepend (profile_list, profile); } card = gvc_mixer_card_new (control->priv->pa_context, info->index); gvc_mixer_card_set_profiles (card, profile_list); for (i = 0; i < info->n_ports; i++) { GvcMixerCardPort *port; port = g_new0 (GvcMixerCardPort, 1); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; port->available = info->ports[i]->available; port->direction = info->ports[i]->direction; port->profiles = determine_profiles_for_port (info->ports[i], profile_list); port_list = g_list_prepend (port_list, port); } gvc_mixer_card_set_ports (card, port_list); is_new = TRUE; } gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description")); gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name")); gvc_mixer_card_set_profile (card, info->active_profile->name); if (is_new) { g_hash_table_insert (control->priv->cards, GUINT_TO_POINTER (info->index), g_object_ref (card)); } card_ports = gvc_mixer_card_get_ports (card); if (card_ports == NULL && is_new) { g_debug ("Portless card just registered - %s", gvc_mixer_card_get_name (card)); create_ui_device_from_card (control, card); } for (m = card_ports; m != NULL; m = m->next) { GvcMixerCardPort *card_port; card_port = m->data; if (is_new) create_ui_device_from_port (control, card_port, card); else { for (i = 0; i < info->n_ports; i++) { if (g_strcmp0 (card_port->port, info->ports[i]->name) == 0) { if ((card_port->available == PA_PORT_AVAILABLE_NO) != (info->ports[i]->available == PA_PORT_AVAILABLE_NO)) { card_port->available = info->ports[i]->available; g_debug ("sync port availability on card %i, card port name '%s', new available value %i", gvc_mixer_card_get_index (card), card_port->port, card_port->available); match_card_port_with_existing_device (control, card_port, card, card_port->available != PA_PORT_AVAILABLE_NO); } } } } } g_signal_emit (G_OBJECT (control), signals[CARD_ADDED], 0, info->index); } static void _pa_context_get_sink_info_cb (pa_context *context, const pa_sink_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Sink callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_sink (control, i); } static void _pa_context_get_source_info_cb (pa_context *context, const pa_source_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Source callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_source (control, i); } static void _pa_context_get_sink_input_info_cb (pa_context *context, const pa_sink_input_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Sink input callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_sink_input (control, i); } static void _pa_context_get_source_output_info_cb (pa_context *context, const pa_source_output_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Source output callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_source_output (control, i); } static void _pa_context_get_client_info_cb (pa_context *context, const pa_client_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Client callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_client (control, i); } static void _pa_context_get_card_info_by_index_cb (pa_context *context, const pa_card_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) return; g_warning ("Card callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_card (control, i); } static void _pa_context_get_server_info_cb (pa_context *context, const pa_server_info *i, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (i == NULL) { g_warning ("Server info callback failure"); return; } g_debug ("get server info"); update_server (control, i); dec_outstanding (control); } static void remove_event_role_stream (GvcMixerControl *control) { g_debug ("Removing event role"); } static void update_event_role_stream (GvcMixerControl *control, const pa_ext_stream_restore_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; if (strcmp (info->name, "sink-input-by-media-role:event") != 0) { return; } #if 0 g_debug ("Updating event role: name='%s' device='%s'", info->name, info->device); #endif is_new = FALSE; if (!control->priv->event_sink_input_is_set) { pa_channel_map pa_map; GvcChannelMap *map; pa_map.channels = 1; pa_map.map[0] = PA_CHANNEL_POSITION_MONO; map = gvc_channel_map_new_from_pa_channel_map (&pa_map); stream = gvc_mixer_event_role_new (control->priv->pa_context, info->device, map); control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream); control->priv->event_sink_input_is_set = TRUE; is_new = TRUE; } else { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->event_sink_input_id)); } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, _("System Sounds")); gvc_mixer_stream_set_icon_name (stream, "cin-multimedia-volume-control"); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); if (is_new) { add_stream (control, stream); } } static void _pa_ext_stream_restore_read_cb (pa_context *context, const pa_ext_stream_restore_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { g_debug ("Failed to initialized stream_restore extension: %s", pa_strerror (pa_context_errno (context))); remove_event_role_stream (control); return; } if (eol > 0) { dec_outstanding (control); /* If we don't have an event stream to restore, then * set one up with a default 100% volume */ if (!control->priv->event_sink_input_is_set) { pa_ext_stream_restore_info info; memset (&info, 0, sizeof(info)); info.name = "sink-input-by-media-role:event"; info.volume.channels = 1; info.volume.values[0] = PA_VOLUME_NORM; update_event_role_stream (control, &info); } return; } update_event_role_stream (control, i); } static void _pa_ext_stream_restore_subscribe_cb (pa_context *context, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); pa_operation *o; o = pa_ext_stream_restore_read (context, _pa_ext_stream_restore_read_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed"); return; } pa_operation_unref (o); } static void req_update_server_info (GvcMixerControl *control, int index) { pa_operation *o; o = pa_context_get_server_info (control->priv->pa_context, _pa_context_get_server_info_cb, control); if (o == NULL) { g_warning ("pa_context_get_server_info() failed"); return; } pa_operation_unref (o); } static void req_update_client_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_client_info_list (control->priv->pa_context, _pa_context_get_client_info_cb, control); } else { o = pa_context_get_client_info (control->priv->pa_context, index, _pa_context_get_client_info_cb, control); } if (o == NULL) { g_warning ("pa_context_client_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_card (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_card_info_list (control->priv->pa_context, _pa_context_get_card_info_by_index_cb, control); } else { o = pa_context_get_card_info_by_index (control->priv->pa_context, index, _pa_context_get_card_info_by_index_cb, control); } if (o == NULL) { g_warning ("pa_context_get_card_info_by_index() failed"); return; } pa_operation_unref (o); } static void req_update_sink_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_sink_info_list (control->priv->pa_context, _pa_context_get_sink_info_cb, control); } else { o = pa_context_get_sink_info_by_index (control->priv->pa_context, index, _pa_context_get_sink_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_sink_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_source_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_source_info_list (control->priv->pa_context, _pa_context_get_source_info_cb, control); } else { o = pa_context_get_source_info_by_index(control->priv->pa_context, index, _pa_context_get_source_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_source_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_sink_input_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_sink_input_info_list (control->priv->pa_context, _pa_context_get_sink_input_info_cb, control); } else { o = pa_context_get_sink_input_info (control->priv->pa_context, index, _pa_context_get_sink_input_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_sink_input_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_source_output_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_source_output_info_list (control->priv->pa_context, _pa_context_get_source_output_info_cb, control); } else { o = pa_context_get_source_output_info (control->priv->pa_context, index, _pa_context_get_source_output_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_source_output_info_list() failed"); return; } pa_operation_unref (o); } static void remove_client (GvcMixerControl *control, guint index) { g_hash_table_remove (control->priv->clients, GUINT_TO_POINTER (index)); } static void remove_card (GvcMixerControl *control, guint index) { GList *devices, *d; devices = g_list_concat (g_hash_table_get_values (control->priv->ui_inputs), g_hash_table_get_values (control->priv->ui_outputs)); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *card; GvcMixerUIDevice *device = d->data; g_object_get (G_OBJECT (device), "card", &card, NULL); if (gvc_mixer_card_get_index (card) == index) { g_signal_emit (G_OBJECT (control), signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); g_debug ("Card removal remove device %s", gvc_mixer_ui_device_get_description (device)); g_hash_table_remove (gvc_mixer_ui_device_is_output (device) ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device))); } } g_list_free (devices); g_hash_table_remove (control->priv->cards, GUINT_TO_POINTER (index)); g_signal_emit (G_OBJECT (control), signals[CARD_REMOVED], 0, index); } static void remove_sink (GvcMixerControl *control, guint index) { GvcMixerStream *stream; GvcMixerUIDevice *device; g_debug ("Removing sink: index=%u", index); stream = g_hash_table_lookup (control->priv->sinks, GUINT_TO_POINTER (index)); if (stream == NULL) return; device = gvc_mixer_control_lookup_device_from_stream (control, stream); if (device != NULL) { gvc_mixer_ui_device_invalidate_stream (device); if (!gvc_mixer_ui_device_has_ports (device)) { g_signal_emit (G_OBJECT (control), signals[OUTPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } else { GList *devices, *d; devices = g_hash_table_get_values (control->priv->ui_outputs); for (d = devices; d != NULL; d = d->next) { gint stream_id = GVC_MIXER_UI_DEVICE_INVALID; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (stream_id == gvc_mixer_stream_get_id (stream)) gvc_mixer_ui_device_invalidate_stream (device); } g_list_free (devices); } } g_hash_table_remove (control->priv->sinks, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_source (GvcMixerControl *control, guint index) { GvcMixerStream *stream; GvcMixerUIDevice *device; g_debug ("Removing source: index=%u", index); stream = g_hash_table_lookup (control->priv->sources, GUINT_TO_POINTER (index)); if (stream == NULL) return; device = gvc_mixer_control_lookup_device_from_stream (control, stream); if (device != NULL) { gvc_mixer_ui_device_invalidate_stream (device); if (!gvc_mixer_ui_device_has_ports (device)) { g_signal_emit (G_OBJECT (control), signals[INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } else { GList *devices, *d; devices = g_hash_table_get_values (control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { gint stream_id = GVC_MIXER_UI_DEVICE_INVALID; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (stream_id == gvc_mixer_stream_get_id (stream)) gvc_mixer_ui_device_invalidate_stream (device); } g_list_free (devices); } } g_hash_table_remove (control->priv->sources, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_sink_input (GvcMixerControl *control, guint index) { GvcMixerStream *stream; g_debug ("Removing sink input: index=%u", index); stream = g_hash_table_lookup (control->priv->sink_inputs, GUINT_TO_POINTER (index)); if (stream == NULL) { return; } g_hash_table_remove (control->priv->sink_inputs, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_source_output (GvcMixerControl *control, guint index) { GvcMixerStream *stream; g_debug ("Removing source output: index=%u", index); stream = g_hash_table_lookup (control->priv->source_outputs, GUINT_TO_POINTER (index)); if (stream == NULL) { return; } g_hash_table_remove (control->priv->source_outputs, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void _pa_context_subscribe_cb (pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_sink (control, index); } else { req_update_sink_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_source (control, index); } else { req_update_source_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_sink_input (control, index); } else { req_update_sink_input_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_source_output (control, index); } else { req_update_source_output_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_client (control, index); } else { req_update_client_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SERVER: req_update_server_info (control, index); break; case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_card (control, index); } else { req_update_card (control, index); } break; } } static void gvc_mixer_control_ready (GvcMixerControl *control) { pa_operation *o; pa_context_set_subscribe_callback (control->priv->pa_context, _pa_context_subscribe_cb, control); o = pa_context_subscribe (control->priv->pa_context, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_SERVER| PA_SUBSCRIPTION_MASK_CARD), NULL, NULL); if (o == NULL) { g_warning ("pa_context_subscribe() failed"); return; } pa_operation_unref (o); req_update_server_info (control, -1); req_update_card (control, -1); req_update_client_info (control, -1); req_update_sink_info (control, -1); req_update_source_info (control, -1); req_update_sink_input_info (control, -1); req_update_source_output_info (control, -1); control->priv->n_outstanding = 6; /* This call is not always supported */ o = pa_ext_stream_restore_read (control->priv->pa_context, _pa_ext_stream_restore_read_cb, control); if (o != NULL) { pa_operation_unref (o); control->priv->n_outstanding++; pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context, _pa_ext_stream_restore_subscribe_cb, control); o = pa_ext_stream_restore_subscribe (control->priv->pa_context, 1, NULL, NULL); if (o != NULL) { pa_operation_unref (o); } } else { g_debug ("Failed to initialized stream_restore extension: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); } } static void gvc_mixer_new_pa_context (GvcMixerControl *self) { pa_proplist *proplist; g_return_if_fail (self); g_return_if_fail (!self->priv->pa_context); proplist = pa_proplist_new (); pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, self->priv->name); pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "org.gnome.VolumeControl"); pa_proplist_sets (proplist, PA_PROP_APPLICATION_ICON_NAME, "cin-multimedia-volume-control"); pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist); pa_proplist_free (proplist); g_assert (self->priv->pa_context); } static void remove_all_streams (GvcMixerControl *control, GHashTable *hash_table) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, &key, &value)) { remove_stream (control, value); g_hash_table_iter_remove (&iter); } } static gboolean idle_reconnect (gpointer data) { GvcMixerControl *control = GVC_MIXER_CONTROL (data); GHashTableIter iter; gpointer key, value; g_return_val_if_fail (control, FALSE); if (control->priv->pa_context) { pa_context_unref (control->priv->pa_context); control->priv->pa_context = NULL; gvc_mixer_new_pa_context (control); } remove_all_streams (control, control->priv->sinks); remove_all_streams (control, control->priv->sources); remove_all_streams (control, control->priv->sink_inputs); remove_all_streams (control, control->priv->source_outputs); g_hash_table_iter_init (&iter, control->priv->clients); while (g_hash_table_iter_next (&iter, &key, &value)) g_hash_table_iter_remove (&iter); gvc_mixer_control_open (control); /* cannot fail */ control->priv->reconnect_id = 0; return FALSE; } static void _pa_context_state_cb (pa_context *context, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); switch (pa_context_get_state (context)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: gvc_mixer_control_ready (control); break; case PA_CONTEXT_FAILED: control->priv->state = GVC_STATE_FAILED; g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED); if (control->priv->reconnect_id == 0) control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control); break; case PA_CONTEXT_TERMINATED: default: /* FIXME: */ break; } } gboolean gvc_mixer_control_open (GvcMixerControl *control) { int res; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (control->priv->pa_context != NULL, FALSE); g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE); pa_context_set_state_callback (control->priv->pa_context, _pa_context_state_cb, control); control->priv->state = GVC_STATE_CONNECTING; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING); res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL); if (res < 0) { g_warning ("Failed to connect context: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); } return res; } gboolean gvc_mixer_control_close (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (control->priv->pa_context != NULL, FALSE); pa_context_disconnect (control->priv->pa_context); control->priv->state = GVC_STATE_CLOSED; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED); return TRUE; } static void gvc_mixer_control_dispose (GObject *object) { GvcMixerControl *control = GVC_MIXER_CONTROL (object); if (control->priv->reconnect_id != 0) { g_source_remove (control->priv->reconnect_id); control->priv->reconnect_id = 0; } if (control->priv->pa_context != NULL) { pa_context_unref (control->priv->pa_context); control->priv->pa_context = NULL; } if (control->priv->default_source_name != NULL) { g_free (control->priv->default_source_name); control->priv->default_source_name = NULL; } if (control->priv->default_sink_name != NULL) { g_free (control->priv->default_sink_name); control->priv->default_sink_name = NULL; } if (control->priv->pa_mainloop != NULL) { pa_glib_mainloop_free (control->priv->pa_mainloop); control->priv->pa_mainloop = NULL; } if (control->priv->all_streams != NULL) { g_hash_table_destroy (control->priv->all_streams); control->priv->all_streams = NULL; } if (control->priv->sinks != NULL) { g_hash_table_destroy (control->priv->sinks); control->priv->sinks = NULL; } if (control->priv->sources != NULL) { g_hash_table_destroy (control->priv->sources); control->priv->sources = NULL; } if (control->priv->sink_inputs != NULL) { g_hash_table_destroy (control->priv->sink_inputs); control->priv->sink_inputs = NULL; } if (control->priv->source_outputs != NULL) { g_hash_table_destroy (control->priv->source_outputs); control->priv->source_outputs = NULL; } if (control->priv->clients != NULL) { g_hash_table_destroy (control->priv->clients); control->priv->clients = NULL; } if (control->priv->cards != NULL) { g_hash_table_destroy (control->priv->cards); control->priv->cards = NULL; } if (control->priv->ui_outputs != NULL) { g_hash_table_destroy (control->priv->ui_outputs); control->priv->ui_outputs = NULL; } if (control->priv->ui_inputs != NULL) { g_hash_table_destroy (control->priv->ui_inputs); control->priv->ui_inputs = NULL; } G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object); } static void gvc_mixer_control_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerControl *self = GVC_MIXER_CONTROL (object); switch (prop_id) { case PROP_NAME: g_free (self->priv->name); self->priv->name = g_value_dup_string (value); g_object_notify (G_OBJECT (self), "name"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_control_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerControl *self = GVC_MIXER_CONTROL (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, self->priv->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_control_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerControl *self; object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_CONTROL (object); gvc_mixer_new_pa_context (self); self->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; return object; } static void gvc_mixer_control_class_init (GvcMixerControlClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gvc_mixer_control_constructor; object_class->dispose = gvc_mixer_control_dispose; object_class->finalize = gvc_mixer_control_finalize; object_class->set_property = gvc_mixer_control_set_property; object_class->get_property = gvc_mixer_control_get_property; g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this mixer control", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); signals [STATE_CHANGED] = g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, state_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [STREAM_ADDED] = g_signal_new ("stream-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, stream_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [STREAM_REMOVED] = g_signal_new ("stream-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [CARD_ADDED] = g_signal_new ("card-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, card_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [CARD_REMOVED] = g_signal_new ("card-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, card_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [DEFAULT_SINK_CHANGED] = g_signal_new ("default-sink-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [DEFAULT_SOURCE_CHANGED] = g_signal_new ("default-source-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [ACTIVE_OUTPUT_UPDATE] = g_signal_new ("active-output-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [ACTIVE_INPUT_UPDATE] = g_signal_new ("active-input-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [OUTPUT_ADDED] = g_signal_new ("output-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, output_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [INPUT_ADDED] = g_signal_new ("input-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, input_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [OUTPUT_REMOVED] = g_signal_new ("output-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, output_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [INPUT_REMOVED] = g_signal_new ("input-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, input_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate)); } static void gvc_mixer_control_init (GvcMixerControl *control) { control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control); control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ()); g_assert (control->priv->pa_mainloop); control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop); g_assert (control->priv->pa_api); control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->ui_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->ui_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free); control->priv->state = GVC_STATE_CLOSED; } static void gvc_mixer_control_finalize (GObject *object) { GvcMixerControl *mixer_control; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_CONTROL (object)); mixer_control = GVC_MIXER_CONTROL (object); g_free (mixer_control->priv->name); mixer_control->priv->name = NULL; g_return_if_fail (mixer_control->priv != NULL); G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object); } GvcMixerControl * gvc_mixer_control_new (const char *name) { GObject *control; control = g_object_new (GVC_TYPE_MIXER_CONTROL, "name", name, NULL); return GVC_MIXER_CONTROL (control); } gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control) { return (gdouble) PA_VOLUME_NORM; } gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control) { return (gdouble) PA_VOLUME_UI_MAX; } Cinnamon-2.8.6/src/gvc/gvc-mixer-sink.c0000664000175000017500000001334312625666430016610 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-sink.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SINK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkPrivate)) struct GvcMixerSinkPrivate { gpointer dummy; }; static void gvc_mixer_sink_class_init (GvcMixerSinkClass *klass); static void gvc_mixer_sink_init (GvcMixerSink *mixer_sink); static void gvc_mixer_sink_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); /* set the volume */ cv = gvc_channel_map_get_cvolume(map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_volume_by_index (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_sink_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_mute_by_index (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static gboolean gvc_mixer_sink_change_port (GvcMixerStream *stream, const char *port) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_port_by_index (context, index, port, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_port_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_sink_class_init (GvcMixerSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_sink_finalize; stream_class->push_volume = gvc_mixer_sink_push_volume; stream_class->change_port = gvc_mixer_sink_change_port; stream_class->change_is_muted = gvc_mixer_sink_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate)); } static void gvc_mixer_sink_init (GvcMixerSink *sink) { sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink); } static void gvc_mixer_sink_finalize (GObject *object) { GvcMixerSink *mixer_sink; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SINK (object)); mixer_sink = GVC_MIXER_SINK (object); g_return_if_fail (mixer_sink->priv != NULL); G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->finalize (object); } /** * gvc_mixer_sink_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_sink_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SINK, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } Cinnamon-2.8.6/src/gvc/gvc-channel-map.h0000664000175000017500000000557312625666430016720 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_CHANNEL_MAP_H #define __GVC_CHANNEL_MAP_H #include #include G_BEGIN_DECLS #define GVC_TYPE_CHANNEL_MAP (gvc_channel_map_get_type ()) #define GVC_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMap)) #define GVC_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass)) #define GVC_IS_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_MAP)) #define GVC_IS_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_MAP)) #define GVC_CHANNEL_MAP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass)) typedef struct GvcChannelMapPrivate GvcChannelMapPrivate; typedef struct { GObject parent; GvcChannelMapPrivate *priv; } GvcChannelMap; typedef struct { GObjectClass parent_class; void (*volume_changed) (GvcChannelMap *channel_map, gboolean set); } GvcChannelMapClass; enum { VOLUME, BALANCE, FADE, LFE, NUM_TYPES }; GType gvc_channel_map_get_type (void); GvcChannelMap * gvc_channel_map_new (void); guint gvc_channel_map_get_num_channels (const GvcChannelMap *map); const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map); gboolean gvc_channel_map_can_balance (const GvcChannelMap *map); gboolean gvc_channel_map_can_fade (const GvcChannelMap *map); gboolean gvc_channel_map_has_position (const GvcChannelMap *map, pa_channel_position_t position); #define gvc_channel_map_has_lfe(x) gvc_channel_map_has_position (x, PA_CHANNEL_POSITION_LFE) const char * gvc_channel_map_get_mapping (const GvcChannelMap *map); G_END_DECLS #endif /* __GVC_CHANNEL_MAP_H */ Cinnamon-2.8.6/src/gvc/gvc-channel-map.c0000664000175000017500000001661712625666430016714 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-channel-map.h" #include "gvc-channel-map-private.h" #define GVC_CHANNEL_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapPrivate)) struct GvcChannelMapPrivate { pa_channel_map pa_map; gboolean pa_volume_is_set; pa_cvolume pa_volume; gdouble extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */ gboolean can_balance; gboolean can_fade; }; enum { VOLUME_CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void gvc_channel_map_class_init (GvcChannelMapClass *klass); static void gvc_channel_map_init (GvcChannelMap *channel_map); static void gvc_channel_map_finalize (GObject *object); G_DEFINE_TYPE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT) guint gvc_channel_map_get_num_channels (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0); if (!pa_channel_map_valid(&map->priv->pa_map)) return 0; return map->priv->pa_map.channels; } const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume); if (gvc_channel_map_can_balance (map)) map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map); else map->priv->extern_volume[BALANCE] = 0; if (gvc_channel_map_can_fade (map)) map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map); else map->priv->extern_volume[FADE] = 0; if (gvc_channel_map_has_lfe (map)) map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE); else map->priv->extern_volume[LFE] = 0; return map->priv->extern_volume; } gboolean gvc_channel_map_can_balance (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return map->priv->can_balance; } gboolean gvc_channel_map_can_fade (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return map->priv->can_fade; } const char * gvc_channel_map_get_mapping (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return pa_channel_map_to_pretty_name (&map->priv->pa_map); } /** * gvc_channel_map_has_position: (skip) * @map: * @position: * * Returns: */ gboolean gvc_channel_map_has_position (const GvcChannelMap *map, pa_channel_position_t position) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return pa_channel_map_has_position (&(map->priv->pa_map), position); } const pa_channel_map * gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return &map->priv->pa_map; } const pa_cvolume * gvc_channel_map_get_cvolume (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return &map->priv->pa_volume; } static void gvc_channel_map_class_init (GvcChannelMapClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = gvc_channel_map_finalize; signals [VOLUME_CHANGED] = g_signal_new ("volume-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); g_type_class_add_private (klass, sizeof (GvcChannelMapPrivate)); } void gvc_channel_map_volume_changed (GvcChannelMap *map, const pa_cvolume *cv, gboolean set) { g_return_if_fail (GVC_IS_CHANNEL_MAP (map)); g_return_if_fail (cv != NULL); g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map)); if (pa_cvolume_equal(cv, &map->priv->pa_volume)) return; map->priv->pa_volume = *cv; if (map->priv->pa_volume_is_set == FALSE) { map->priv->pa_volume_is_set = TRUE; return; } g_signal_emit (map, signals[VOLUME_CHANGED], 0, set); } static void gvc_channel_map_init (GvcChannelMap *map) { map->priv = GVC_CHANNEL_MAP_GET_PRIVATE (map); map->priv->pa_volume_is_set = FALSE; } static void gvc_channel_map_finalize (GObject *object) { GvcChannelMap *channel_map; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_CHANNEL_MAP (object)); channel_map = GVC_CHANNEL_MAP (object); g_return_if_fail (channel_map->priv != NULL); G_OBJECT_CLASS (gvc_channel_map_parent_class)->finalize (object); } GvcChannelMap * gvc_channel_map_new (void) { GObject *map; map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL); return GVC_CHANNEL_MAP (map); } static void set_from_pa_map (GvcChannelMap *map, const pa_channel_map *pa_map) { g_assert (pa_channel_map_valid(pa_map)); map->priv->can_balance = pa_channel_map_can_balance (pa_map); map->priv->can_fade = pa_channel_map_can_fade (pa_map); map->priv->pa_map = *pa_map; pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM); } GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *pa_map) { GObject *map; map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL); set_from_pa_map (GVC_CHANNEL_MAP (map), pa_map); return GVC_CHANNEL_MAP (map); } Cinnamon-2.8.6/src/gvc/gvc-mixer-source-output.h0000664000175000017500000000445212625666430020510 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_SOURCE_OUTPUT_H #define __GVC_MIXER_SOURCE_OUTPUT_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SOURCE_OUTPUT (gvc_mixer_source_output_get_type ()) #define GVC_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutput)) #define GVC_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass)) #define GVC_IS_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT)) #define GVC_IS_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE_OUTPUT)) #define GVC_MIXER_SOURCE_OUTPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass)) typedef struct GvcMixerSourceOutputPrivate GvcMixerSourceOutputPrivate; typedef struct { GvcMixerStream parent; GvcMixerSourceOutputPrivate *priv; } GvcMixerSourceOutput; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSourceOutputClass; GType gvc_mixer_source_output_get_type (void); GvcMixerStream * gvc_mixer_source_output_new (pa_context *context, guint index, GvcChannelMap *map); G_END_DECLS #endif /* __GVC_MIXER_SOURCE_OUTPUT_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-card.c0000664000175000017500000004466512625666430016570 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * Copyright (C) 2009 Bastien Nocera * Copyright (C) Conor Curran 2011 * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-card.h" #include "gvc-mixer-card-private.h" #define GVC_MIXER_CARD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardPrivate)) static guint32 card_serial = 1; struct GvcMixerCardPrivate { pa_context *pa_context; guint id; guint index; char *name; char *icon_name; char *profile; char *target_profile; char *human_profile; GList *profiles; pa_operation *profile_op; GList *ports; }; enum { PROP_0, PROP_ID, PROP_PA_CONTEXT, PROP_INDEX, PROP_NAME, PROP_ICON_NAME, PROP_PROFILE, PROP_HUMAN_PROFILE, }; static void gvc_mixer_card_class_init (GvcMixerCardClass *klass); static void gvc_mixer_card_init (GvcMixerCard *mixer_card); static void gvc_mixer_card_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerCard, gvc_mixer_card, G_TYPE_OBJECT) static guint32 get_next_card_serial (void) { guint32 serial; serial = card_serial++; if ((gint32)card_serial < 0) { card_serial = 1; } return serial; } pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->pa_context; } guint gvc_mixer_card_get_index (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->index; } guint gvc_mixer_card_get_id (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->id; } const char * gvc_mixer_card_get_name (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->name; } gboolean gvc_mixer_card_set_name (GvcMixerCard *card, const char *name) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_free (card->priv->name); card->priv->name = g_strdup (name); g_object_notify (G_OBJECT (card), "name"); return TRUE; } const char * gvc_mixer_card_get_icon_name (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->icon_name; } gboolean gvc_mixer_card_set_icon_name (GvcMixerCard *card, const char *icon_name) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_free (card->priv->icon_name); card->priv->icon_name = g_strdup (icon_name); g_object_notify (G_OBJECT (card), "icon-name"); return TRUE; } /** * gvc_mixer_card_get_profile: (skip) * @card: * * Returns: */ GvcMixerCardProfile * gvc_mixer_card_get_profile (GvcMixerCard *card) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); g_return_val_if_fail (card->priv->profiles != NULL, NULL); for (l = card->priv->profiles; l != NULL; l = l->next) { GvcMixerCardProfile *p = l->data; if (g_str_equal (card->priv->profile, p->profile)) { return p; } } g_assert_not_reached (); return NULL; } gboolean gvc_mixer_card_set_profile (GvcMixerCard *card, const char *profile) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles != NULL, FALSE); g_free (card->priv->profile); card->priv->profile = g_strdup (profile); g_free (card->priv->human_profile); card->priv->human_profile = NULL; for (l = card->priv->profiles; l != NULL; l = l->next) { GvcMixerCardProfile *p = l->data; if (g_str_equal (card->priv->profile, p->profile)) { card->priv->human_profile = g_strdup (p->human_profile); break; } } g_object_notify (G_OBJECT (card), "profile"); return TRUE; } static void _pa_context_set_card_profile_by_index_cb (pa_context *context, int success, void *userdata) { GvcMixerCard *card = GVC_MIXER_CARD (userdata); g_assert (card->priv->target_profile); if (success > 0) { gvc_mixer_card_set_profile (card, card->priv->target_profile); } else { g_debug ("Failed to switch profile on '%s' from '%s' to '%s'", card->priv->name, card->priv->profile, card->priv->target_profile); } g_free (card->priv->target_profile); card->priv->target_profile = NULL; pa_operation_unref (card->priv->profile_op); card->priv->profile_op = NULL; } gboolean gvc_mixer_card_change_profile (GvcMixerCard *card, const char *profile) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles != NULL, FALSE); /* Same profile, or already requested? */ if (g_strcmp0 (card->priv->profile, profile) == 0) return TRUE; if (g_strcmp0 (profile, card->priv->target_profile) == 0) return TRUE; if (card->priv->profile_op != NULL) { pa_operation_cancel (card->priv->profile_op); pa_operation_unref (card->priv->profile_op); card->priv->profile_op = NULL; } if (card->priv->profile != NULL) { g_free (card->priv->target_profile); card->priv->target_profile = g_strdup (profile); card->priv->profile_op = pa_context_set_card_profile_by_index (card->priv->pa_context, card->priv->index, card->priv->target_profile, _pa_context_set_card_profile_by_index_cb, card); if (card->priv->profile_op == NULL) { g_warning ("pa_context_set_card_profile_by_index() failed"); return FALSE; } } else { g_assert (card->priv->human_profile == NULL); card->priv->profile = g_strdup (profile); } return TRUE; } /** * gvc_mixer_card_get_profiles: * * Return value: (transfer none) (element-type GvcMixerCardProfile): */ const GList * gvc_mixer_card_get_profiles (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->profiles; } /** * gvc_mixer_card_get_ports: * * Return value: (transfer none) (element-type GvcMixerCardPort): */ const GList * gvc_mixer_card_get_ports (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->ports; } /** * gvc_mixer_card_profile_compare: * * Return value: 1 if @a has a higher priority, -1 if @b has a higher * priority, 0 if @a and @b have the same priority. */ int gvc_mixer_card_profile_compare (GvcMixerCardProfile *a, GvcMixerCardProfile *b) { if (a->priority == b->priority) return 0; if (a->priority > b->priority) return 1; return -1; } /** * gvc_mixer_card_set_profiles: * @profiles: (transfer full) (element-type GvcMixerCardProfile): */ gboolean gvc_mixer_card_set_profiles (GvcMixerCard *card, GList *profiles) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles == NULL, FALSE); card->priv->profiles = g_list_sort (profiles, (GCompareFunc) gvc_mixer_card_profile_compare); return TRUE; } /** * gvc_mixer_card_get_gicon: * @card: * * Return value: (transfer full) (element-type GIcon): */ GIcon * gvc_mixer_card_get_gicon (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); if (card->priv->icon_name == NULL) return NULL; return g_themed_icon_new_with_default_fallbacks (card->priv->icon_name); } /** * gvc_mixer_card_set_ports: * @profiles: (transfer full) (element-type GvcMixerCardPort): */ gboolean gvc_mixer_card_set_ports (GvcMixerCard *card, GList *ports) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->ports == NULL, FALSE); card->priv->ports = ports; return TRUE; } static void gvc_mixer_card_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerCard *self = GVC_MIXER_CARD (object); switch (prop_id) { case PROP_PA_CONTEXT: self->priv->pa_context = g_value_get_pointer (value); break; case PROP_INDEX: self->priv->index = g_value_get_ulong (value); break; case PROP_ID: self->priv->id = g_value_get_ulong (value); break; case PROP_NAME: gvc_mixer_card_set_name (self, g_value_get_string (value)); break; case PROP_ICON_NAME: gvc_mixer_card_set_icon_name (self, g_value_get_string (value)); break; case PROP_PROFILE: gvc_mixer_card_set_profile (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_card_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerCard *self = GVC_MIXER_CARD (object); switch (prop_id) { case PROP_PA_CONTEXT: g_value_set_pointer (value, self->priv->pa_context); break; case PROP_INDEX: g_value_set_ulong (value, self->priv->index); break; case PROP_ID: g_value_set_ulong (value, self->priv->id); break; case PROP_NAME: g_value_set_string (value, self->priv->name); break; case PROP_ICON_NAME: g_value_set_string (value, self->priv->icon_name); break; case PROP_PROFILE: g_value_set_string (value, self->priv->profile); break; case PROP_HUMAN_PROFILE: g_value_set_string (value, self->priv->human_profile); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_card_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerCard *self; object = G_OBJECT_CLASS (gvc_mixer_card_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_CARD (object); self->priv->id = get_next_card_serial (); return object; } static void gvc_mixer_card_class_init (GvcMixerCardClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gvc_mixer_card_constructor; gobject_class->finalize = gvc_mixer_card_finalize; gobject_class->set_property = gvc_mixer_card_set_property; gobject_class->get_property = gvc_mixer_card_get_property; g_object_class_install_property (gobject_class, PROP_INDEX, g_param_spec_ulong ("index", "Index", "The index for this card", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_ID, g_param_spec_ulong ("id", "id", "The id for this card", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_PA_CONTEXT, g_param_spec_pointer ("pa-context", "PulseAudio context", "The PulseAudio context for this card", G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this card", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ICON_NAME, g_param_spec_string ("icon-name", "Icon Name", "Name of icon to display for this card", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PROFILE, g_param_spec_string ("profile", "Profile", "Name of current profile for this card", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_HUMAN_PROFILE, g_param_spec_string ("human-profile", "Profile (Human readable)", "Name of current profile for this card in human readable form", NULL, G_PARAM_READABLE)); g_type_class_add_private (klass, sizeof (GvcMixerCardPrivate)); } static void gvc_mixer_card_init (GvcMixerCard *card) { card->priv = GVC_MIXER_CARD_GET_PRIVATE (card); } GvcMixerCard * gvc_mixer_card_new (pa_context *context, guint index) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_CARD, "index", index, "pa-context", context, NULL); return GVC_MIXER_CARD (object); } static void free_profile (GvcMixerCardProfile *p) { g_free (p->profile); g_free (p->human_profile); g_free (p->status); g_free (p); } static void gvc_mixer_card_finalize (GObject *object) { GvcMixerCard *mixer_card; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_CARD (object)); mixer_card = GVC_MIXER_CARD (object); g_return_if_fail (mixer_card->priv != NULL); g_free (mixer_card->priv->name); mixer_card->priv->name = NULL; g_free (mixer_card->priv->icon_name); mixer_card->priv->icon_name = NULL; g_free (mixer_card->priv->target_profile); mixer_card->priv->target_profile = NULL; g_free (mixer_card->priv->profile); mixer_card->priv->profile = NULL; g_free (mixer_card->priv->human_profile); mixer_card->priv->human_profile = NULL; g_list_foreach (mixer_card->priv->profiles, (GFunc) free_profile, NULL); g_list_free (mixer_card->priv->profiles); mixer_card->priv->profiles = NULL; G_OBJECT_CLASS (gvc_mixer_card_parent_class)->finalize (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-event-role.h0000664000175000017500000000433712625666430017734 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_EVENT_ROLE_H #define __GVC_MIXER_EVENT_ROLE_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_EVENT_ROLE (gvc_mixer_event_role_get_type ()) #define GVC_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRole)) #define GVC_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass)) #define GVC_IS_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_EVENT_ROLE)) #define GVC_IS_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_EVENT_ROLE)) #define GVC_MIXER_EVENT_ROLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass)) typedef struct GvcMixerEventRolePrivate GvcMixerEventRolePrivate; typedef struct { GvcMixerStream parent; GvcMixerEventRolePrivate *priv; } GvcMixerEventRole; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerEventRoleClass; GType gvc_mixer_event_role_get_type (void); GvcMixerStream * gvc_mixer_event_role_new (pa_context *context, const char *device, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_EVENT_ROLE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-source.h0000664000175000017500000000417712625666430017156 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_SOURCE_H #define __GVC_MIXER_SOURCE_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SOURCE (gvc_mixer_source_get_type ()) #define GVC_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSource)) #define GVC_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass)) #define GVC_IS_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE)) #define GVC_IS_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE)) #define GVC_MIXER_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass)) typedef struct GvcMixerSourcePrivate GvcMixerSourcePrivate; typedef struct { GvcMixerStream parent; GvcMixerSourcePrivate *priv; } GvcMixerSource; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSourceClass; GType gvc_mixer_source_get_type (void); GvcMixerStream * gvc_mixer_source_new (pa_context *context, guint index, GvcChannelMap *map); G_END_DECLS #endif /* __GVC_MIXER_SOURCE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-sink-input.c0000664000175000017500000001170512625666430017745 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-sink-input.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SINK_INPUT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputPrivate)) struct GvcMixerSinkInputPrivate { gpointer dummy; }; static void gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass); static void gvc_mixer_sink_input_init (GvcMixerSinkInput *mixer_sink_input); static void gvc_mixer_sink_input_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); cv = gvc_channel_map_get_cvolume(map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_input_volume (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_input_volume() failed"); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_sink_input_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_input_mute (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_input_mute_by_index() failed"); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_sink_input_finalize; stream_class->push_volume = gvc_mixer_sink_input_push_volume; stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate)); } static void gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input) { sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input); } static void gvc_mixer_sink_input_finalize (GObject *object) { GvcMixerSinkInput *mixer_sink_input; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object)); mixer_sink_input = GVC_MIXER_SINK_INPUT (object); g_return_if_fail (mixer_sink_input->priv != NULL); G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->finalize (object); } /** * gvc_mixer_sink_input_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_sink_input_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SINK_INPUT, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-control-private.h0000664000175000017500000000224712625666430021002 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_CONTROL_PRIVATE_H #define __GVC_MIXER_CONTROL_PRIVATE_H #include #include #include "gvc-mixer-stream.h" #include "gvc-mixer-card.h" G_BEGIN_DECLS pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control); G_END_DECLS #endif /* __GVC_MIXER_CONTROL_PRIVATE_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-sink.h0000664000175000017500000000411712625666430016614 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_SINK_H #define __GVC_MIXER_SINK_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SINK (gvc_mixer_sink_get_type ()) #define GVC_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK, GvcMixerSink)) #define GVC_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass)) #define GVC_IS_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK)) #define GVC_IS_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK)) #define GVC_MIXER_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass)) typedef struct GvcMixerSinkPrivate GvcMixerSinkPrivate; typedef struct { GvcMixerStream parent; GvcMixerSinkPrivate *priv; } GvcMixerSink; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSinkClass; GType gvc_mixer_sink_get_type (void); GvcMixerStream * gvc_mixer_sink_new (pa_context *context, guint index, GvcChannelMap *map); G_END_DECLS #endif /* __GVC_MIXER_SINK_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-source-output.c0000664000175000017500000000717712625666430020512 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #include "config.h" #include #include #include #include #include #include #include "gvc-mixer-source-output.h" #define GVC_MIXER_SOURCE_OUTPUT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputPrivate)) struct GvcMixerSourceOutputPrivate { gpointer dummy; }; static void gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass); static void gvc_mixer_source_output_init (GvcMixerSourceOutput *mixer_source_output); static void gvc_mixer_source_output_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op) { /* FIXME: */ *op = NULL; return TRUE; } static gboolean gvc_mixer_source_output_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { /* FIXME: */ return TRUE; } static void gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_source_output_finalize; stream_class->push_volume = gvc_mixer_source_output_push_volume; stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSourceOutputPrivate)); } static void gvc_mixer_source_output_init (GvcMixerSourceOutput *source_output) { source_output->priv = GVC_MIXER_SOURCE_OUTPUT_GET_PRIVATE (source_output); } static void gvc_mixer_source_output_finalize (GObject *object) { GvcMixerSourceOutput *mixer_source_output; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SOURCE_OUTPUT (object)); mixer_source_output = GVC_MIXER_SOURCE_OUTPUT (object); g_return_if_fail (mixer_source_output->priv != NULL); G_OBJECT_CLASS (gvc_mixer_source_output_parent_class)->finalize (object); } /** * gvc_mixer_source_output_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_source_output_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SOURCE_OUTPUT, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } Cinnamon-2.8.6/src/gvc/gvc-mixer-ui-device.h0000664000175000017500000001005512625666430017520 0ustar fabiofabio/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * Copyright (C) Conor Curran 2011 * * This 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 2 of the License, or * (at your option) any later version. * * gvc-mixer-ui-device.h 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 _GVC_MIXER_UI_DEVICE_H_ #define _GVC_MIXER_UI_DEVICE_H_ #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_UI_DEVICE (gvc_mixer_ui_device_get_type ()) #define GVC_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevice)) #define GVC_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass)) #define GVC_IS_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GVC_TYPE_MIXER_UI_DEVICE)) #define GVC_IS_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GVC_TYPE_MIXER_UI_DEVICE)) #define GVC_MIXER_UI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass)) #define GVC_MIXER_UI_DEVICE_INVALID -1 typedef struct GvcMixerUIDevicePrivate GvcMixerUIDevicePrivate; typedef struct { GObjectClass parent_class; } GvcMixerUIDeviceClass; typedef struct { GObject parent_instance; GvcMixerUIDevicePrivate *priv; } GvcMixerUIDevice; typedef enum { UIDeviceInput, UIDeviceOutput, } GvcMixerUIDeviceDirection; GType gvc_mixer_ui_device_get_type (void) G_GNUC_CONST; guint gvc_mixer_ui_device_get_id (GvcMixerUIDevice *dev); gint gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *dev); const gchar * gvc_mixer_ui_device_get_description (GvcMixerUIDevice *dev); const gchar * gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *dev); const gchar * gvc_mixer_ui_device_get_port (GvcMixerUIDevice *dev); const gchar * gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *dev, const gchar *selected, const gchar *current); const gchar * gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device); const gchar * gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *dev, const gchar *profile); const gchar * gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *dev); const gchar * gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *dev); GList * gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *dev); GList * gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device); gboolean gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device); void gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device, const GList *profiles); void gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device, const gchar *profile); void gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *dev); gboolean gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *dev); gboolean gvc_mixer_ui_device_is_output (GvcMixerUIDevice *dev); G_END_DECLS #endif /* _GVC_MIXER_UI_DEVICE_H_ */ Cinnamon-2.8.6/src/gvc/gvc-mixer-control.h0000664000175000017500000001637512625666430017341 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_CONTROL_H #define __GVC_MIXER_CONTROL_H #include #include "gvc-mixer-stream.h" #include "gvc-mixer-card.h" #include "gvc-mixer-ui-device.h" G_BEGIN_DECLS typedef enum { GVC_STATE_CLOSED, GVC_STATE_READY, GVC_STATE_CONNECTING, GVC_STATE_FAILED } GvcMixerControlState; #define GVC_TYPE_MIXER_CONTROL (gvc_mixer_control_get_type ()) #define GVC_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl)) #define GVC_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass)) #define GVC_IS_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CONTROL)) #define GVC_IS_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CONTROL)) #define GVC_MIXER_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass)) typedef struct GvcMixerControlPrivate GvcMixerControlPrivate; typedef struct { GObject parent; GvcMixerControlPrivate *priv; } GvcMixerControl; typedef struct { GObjectClass parent_class; void (*state_changed) (GvcMixerControl *control, GvcMixerControlState new_state); void (*stream_added) (GvcMixerControl *control, guint id); void (*stream_removed) (GvcMixerControl *control, guint id); void (*card_added) (GvcMixerControl *control, guint id); void (*card_removed) (GvcMixerControl *control, guint id); void (*default_sink_changed) (GvcMixerControl *control, guint id); void (*default_source_changed) (GvcMixerControl *control, guint id); void (*active_output_update) (GvcMixerControl *control, guint id); void (*active_input_update) (GvcMixerControl *control, guint id); void (*output_added) (GvcMixerControl *control, guint id); void (*input_added) (GvcMixerControl *control, guint id); void (*output_removed) (GvcMixerControl *control, guint id); void (*input_removed) (GvcMixerControl *control, guint id); } GvcMixerControlClass; GType gvc_mixer_control_get_type (void); GvcMixerControl * gvc_mixer_control_new (const char *name); gboolean gvc_mixer_control_open (GvcMixerControl *control); gboolean gvc_mixer_control_close (GvcMixerControl *control); GSList * gvc_mixer_control_get_cards (GvcMixerControl *control); GSList * gvc_mixer_control_get_streams (GvcMixerControl *control); GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control); GSList * gvc_mixer_control_get_sources (GvcMixerControl *control); GSList * gvc_mixer_control_get_sink_inputs (GvcMixerControl *control); GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control, guint id); GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_output_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_input_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control, GvcMixerStream *stream); GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_get_event_sink_input (GvcMixerControl *control); gboolean gvc_mixer_control_set_default_sink (GvcMixerControl *control, GvcMixerStream *stream); gboolean gvc_mixer_control_set_default_source (GvcMixerControl *control, GvcMixerStream *stream); gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control); gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control); void gvc_mixer_control_change_output (GvcMixerControl *control, GvcMixerUIDevice* output); void gvc_mixer_control_change_input (GvcMixerControl *control, GvcMixerUIDevice* input); GvcMixerStream* gvc_mixer_control_get_stream_from_device (GvcMixerControl *control, GvcMixerUIDevice *device); gboolean gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control, GvcMixerUIDevice *device, const gchar* profile); GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control); G_END_DECLS #endif /* __GVC_MIXER_CONTROL_H */ Cinnamon-2.8.6/src/gvc/gvc-mixer-card-private.h0000664000175000017500000000240012625666430020222 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008-2009 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * */ #ifndef __GVC_MIXER_CARD_PRIVATE_H #define __GVC_MIXER_CARD_PRIVATE_H #include #include "gvc-mixer-card.h" G_BEGIN_DECLS GvcMixerCard * gvc_mixer_card_new (pa_context *context, guint index); pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card); G_END_DECLS #endif /* __GVC_MIXER_CARD_PRIVATE_H */ Cinnamon-2.8.6/src/cinnamon-recorder.h0000664000175000017500000000427112625666430016600 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_RECORDER_H__ #define __CINNAMON_RECORDER_H__ #include G_BEGIN_DECLS /** * SECTION:CinnamonRecorder * short_description: Record from a #ClutterStage * * The #CinnamonRecorder object is used to make recordings ("screencasts") * of a #ClutterStage. Recording is done via #GStreamer. The default is * to encode as a Theora movie and write it to a file in the current * directory named after the date, but the encoding and output can * be configured. */ typedef struct _CinnamonRecorder CinnamonRecorder; typedef struct _CinnamonRecorderClass CinnamonRecorderClass; #define CINNAMON_TYPE_RECORDER (cinnamon_recorder_get_type ()) #define CINNAMON_RECORDER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_RECORDER, CinnamonRecorder)) #define CINNAMON_RECORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_RECORDER, CinnamonRecorderClass)) #define CINNAMON_IS_RECORDER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_RECORDER)) #define CINNAMON_IS_RECORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_RECORDER)) #define CINNAMON_RECORDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_RECORDER, CinnamonRecorderClass)) GType cinnamon_recorder_get_type (void) G_GNUC_CONST; CinnamonRecorder *cinnamon_recorder_new (ClutterStage *stage); void cinnamon_recorder_set_framerate (CinnamonRecorder *recorder, int framerate); void cinnamon_recorder_set_filename (CinnamonRecorder *recorder, const char *filename); void cinnamon_recorder_set_pipeline (CinnamonRecorder *recorder, const char *pipeline); gboolean cinnamon_recorder_record (CinnamonRecorder *recorder); void cinnamon_recorder_close (CinnamonRecorder *recorder); void cinnamon_recorder_pause (CinnamonRecorder *recorder); gboolean cinnamon_recorder_is_recording (CinnamonRecorder *recorder); G_END_DECLS #endif /* __CINNAMON_RECORDER_H__ */ Cinnamon-2.8.6/src/cinnamon-screen-grabber.c0000664000175000017500000001514712625666430017653 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include #include #include #include #include #include #include "cinnamon-screen-grabber.h" PFNGLBINDBUFFERARBPROC pf_glBindBufferARB; PFNGLBUFFERDATAARBPROC pf_glBufferDataARB; PFNGLDELETEBUFFERSARBPROC pf_glDeleteBuffersARB; PFNGLGENBUFFERSARBPROC pf_glGenBuffersARB; PFNGLMAPBUFFERARBPROC pf_glMapBufferARB; PFNGLUNMAPBUFFERARBPROC pf_glUnmapBufferARB; struct _CinnamonScreenGrabberClass { GObjectClass parent_class; }; struct _CinnamonScreenGrabber { GObject parent_instance; int have_pixel_buffers; int have_pack_invert; int width, height; GLuint pixel_buffer; }; G_DEFINE_TYPE(CinnamonScreenGrabber, cinnamon_screen_grabber, G_TYPE_OBJECT); static void cinnamon_screen_grabber_finalize (GObject *gobject) { CinnamonScreenGrabber *grabber = CINNAMON_SCREEN_GRABBER (gobject); if (grabber->pixel_buffer != 0) pf_glDeleteBuffersARB (1, &grabber->pixel_buffer); } static void cinnamon_screen_grabber_class_init (CinnamonScreenGrabberClass *grabber_class) { GObjectClass *gobject_class = G_OBJECT_CLASS (grabber_class); gobject_class->finalize = cinnamon_screen_grabber_finalize; } static void cinnamon_screen_grabber_init (CinnamonScreenGrabber *grabber) { grabber->have_pixel_buffers = -1; grabber->width = -1; grabber->height= -1; grabber->pixel_buffer = 0; } CinnamonScreenGrabber * cinnamon_screen_grabber_new (void) { return g_object_new (CINNAMON_TYPE_SCREEN_GRABBER, NULL); } /** * cinnamon_screen_grabber_grab: * x: X coordinate of the rectangle to grab * y: Y coordinate of the rectangle to grab * width: width of the rectangle to grab * height: heigth of the rectangle to grab * * Grabs pixel data from a portion of the screen. * * Return value: buffer holding the grabbed data. The data is stored as 32-bit * words with native-endian xRGB pixels (i.e., the same as CAIRO_FORMAT_RGB24) * with no padding on the rows. So, the size of the buffer is width * height * 4 * bytes. Free with g_free(). **/ guchar * cinnamon_screen_grabber_grab (CinnamonScreenGrabber *grabber, int x, int y, int width, int height) { guchar *data; gsize row_bytes; gsize data_size; row_bytes = width * 4; data_size = row_bytes * height; data = g_malloc (data_size); if (grabber->have_pixel_buffers == -1) { const GLubyte* extensions = glGetString (GL_EXTENSIONS); grabber->have_pixel_buffers = strstr ((const char *)extensions, "GL_EXT_pixel_buffer_object") != NULL; grabber->have_pack_invert = strstr ((const char *)extensions, "GL_MESA_pack_invert") != NULL; } if (grabber->have_pixel_buffers) { GLubyte *mapped_data; GLint old_swap_bytes, old_lsb_first, old_row_length, old_skip_pixels, old_skip_rows, old_alignment; GLint old_pack_invert = GL_FALSE; GLint vp_size[4]; guchar *src_row, *dest_row; int i; cogl_flush (); if (pf_glBindBufferARB == NULL) { pf_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cogl_get_proc_address ("glBindBufferARB"); pf_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) cogl_get_proc_address ("glBufferDataARB"); pf_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) cogl_get_proc_address ("glDeleteBuffersARB"); pf_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) cogl_get_proc_address ("glGenBuffersARB"); pf_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cogl_get_proc_address ("glMapBufferARB"); pf_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) cogl_get_proc_address ("glUnmapBufferARB"); } glGetIntegerv (GL_PACK_SWAP_BYTES, &old_swap_bytes); glGetIntegerv (GL_PACK_LSB_FIRST, &old_lsb_first); glGetIntegerv (GL_PACK_ROW_LENGTH, &old_row_length); glGetIntegerv (GL_PACK_SKIP_PIXELS, &old_skip_pixels); glGetIntegerv (GL_PACK_SKIP_ROWS, &old_skip_rows); glGetIntegerv (GL_PACK_ALIGNMENT, &old_alignment); glPixelStorei (GL_PACK_SWAP_BYTES, GL_FALSE); glPixelStorei (GL_PACK_LSB_FIRST, GL_FALSE); glPixelStorei (GL_PACK_ROW_LENGTH, 0); glPixelStorei (GL_PACK_SKIP_PIXELS, 0); glPixelStorei (GL_PACK_SKIP_ROWS, 0); glPixelStorei (GL_PACK_ALIGNMENT, 1); if (grabber->have_pack_invert) { glGetIntegerv (GL_PACK_INVERT_MESA, &old_pack_invert); glPixelStorei (GL_PACK_INVERT_MESA, GL_FALSE); } if (grabber->pixel_buffer != 0 && (grabber->width != width || grabber->height != height)) { pf_glDeleteBuffersARB (1, &grabber->pixel_buffer); grabber->pixel_buffer = 0; } if (grabber->pixel_buffer == 0) { pf_glGenBuffersARB (1, &grabber->pixel_buffer); pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer); pf_glBufferDataARB (GL_PIXEL_PACK_BUFFER_ARB, data_size, 0, GL_STREAM_READ_ARB); grabber->width = width; grabber->height = height; } else { pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer); } /* In OpenGL, (x,y) specifies the bottom-left corner rather than the * top-left */ glGetIntegerv (GL_VIEWPORT, vp_size); y = vp_size[3] - (y + height); glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0); mapped_data = pf_glMapBufferARB (GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); src_row = mapped_data + (height - 1) * row_bytes; dest_row = data; for (i = 0; i < height; i++) { memcpy (dest_row, src_row, row_bytes); src_row -= row_bytes; dest_row += row_bytes; } pf_glUnmapBufferARB (GL_PIXEL_PACK_BUFFER_ARB); pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, 0); glPixelStorei (GL_PACK_SWAP_BYTES, old_swap_bytes); glPixelStorei (GL_PACK_LSB_FIRST, old_lsb_first); glPixelStorei (GL_PACK_ROW_LENGTH, old_row_length); glPixelStorei (GL_PACK_SKIP_PIXELS, old_skip_pixels); glPixelStorei (GL_PACK_SKIP_ROWS, old_skip_rows); glPixelStorei (GL_PACK_ALIGNMENT, old_alignment); if (grabber->have_pack_invert) glPixelStorei (GL_PACK_INVERT_MESA, old_pack_invert); } else { cogl_read_pixels (x, y, width, height, COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); } return data; } Cinnamon-2.8.6/src/cinnamon-doc-system.h0000664000175000017500000000356712625666430017071 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_DOC_SYSTEM_H__ #define __CINNAMON_DOC_SYSTEM_H__ #include #include #define CINNAMON_TYPE_DOC_SYSTEM (cinnamon_doc_system_get_type ()) #define CINNAMON_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_DOC_SYSTEM, CinnamonDocSystem)) #define CINNAMON_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_DOC_SYSTEM, CinnamonDocSystemClass)) #define CINNAMON_IS_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_DOC_SYSTEM)) #define CINNAMON_IS_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_DOC_SYSTEM)) #define CINNAMON_DOC_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_DOC_SYSTEM, CinnamonDocSystemClass)) typedef struct _CinnamonDocSystem CinnamonDocSystem; typedef struct _CinnamonDocSystemClass CinnamonDocSystemClass; typedef struct _CinnamonDocSystemPrivate CinnamonDocSystemPrivate; struct _CinnamonDocSystem { GObject parent; CinnamonDocSystemPrivate *priv; }; struct _CinnamonDocSystemClass { GObjectClass parent_class; }; GType cinnamon_doc_system_get_type (void) G_GNUC_CONST; CinnamonDocSystem* cinnamon_doc_system_get_default (void); GSList *cinnamon_doc_system_get_all (CinnamonDocSystem *system); GtkRecentInfo *cinnamon_doc_system_lookup_by_uri (CinnamonDocSystem *system, const char *uri); void cinnamon_doc_system_queue_existence_check (CinnamonDocSystem *system, guint n_items); void cinnamon_doc_system_open (CinnamonDocSystem *system, GtkRecentInfo *info, int workspace); #endif /* __CINNAMON_DOC_SYSTEM_H__ */ Cinnamon-2.8.6/src/cinnamon-screen-grabber.h0000664000175000017500000000416512625666430017656 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_SCREEN_GRABBER_H__ #define __CINNAMON_SCREEN_GRABBER_H__ #include G_BEGIN_DECLS /** * SECTION:cinnamon-screen-grabber * @short_description: Grab pixel data from the screen * * The #CinnamonScreenGrabber object is used to download previous drawn * content to the screen. It internally uses pixel-buffer objects if * available, otherwise falls back to cogl_read_pixels(). * * If you are repeatedly grabbing images of the same size from the * screen, it makes sense to create one #CinnamonScreenGrabber and keep * it around. Otherwise, it's fine to simply create one as needed and * then get rid of it. */ typedef struct _CinnamonScreenGrabber CinnamonScreenGrabber; typedef struct _CinnamonScreenGrabberClass CinnamonScreenGrabberClass; #define CINNAMON_TYPE_SCREEN_GRABBER (cinnamon_screen_grabber_get_type ()) #define CINNAMON_SCREEN_GRABBER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabber)) #define CINNAMON_SCREEN_GRABBER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabberClass)) #define CINNAMON_IS_SCREEN_GRABBER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_SCREEN_GRABBER)) #define CINNAMON_IS_SCREEN_GRABBER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_SCREEN_GRABBER)) #define CINNAMON_SCREEN_GRABBER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabberClass)) GType cinnamon_screen_grabber_get_type (void) G_GNUC_CONST; CinnamonScreenGrabber *cinnamon_screen_grabber_new (void); guchar * cinnamon_screen_grabber_grab (CinnamonScreenGrabber *grabber, int x, int y, int width, int height); G_END_DECLS #endif /* __CINNAMON_SCREEN_GRABBER_H__ */ Cinnamon-2.8.6/src/cinnamon-gtk-embed.c0000664000175000017500000002144512625666430016627 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include "cinnamon-embedded-window-private.h" #include enum { PROP_0, PROP_WINDOW }; struct _CinnamonGtkEmbedPrivate { CinnamonEmbeddedWindow *window; }; G_DEFINE_TYPE (CinnamonGtkEmbed, cinnamon_gtk_embed, CLUTTER_X11_TYPE_TEXTURE_PIXMAP); static void cinnamon_gtk_embed_set_window (CinnamonGtkEmbed *embed, CinnamonEmbeddedWindow *window); static void cinnamon_gtk_embed_on_window_destroy (GtkWidget *object, CinnamonGtkEmbed *embed) { cinnamon_gtk_embed_set_window (embed, NULL); } static void cinnamon_gtk_embed_on_window_realize (GtkWidget *widget, CinnamonGtkEmbed *embed) { /* Here automatic=FALSE means to use CompositeRedirectManual. * That is, the X server shouldn't draw the window onto the * screen. */ clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), gdk_x11_window_get_xid (gtk_widget_get_window (widget)), FALSE); } static void cinnamon_gtk_embed_set_window (CinnamonGtkEmbed *embed, CinnamonEmbeddedWindow *window) { if (embed->priv->window) { _cinnamon_embedded_window_set_actor (embed->priv->window, NULL); g_object_unref (embed->priv->window); clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), None, FALSE); g_signal_handlers_disconnect_by_func (embed->priv->window, (gpointer)cinnamon_gtk_embed_on_window_destroy, embed); g_signal_handlers_disconnect_by_func (embed->priv->window, (gpointer)cinnamon_gtk_embed_on_window_realize, embed); } embed->priv->window = window; if (embed->priv->window) { g_object_ref (embed->priv->window); _cinnamon_embedded_window_set_actor (embed->priv->window, embed); g_signal_connect (embed->priv->window, "destroy", G_CALLBACK (cinnamon_gtk_embed_on_window_destroy), embed); g_signal_connect (embed->priv->window, "realize", G_CALLBACK (cinnamon_gtk_embed_on_window_realize), embed); if (gtk_widget_get_realized (GTK_WIDGET (window))) cinnamon_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed); } clutter_actor_queue_relayout (CLUTTER_ACTOR (embed)); } static void cinnamon_gtk_embed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (object); switch (prop_id) { case PROP_WINDOW: cinnamon_gtk_embed_set_window (embed, (CinnamonEmbeddedWindow *)g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_gtk_embed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, embed->priv->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_gtk_embed_get_preferred_width (ClutterActor *actor, float for_height, float *min_width_p, float *natural_width_p) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (actor); if (embed->priv->window && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window))) { GtkRequisition min_req, natural_req; gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req); *min_width_p = min_req.width; *natural_width_p = natural_req.width; } else *min_width_p = *natural_width_p = 0; } static void cinnamon_gtk_embed_get_preferred_height (ClutterActor *actor, float for_width, float *min_height_p, float *natural_height_p) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (actor); if (embed->priv->window && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window))) { GtkRequisition min_req, natural_req; gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req); *min_height_p = min_req.height; *natural_height_p = natural_req.height; } else *min_height_p = *natural_height_p = 0; } static void cinnamon_gtk_embed_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (actor); float wx = 0.0, wy = 0.0, x, y, ax, ay; CLUTTER_ACTOR_CLASS (cinnamon_gtk_embed_parent_class)-> allocate (actor, box, flags); /* Find the actor's new coordinates in terms of the stage (which is * priv->window's parent window. */ while (actor) { clutter_actor_get_position (actor, &x, &y); clutter_actor_get_anchor_point (actor, &ax, &ay); wx += x - ax; wy += y - ay; actor = clutter_actor_get_parent (actor); } _cinnamon_embedded_window_allocate (embed->priv->window, (int)(0.5 + wx), (int)(0.5 + wy), box->x2 - box->x1, box->y2 - box->y1); } static void cinnamon_gtk_embed_realize (ClutterActor *actor) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (actor); _cinnamon_embedded_window_realize (embed->priv->window); CLUTTER_ACTOR_CLASS (cinnamon_gtk_embed_parent_class)->realize (actor); } static void cinnamon_gtk_embed_unrealize (ClutterActor *actor) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (actor); if (embed->priv->window) _cinnamon_embedded_window_unrealize (embed->priv->window); CLUTTER_ACTOR_CLASS (cinnamon_gtk_embed_parent_class)->unrealize (actor); } static void cinnamon_gtk_embed_dispose (GObject *object) { CinnamonGtkEmbed *embed = CINNAMON_GTK_EMBED (object); cinnamon_gtk_embed_set_window (embed, NULL); G_OBJECT_CLASS (cinnamon_gtk_embed_parent_class)->dispose (object); } static void cinnamon_gtk_embed_class_init (CinnamonGtkEmbedClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (CinnamonGtkEmbedPrivate)); object_class->get_property = cinnamon_gtk_embed_get_property; object_class->set_property = cinnamon_gtk_embed_set_property; object_class->dispose = cinnamon_gtk_embed_dispose; actor_class->get_preferred_width = cinnamon_gtk_embed_get_preferred_width; actor_class->get_preferred_height = cinnamon_gtk_embed_get_preferred_height; actor_class->allocate = cinnamon_gtk_embed_allocate; actor_class->realize = cinnamon_gtk_embed_realize; actor_class->unrealize = cinnamon_gtk_embed_unrealize; g_object_class_install_property (object_class, PROP_WINDOW, g_param_spec_object ("window", "Window", "CinnamonEmbeddedWindow to embed", CINNAMON_TYPE_EMBEDDED_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void cinnamon_gtk_embed_init (CinnamonGtkEmbed *embed) { embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, CINNAMON_TYPE_GTK_EMBED, CinnamonGtkEmbedPrivate); /* automatic here means whether ClutterX11TexturePixmap should * process damage update and refresh the pixmap itself. */ clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (embed), TRUE); } /* * Public API */ ClutterActor * cinnamon_gtk_embed_new (CinnamonEmbeddedWindow *window) { g_return_val_if_fail (CINNAMON_IS_EMBEDDED_WINDOW (window), NULL); return g_object_new (CINNAMON_TYPE_GTK_EMBED, "window", window, NULL); } Cinnamon-2.8.6/src/main.c0000664000175000017500000002420512625666430014111 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #ifdef HAVE_MALLINFO #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cinnamon-global.h" #include "cinnamon-global-private.h" #include "cinnamon-perf-log.h" #include "cinnamon-js.h" #include "st.h" extern GType gnome_cinnamon_plugin_get_type (void); #define CINNAMON_DBUS_SERVICE "org.Cinnamon" #define MAGNIFIER_DBUS_SERVICE "org.gnome.Magnifier" static void cinnamon_dbus_acquire_name (GDBusProxy *bus, guint32 request_name_flags, guint32 *request_name_result, gchar *name, gboolean fatal) { GError *error = NULL; GVariant *request_name_variant; if (!(request_name_variant = g_dbus_proxy_call_sync (bus, "RequestName", g_variant_new ("(su)", name, request_name_flags), 0, /* call flags */ -1, /* timeout */ NULL, /* cancellable */ &error))) { g_printerr ("failed to acquire %s: %s\n", name, error->message); if (!fatal) return; exit (1); } g_variant_get (request_name_variant, "(u)", request_name_result); } static void cinnamon_dbus_acquire_names (GDBusProxy *bus, guint32 request_name_flags, gchar *name, gboolean fatal, ...) G_GNUC_NULL_TERMINATED; static void cinnamon_dbus_acquire_names (GDBusProxy *bus, guint32 request_name_flags, gchar *name, gboolean fatal, ...) { va_list al; guint32 request_name_result; va_start (al, fatal); for (;;) { cinnamon_dbus_acquire_name (bus, request_name_flags, &request_name_result, name, fatal); name = va_arg (al, gchar *); if (!name) break; fatal = va_arg (al, gboolean); } va_end (al); } static void cinnamon_dbus_init (gboolean replace) { GDBusConnection *session; GDBusProxy *bus; GError *error = NULL; guint32 request_name_flags; guint32 request_name_result; session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (error) { g_printerr ("Failed to connect to session bus: %s", error->message); exit (1); } bus = g_dbus_proxy_new_sync (session, G_DBUS_PROXY_FLAGS_NONE, NULL, /* interface info */ "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", NULL, /* cancellable */ &error); request_name_flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; if (replace) request_name_flags |= DBUS_NAME_FLAG_REPLACE_EXISTING; cinnamon_dbus_acquire_name (bus, request_name_flags, &request_name_result, CINNAMON_DBUS_SERVICE, TRUE); if (!(request_name_result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER || request_name_result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER)) { g_printerr (CINNAMON_DBUS_SERVICE " already exists on bus and --replace not specified\n"); exit (1); } /* * We always specify REPLACE_EXISTING to ensure we kill off * the existing service if it was running. */ request_name_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; cinnamon_dbus_acquire_names (bus, request_name_flags, /* Also grab org.gnome.Panel to replace any existing panel process */ "org.gnome.Panel", TRUE, /* ...and the org.gnome.Magnifier service. */ MAGNIFIER_DBUS_SERVICE, FALSE, /* ...and the org.freedesktop.Notifications service. */ "org.freedesktop.Notifications", FALSE, NULL); /* ...and the on-screen keyboard service */ cinnamon_dbus_acquire_name (bus, DBUS_NAME_FLAG_REPLACE_EXISTING, &request_name_result, "org.gnome.Caribou.Keyboard", FALSE); g_object_unref (bus); g_object_unref (session); } static void malloc_statistics_callback (CinnamonPerfLog *perf_log, gpointer data) { #ifdef HAVE_MALLINFO struct mallinfo info = mallinfo (); cinnamon_perf_log_update_statistic_i (perf_log, "malloc.arenaSize", info.arena); cinnamon_perf_log_update_statistic_i (perf_log, "malloc.mmapSize", info.hblkhd); cinnamon_perf_log_update_statistic_i (perf_log, "malloc.usedSize", info.uordblks); #endif } static void cinnamon_a11y_init (void) { if (clutter_get_accessibility_enabled () == FALSE) { g_warning ("Accessibility: clutter has no accessibility enabled" " skipping the atk-bridge load"); } else { atk_bridge_adaptor_init (NULL, NULL); } } static void cinnamon_perf_log_init (void) { CinnamonPerfLog *perf_log = cinnamon_perf_log_get_default (); /* For probably historical reasons, mallinfo() defines the returned values, * even those in bytes as int, not size_t. We're determined not to use * more than 2G of malloc'ed memory, so are OK with that. */ cinnamon_perf_log_define_statistic (perf_log, "malloc.arenaSize", "Amount of memory allocated by malloc() with brk(), in bytes", "i"); cinnamon_perf_log_define_statistic (perf_log, "malloc.mmapSize", "Amount of memory allocated by malloc() with mmap(), in bytes", "i"); cinnamon_perf_log_define_statistic (perf_log, "malloc.usedSize", "Amount of malloc'ed memory currently in use", "i"); cinnamon_perf_log_add_statistics_callback (perf_log, malloc_statistics_callback, NULL, NULL); } static void muted_log_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer data) { /* Intentionally empty to discard message */ } static gboolean print_version (const gchar *option_name, const gchar *value, gpointer data, GError **error) { g_print ("Cinnamon %s\n", VERSION); exit (0); } GOptionEntry gnome_cinnamon_options[] = { { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_version, N_("Print version"), NULL }, { NULL } }; static void center_pointer_on_screen () { Display *dpy; Window root_window; Screen *screen; dpy = XOpenDisplay(0); root_window = XRootWindow(dpy, 0); XSelectInput(dpy, root_window, KeyReleaseMask); screen = DefaultScreenOfDisplay(dpy); XWarpPointer(dpy, None, root_window, 0, 0, 0, 0, WidthOfScreen(screen)/2, HeightOfScreen(screen)/2); XFlush(dpy); } int main (int argc, char **argv) { GOptionContext *ctx; GError *error = NULL; int ecode; g_setenv ("CLUTTER_DISABLE_XINPUT", "1", TRUE); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); g_setenv ("GDK_SCALE", "1", TRUE); ctx = meta_get_option_context (); g_option_context_add_main_entries (ctx, gnome_cinnamon_options, GETTEXT_PACKAGE); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("%s: %s\n", argv[0], error->message); exit (1); } g_option_context_free (ctx); meta_plugin_manager_set_plugin_type (gnome_cinnamon_plugin_get_type ()); /* Prevent meta_init() from causing gtk to load gail and at-bridge */ g_setenv ("NO_GAIL", "1", TRUE); g_setenv ("NO_AT_BRIDGE", "1", TRUE); meta_init (); g_unsetenv ("NO_GAIL"); g_unsetenv ("NO_AT_BRIDGE"); g_unsetenv ("CLUTTER_DISABLE_XINPUT"); /* FIXME: Add gjs API to set this stuff and don't depend on the * environment. These propagate to child processes. */ g_setenv ("GJS_DEBUG_OUTPUT", "stderr", TRUE); g_setenv ("GJS_DEBUG_TOPICS", "JS ERROR;JS LOG", TRUE); g_setenv ("CINNAMON_VERSION", VERSION, TRUE); center_pointer_on_screen(); cinnamon_dbus_init (meta_get_replace_current_wm ()); cinnamon_a11y_init (); cinnamon_perf_log_init (); g_irepository_prepend_search_path (CINNAMON_PKGLIBDIR); #if HAVE_BLUETOOTH g_irepository_prepend_search_path (BLUETOOTH_DIR); #endif /* Disable debug spew from various libraries */ g_log_set_handler ("Gvc", G_LOG_LEVEL_DEBUG, muted_log_handler, NULL); g_log_set_handler ("AccountsService", G_LOG_LEVEL_DEBUG, muted_log_handler, NULL); g_log_set_handler ("Bluetooth", G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_MESSAGE, muted_log_handler, NULL); /* Initialize the global object */ _cinnamon_global_init (NULL); g_unsetenv ("GDK_SCALE"); ecode = meta_run (); if (g_getenv ("CINNAMON_ENABLE_CLEANUP")) { g_printerr ("Doing final cleanup...\n"); g_object_unref (cinnamon_global_get ()); } return ecode; } Cinnamon-2.8.6/src/cinnamon-window-tracker-private.h0000664000175000017500000000067512625666430021407 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_WINDOW_TRACKER_PRIVATE_H__ #define __CINNAMON_WINDOW_TRACKER_PRIVATE_H__ #include "cinnamon-window-tracker.h" void _cinnamon_window_tracker_add_child_process_app (CinnamonWindowTracker *tracker, GPid pid, CinnamonApp *app); #endif Cinnamon-2.8.6/src/cinnamon-slicer.c0000664000175000017500000001116212625666430016244 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:cinnamon-slicer * @short_description: Display only part of another actor * * A #StBin that has 0 minimum size, and will clip its child * in the middle. */ #include "config.h" #include "cinnamon-slicer.h" G_DEFINE_TYPE (CinnamonSlicer, cinnamon_slicer, ST_TYPE_BIN); static void cinnamon_slicer_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterActor *child = st_bin_get_child (ST_BIN (self)); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_height (theme_node, &for_height); if (min_width_p) *min_width_p = 0; if (child == NULL) { if (natural_width_p) *natural_width_p = 0; } else { _st_actor_get_preferred_width (child, for_height, FALSE, NULL, natural_width_p); } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void cinnamon_slicer_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterActor *child = st_bin_get_child (ST_BIN (self)); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_width (theme_node, &for_width); if (min_height_p) *min_height_p = 0; if (child == NULL) { if (natural_height_p) *natural_height_p = 0; } else { _st_actor_get_preferred_height (child, for_width, FALSE, NULL, natural_height_p); } st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void cinnamon_slicer_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActor *child; /* Chain up directly to ClutterActor to set actor->allocation. We explicitly skip our parent class * StBin here because we want to override the allocate function. */ CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ()))->allocate (self, box, flags); child = st_bin_get_child (ST_BIN (self)); if (child) clutter_actor_allocate_preferred_size (child, flags); } static void cinnamon_slicer_paint_child (CinnamonSlicer *self) { ClutterActor *child; ClutterActorBox self_box; ClutterActorBox child_box; float width, height, child_width, child_height; StAlign x_align, y_align; double x_align_factor, y_align_factor; child = st_bin_get_child (ST_BIN (self)); if (!child) return; st_bin_get_alignment (ST_BIN (self), &x_align, &y_align); _st_get_align_factors (ST_WIDGET (self), x_align, y_align, &x_align_factor, &y_align_factor); clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &self_box); clutter_actor_get_allocation_box (child, &child_box); width = self_box.x2 - self_box.x1; height = self_box.y2 - self_box.y1; child_width = child_box.x2 - child_box.x1; child_height = child_box.y2 - child_box.y1; cogl_push_matrix (); cogl_clip_push_rectangle (0, 0, width, height); cogl_translate ((int)(0.5 + x_align_factor * (width - child_width)), (int)(0.5 + y_align_factor * (height - child_height)), 0); clutter_actor_paint (child); cogl_clip_pop (); cogl_pop_matrix (); } static void cinnamon_slicer_paint (ClutterActor *self) { /* StWidget paints CSS elements */ CLUTTER_ACTOR_CLASS (g_type_class_peek (st_widget_get_type ()))->paint (self); cinnamon_slicer_paint_child (CINNAMON_SLICER (self)); } static void cinnamon_slicer_pick (ClutterActor *self, const ClutterColor *pick_color) { cinnamon_slicer_paint_child (CINNAMON_SLICER (self)); } static void cinnamon_slicer_class_init (CinnamonSlicerClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_width = cinnamon_slicer_get_preferred_width; actor_class->get_preferred_height = cinnamon_slicer_get_preferred_height; actor_class->allocate = cinnamon_slicer_allocate; actor_class->paint = cinnamon_slicer_paint; actor_class->pick = cinnamon_slicer_pick; } static void cinnamon_slicer_init (CinnamonSlicer *actor) { } Cinnamon-2.8.6/src/st/0000775000175000017500000000000012625666430013444 5ustar fabiofabioCinnamon-2.8.6/src/st/st-box-layout-child.c0000664000175000017500000001351512625666430017425 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-box-layout-child.c: box layout child actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-box-layout-child * @short_description: meta data associated with a #StBoxLayout child. * * #StBoxLayoutChild is a #ClutterChildMeta implementation that stores the * child properties for children inside a #StBoxLayout. */ #include "st-box-layout-child.h" #include "st-private.h" G_DEFINE_TYPE (StBoxLayoutChild, st_box_layout_child, CLUTTER_TYPE_CHILD_META) #define BOX_LAYOUT_CHILD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildPrivate)) enum { PROP_0, PROP_EXPAND, PROP_X_FILL, PROP_Y_FILL, PROP_X_ALIGN, PROP_Y_ALIGN }; static void st_box_layout_child_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object); switch (property_id) { case PROP_EXPAND: g_value_set_boolean (value, child->expand); break; case PROP_X_FILL: g_value_set_boolean (value, child->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, child->y_fill); break; case PROP_X_ALIGN: g_value_set_enum (value, child->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, child->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_box_layout_child_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object); StBoxLayout *box = ST_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container); switch (property_id) { case PROP_EXPAND: child->expand = g_value_get_boolean (value); break; case PROP_X_FILL: child->x_fill = g_value_get_boolean (value); break; case PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); break; case PROP_X_ALIGN: child->x_align = g_value_get_enum (value); break; case PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } clutter_actor_queue_relayout ((ClutterActor*) box); } static void st_box_layout_child_dispose (GObject *object) { G_OBJECT_CLASS (st_box_layout_child_parent_class)->dispose (object); } static void st_box_layout_child_finalize (GObject *object) { G_OBJECT_CLASS (st_box_layout_child_parent_class)->finalize (object); } static void st_box_layout_child_class_init (StBoxLayoutChildClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->get_property = st_box_layout_child_get_property; object_class->set_property = st_box_layout_child_set_property; object_class->dispose = st_box_layout_child_dispose; object_class->finalize = st_box_layout_child_finalize; pspec = g_param_spec_boolean ("expand", "Expand", "Allocate the child extra space", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_EXPAND, pspec); pspec = g_param_spec_boolean ("x-fill", "x-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", "y-fill", "Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_FILL, pspec); pspec = g_param_spec_enum ("x-align", "X Alignment", "X alignment of the widget within the cell", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Alignment", "Y alignment of the widget within the cell", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec); } static void st_box_layout_child_init (StBoxLayoutChild *self) { self->expand = FALSE; self->x_fill = TRUE; self->y_fill = TRUE; self->x_align = ST_ALIGN_MIDDLE; self->y_align = ST_ALIGN_MIDDLE; } Cinnamon-2.8.6/src/st/st-polygon.h0000664000175000017500000000367712625666430015745 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-drawing-area.h: A dynamically-sized Cairo drawing area * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_POLYGON_H__ #define __ST_POLYGON_H__ #include #include #define ST_TYPE_POLYGON (st_polygon_get_type ()) #define ST_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_POLYGON, StPolygon)) #define ST_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_POLYGON, StPolygonClass)) #define ST_IS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_POLYGON)) #define ST_IS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_POLYGON)) #define ST_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_POLYGON, StPolygonClass)) typedef struct _StPolygon StPolygon; typedef struct _StPolygonClass StPolygonClass; typedef struct _StPolygonPrivate StPolygonPrivate; struct _StPolygon { ClutterActor parent; StPolygonPrivate *priv; }; struct _StPolygonClass { ClutterActorClass parent_class; void (*repaint) (StPolygon *area); }; GType st_polygon_get_type (void) G_GNUC_CONST; void st_polygon_queue_repaint (StPolygon *area); #endif /* __ST_POLYGON_H__ */ Cinnamon-2.8.6/src/st/st-icon-colors.c0000664000175000017500000000555312625666430016473 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-icon-colors.c: Colors for colorizing a symbolic icon * * Copyright 2010 Red Hat, Inc. * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "st-icon-colors.h" /** * st_icon_colors_new: * * Creates a new #StIconColors. All colors are initialized to transparent black. * * Return value: a newly created #StIconColors. Free with st_icon_colors_unref() */ StIconColors * st_icon_colors_new (void) { StIconColors *colors; colors = g_slice_new0 (StIconColors); colors->ref_count = 1; return colors; } /** * st_icon_colors_ref: * @colors: a #StIconColors * * Atomically increments the reference count of @colors by one. * * Returns: the passed in #StIconColors. */ StIconColors * st_icon_colors_ref (StIconColors *colors) { g_return_val_if_fail (colors != NULL, NULL); g_return_val_if_fail (colors->ref_count > 0, colors); g_atomic_int_inc ((volatile int *)&colors->ref_count); return colors; } /** * st_icon_colors_unref: * @colors: a #StIconColors * * Atomically decrements the reference count of @colors by one. * If the reference count drops to 0, all memory allocated by the * #StIconColors is released. */ void st_icon_colors_unref (StIconColors *colors) { g_return_if_fail (colors != NULL); g_return_if_fail (colors->ref_count > 0); if (g_atomic_int_dec_and_test ((volatile int *)&colors->ref_count)) g_slice_free (StIconColors, colors); } /** * st_icon_colors_copy: * @colors: a #StIconColors * * Creates a new StIconColors structure that is a copy of the passed * in @colors. You would use this function instead of st_icon_colors_ref() * if you were planning to change colors in the result. * * Returns: a newly created #StIconColors. */ StIconColors * st_icon_colors_copy (StIconColors *colors) { StIconColors *copy; g_return_val_if_fail (colors != NULL, NULL); copy = st_icon_colors_new (); copy->foreground = colors->foreground; copy->warning = colors->warning; copy->error = colors->error; copy->success = colors->success; return copy; } G_DEFINE_BOXED_TYPE (StIconColors, st_icon_colors, st_icon_colors_ref, st_icon_colors_unref) Cinnamon-2.8.6/src/st/st-scroll-view-fade.c0000664000175000017500000003600512625666430017403 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-view-fade.h: Edge fade effect for StScrollView * * Copyright 2010 Intel Corporation. * Copyright 2011 Adel Gadllah * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #define ST_SCROLL_VIEW_FADE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass)) #define ST_IS_SCROLL_VIEW_FADE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW_FADE)) #define ST_SCROLL_VIEW_FADE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass)) #include "st-scroll-view-fade.h" #include "st-scroll-view.h" #include "st-widget.h" #include "st-theme-node.h" #include "st-scroll-bar.h" #include "st-scrollable.h" #include "st-cogl-wrapper.h" #include #include typedef struct _StScrollViewFadeClass StScrollViewFadeClass; #define DEFAULT_FADE_OFFSET 68.0f static const gchar *fade_glsl_shader = "uniform sampler2D tex;\n" "uniform float height;\n" "uniform float width;\n" "uniform float offset_bottom;\n" "uniform float offset_top;\n" /* * Used to pass the fade area to the shader * * [0][0] = x1 * [0][1] = y1 * [1][0] = x2 * [1][1] = y2 * */ "uniform mat2 fade_area;\n" "\n" "void main ()\n" "{\n" " vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n" " float y = height * cogl_tex_coord_in[0].y;\n" " float x = width * cogl_tex_coord_in[0].x;\n" " float ratio = 1.0;\n" " float fade_bottom_start = fade_area[1][1] - offset_bottom;\n" " float ratio_top = y / offset_top;\n" " float ratio_bottom = (fade_area[1][1] - y)/(fade_area[1][1] - fade_bottom_start);\n" " bool in_scroll_area = fade_area[0][0] <= x && fade_area[1][0] >= x;\n" " bool fade_top = y < offset_top && in_scroll_area && (y >= fade_area[0][1]);\n" " bool fade_bottom = y > fade_bottom_start && in_scroll_area && (y <= fade_area[1][1]);\n" "\n" " if (fade_top) {\n" " ratio *= ratio_top;\n" " }\n" "\n" " if (fade_bottom) {\n" " ratio *= ratio_bottom;\n" " }\n" "\n" " cogl_color_out = color * ratio;\n" "}"; struct _StScrollViewFade { ClutterOffscreenEffect parent_instance; /* a back pointer to our actor, so that we can query it */ ClutterActor *actor; CoglHandle shader; CoglHandle program; gint tex_uniform; gint height_uniform; gint width_uniform; gint fade_area_uniform; gint offset_top_uniform; gint offset_bottom_uniform; StAdjustment *vadjustment; guint is_attached : 1; float fade_offset; }; struct _StScrollViewFadeClass { ClutterOffscreenEffectClass parent_class; }; G_DEFINE_TYPE (StScrollViewFade, st_scroll_view_fade, CLUTTER_TYPE_OFFSCREEN_EFFECT); enum { PROP_0, PROP_FADE_OFFSET, }; static gboolean st_scroll_view_fade_pre_paint (ClutterEffect *effect) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect); ClutterEffectClass *parent_class; if (self->shader == COGL_INVALID_HANDLE) return FALSE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; if (self->actor == NULL) return FALSE; if (self->program == COGL_INVALID_HANDLE) self->program = cogl_create_program (); if (!self->is_attached) { g_assert (self->shader != COGL_INVALID_HANDLE); g_assert (self->program != COGL_INVALID_HANDLE); cogl_program_attach_shader (self->program, self->shader); cogl_program_link (self->program); cogl_handle_unref (self->shader); self->is_attached = TRUE; self->tex_uniform = cogl_program_get_uniform_location (self->program, "tex"); self->height_uniform = cogl_program_get_uniform_location (self->program, "height"); self->width_uniform = cogl_program_get_uniform_location (self->program, "width"); self->fade_area_uniform = cogl_program_get_uniform_location (self->program, "fade_area"); self->offset_top_uniform = cogl_program_get_uniform_location (self->program, "offset_top"); self->offset_bottom_uniform = cogl_program_get_uniform_location (self->program, "offset_bottom"); } parent_class = CLUTTER_EFFECT_CLASS (st_scroll_view_fade_parent_class); return parent_class->pre_paint (effect); } static CoglHandle st_scroll_view_fade_create_texture (ClutterOffscreenEffect *effect, gfloat min_width, gfloat min_height) { return st_cogl_texture_new_with_size_wrapper (min_width, min_height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); } static void st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect); ClutterOffscreenEffectClass *parent; CoglHandle material; gdouble value, lower, upper, page_size; ClutterActor *vscroll = st_scroll_view_get_vscroll_bar (ST_SCROLL_VIEW (self->actor)); ClutterActor *hscroll = st_scroll_view_get_hscroll_bar (ST_SCROLL_VIEW (self->actor)); gboolean h_scroll_visible, v_scroll_visible; ClutterActorBox allocation, content_box, paint_box; /* * Used to pass the fade area to the shader * * [0][0] = x1 * [0][1] = y1 * [1][0] = x2 * [1][1] = y2 * */ float fade_area[2][2]; ClutterVertex verts[4]; if (self->program == COGL_INVALID_HANDLE) goto out; clutter_actor_get_paint_box (self->actor, &paint_box); clutter_actor_get_abs_allocation_vertices (self->actor, verts); clutter_actor_get_allocation_box (self->actor, &allocation); st_theme_node_get_content_box (st_widget_get_theme_node (ST_WIDGET (self->actor)), (const ClutterActorBox *)&allocation, &content_box); /* * The FBO is based on the paint_volume's size which can be larger then the actual * allocation, so we have to account for that when passing the positions */ fade_area[0][0] = content_box.x1 + (verts[0].x - paint_box.x1); fade_area[0][1] = content_box.y1 + (verts[0].y - paint_box.y1); fade_area[1][0] = content_box.x2 + (verts[3].x - paint_box.x2); fade_area[1][1] = content_box.y2 + (verts[3].y - paint_box.y2); g_object_get (ST_SCROLL_VIEW (self->actor), "hscrollbar-visible", &h_scroll_visible, "vscrollbar-visible", &v_scroll_visible, NULL); if (v_scroll_visible) { if (st_widget_get_direction (ST_WIDGET (self->actor)) == ST_TEXT_DIRECTION_RTL) fade_area[0][0] += clutter_actor_get_width (vscroll); fade_area[1][0] -= clutter_actor_get_width (vscroll); } if (h_scroll_visible) fade_area[1][1] -= clutter_actor_get_height (hscroll); st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size); if (self->offset_top_uniform > -1) { if (value > lower + 0.1) cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, self->fade_offset); else cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, 0.0f); } if (self->offset_bottom_uniform > -1) { if (value < upper - page_size - 0.1) cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, self->fade_offset); else cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, 0.0f); } if (self->tex_uniform > -1) cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0); if (self->height_uniform > -1) cogl_program_set_uniform_1f (self->program, self->height_uniform, clutter_actor_get_height (self->actor)); if (self->width_uniform > -1) cogl_program_set_uniform_1f (self->program, self->width_uniform, clutter_actor_get_width (self->actor)); if (self->fade_area_uniform > -1) cogl_program_set_uniform_matrix (self->program, self->fade_area_uniform, 2, 1, FALSE, (const float *)fade_area); material = clutter_offscreen_effect_get_target (effect); cogl_material_set_user_program (material, self->program); out: parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (st_scroll_view_fade_parent_class); parent->paint_target (effect); } static void on_vadjustment_changed (StAdjustment *adjustment, ClutterEffect *effect) { gdouble value, lower, upper, page_size; gboolean needs_fade; st_adjustment_get_values (adjustment, &value, &lower, &upper, NULL, NULL, &page_size); needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), needs_fade); } static void st_scroll_view_fade_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (meta); ClutterActorMetaClass *parent; g_return_if_fail (actor == NULL || ST_IS_SCROLL_VIEW (actor)); if (self->shader == COGL_INVALID_HANDLE) { clutter_actor_meta_set_enabled (meta, FALSE); return; } if (self->vadjustment) { g_signal_handlers_disconnect_by_func (self->vadjustment, (gpointer)on_vadjustment_changed, self); self->vadjustment = NULL; } if (actor) { StScrollView *scroll_view = ST_SCROLL_VIEW (actor); StScrollBar *vscroll = ST_SCROLL_BAR (st_scroll_view_get_vscroll_bar (scroll_view)); self->vadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (vscroll)); g_signal_connect (self->vadjustment, "changed", G_CALLBACK (on_vadjustment_changed), self); on_vadjustment_changed (self->vadjustment, CLUTTER_EFFECT (self)); } parent = CLUTTER_ACTOR_META_CLASS (st_scroll_view_fade_parent_class); parent->set_actor (meta, actor); /* we keep a back pointer here, to avoid going through the ActorMeta */ self->actor = clutter_actor_meta_get_actor (meta); } static void st_scroll_view_fade_dispose (GObject *gobject) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (gobject); if (self->program != COGL_INVALID_HANDLE) { cogl_handle_unref (self->program); self->program = COGL_INVALID_HANDLE; self->shader = COGL_INVALID_HANDLE; } if (self->vadjustment) { g_signal_handlers_disconnect_by_func (self->vadjustment, (gpointer)on_vadjustment_changed, self); self->vadjustment = NULL; } self->actor = NULL; G_OBJECT_CLASS (st_scroll_view_fade_parent_class)->dispose (gobject); } static void st_scroll_view_fade_set_offset (StScrollViewFade *self, float fade_offset) { if (self->fade_offset == fade_offset) return; g_object_freeze_notify (G_OBJECT (self)); self->fade_offset = fade_offset; if (self->actor != NULL) clutter_actor_queue_redraw (self->actor); g_object_notify (G_OBJECT (self), "fade-offset"); g_object_thaw_notify (G_OBJECT (self)); } static void st_scroll_view_fade_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object); switch (prop_id) { case PROP_FADE_OFFSET: st_scroll_view_fade_set_offset (self, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_scroll_view_fade_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object); switch (prop_id) { case PROP_FADE_OFFSET: g_value_set_float (value, self->fade_offset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_scroll_view_fade_class_init (StScrollViewFadeClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); gobject_class->dispose = st_scroll_view_fade_dispose; gobject_class->get_property = st_scroll_view_fade_get_property; gobject_class->set_property = st_scroll_view_fade_set_property; meta_class->set_actor = st_scroll_view_fade_set_actor; effect_class->pre_paint = st_scroll_view_fade_pre_paint; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->create_texture = st_scroll_view_fade_create_texture; offscreen_class->paint_target = st_scroll_view_fade_paint_target; g_object_class_install_property (gobject_class, PROP_FADE_OFFSET, g_param_spec_float ("fade-offset", "Fade Offset", "The height of the area which is faded at the edge", 0.f, G_MAXFLOAT, DEFAULT_FADE_OFFSET, G_PARAM_READWRITE)); } static void st_scroll_view_fade_init (StScrollViewFade *self) { static CoglHandle shader = COGL_INVALID_HANDLE; if (shader == COGL_INVALID_HANDLE) { if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); cogl_shader_source (shader, fade_glsl_shader); cogl_shader_compile (shader); if (!cogl_shader_is_compiled (shader)) { gchar *log_buf = cogl_shader_get_info_log (shader); g_warning (G_STRLOC ": Unable to compile the fade shader: %s", log_buf); g_free (log_buf); cogl_handle_unref (shader); shader = COGL_INVALID_HANDLE; } } } self->shader = shader; self->is_attached = FALSE; self->tex_uniform = -1; self->height_uniform = -1; self->width_uniform = -1; self->fade_area_uniform = -1; self->offset_top_uniform = -1; self->offset_bottom_uniform = -1; self->fade_offset = DEFAULT_FADE_OFFSET; if (shader != COGL_INVALID_HANDLE) cogl_handle_ref (self->shader); } ClutterEffect * st_scroll_view_fade_new (void) { return g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL); } Cinnamon-2.8.6/src/st/st-scrollable.c0000664000175000017500000001433612625666430016365 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scrollable.c: Scrollable interface * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "st-scrollable.h" /** * SECTION:st-scrollable * @short_description: A #ClutterActor that can be scrolled * * The #StScrollable interface is exposed by actors that support scrolling. * * The interface contains methods for getting and setting the adjustments * for scrolling; these adjustments will be used to hook the scrolled * position up to scrollbars or other external controls. When a #StScrollable * is added to a parent container, the parent container is responsible * for setting the adjustments. The parent container then sets the adjustments * back to %NULL when the scrollable is removed. * * For #StScrollable supporting height-for-width size negotation, size * negotation works as follows: * * In response to get_preferred_width(), the scrollable should report * the minimum width at which horizontal scrolling is needed for the * preferred width, and natural width of the actor when not * horizontally scrolled as the natural width. * * The for_width passed into get_preferred_height() is the width at which * the scrollable will be allocated; this will be smaller than the minimum * width when scrolling horizontally, so the scrollable may want to adjust * it up to the minimum width before computing a preferred height. (Other * scrollables may want to fit as much content into the allocated area * as possible and only scroll what absolutely needs to scroll - consider, * for example, the line-wrapping behavior of a text editor where there * is a long line without any spaces.) As for width, get_preferred_height() * should return the minimum size at which no scrolling is needed for the * minimum height, and the natural size of the actor when not vertically scrolled * as the natural height. * * In allocate() the allocation box passed in will be actual allocated * size of the actor so will be smaller than the reported minimum * width and/or height when scrolling is present. Any scrollable actor * must support being allocated at any size down to 0x0 without * crashing, however if the actor has content around the scrolled area * and has an absolute minimum size that's bigger than 0x0 its * acceptable for it to misdraw between 0x0 and the absolute minimum * size. It's up to the application author to avoid letting the user * resize the scroll view small enough so that the scrolled area * vanishes. * * In response to allocate, in addition to normal handling, the * scrollable should also set the limits of the the horizontal and * vertical adjustments that were set on it earlier. The standard * settings are: * * lower: 0 * page_size: allocated size (width or height) * upper: MAX (total size of the scrolled area,allocated_size) * step_increment: natural row/column height or a fixed fraction of the page size * page_increment: page_size - step_increment */ static void st_scrollable_base_init (gpointer g_iface) { static gboolean initialized = FALSE; if (!initialized) { g_object_interface_install_property (g_iface, g_param_spec_object ("hadjustment", "StAdjustment", "Horizontal adjustment", ST_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_object ("vadjustment", "StAdjustment", "Vertical adjustment", ST_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); initialized = TRUE; } } GType st_scrollable_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (StScrollableInterface), st_scrollable_base_init, /* base_init */ NULL, }; type = g_type_register_static (G_TYPE_INTERFACE, "StScrollable", &info, 0); } return type; } void st_scrollable_set_adjustments (StScrollable *scrollable, StAdjustment *hadjustment, StAdjustment *vadjustment) { ST_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable, hadjustment, vadjustment); } /** * st_scroll_bar_get_adjustments: * @hadjustment: (transfer none) (out) (allow-none): location to store the horizontal adjustment, or %NULL * @vadjustment: (transfer none) (out) (allow-none): location to store the vertical adjustment, or %NULL * * Gets the adjustment objects that store the offsets of the scrollable widget * into its possible scrolling area. */ void st_scrollable_get_adjustments (StScrollable *scrollable, StAdjustment **hadjustment, StAdjustment **vadjustment) { ST_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable, hadjustment, vadjustment); } Cinnamon-2.8.6/src/st/st-im-text.h0000664000175000017500000000443212625666430015633 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-im-text.h: Text widget with input method support * * Copyright 2009 Red Hat, Inc. * * This is a copy of ClutterIMText converted to use GtkIMContext rather * than ClutterIMContext. Original code: * * Author: raymond liu * * Copyright (C) 2009, Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_IM_TEXT_H__ #define __ST_IM_TEXT_H__ G_BEGIN_DECLS #include #define ST_TYPE_IM_TEXT (st_im_text_get_type ()) #define ST_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_IM_TEXT, StIMText)) #define ST_IS_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_IM_TEXT)) #define ST_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_IM_TEXT, StIMTextClass)) #define ST_IS_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_IM_TEXT)) #define ST_IM_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_IM_TEXT, StIMTextClass)) typedef struct _StIMText StIMText; typedef struct _StIMTextPrivate StIMTextPrivate; typedef struct _StIMTextClass StIMTextClass; struct _StIMText { ClutterText parent_instance; StIMTextPrivate *priv; }; struct _StIMTextClass { ClutterTextClass parent_class; }; GType st_im_text_get_type (void) G_GNUC_CONST; ClutterActor *st_im_text_new (const gchar *text); void st_im_text_set_autoshow_im (StIMText *self, gboolean autoshow); G_END_DECLS #endif /* __ST_IM_TEXT_H__ */ Cinnamon-2.8.6/src/st/st-scroll-bar.c0000664000175000017500000011322412625666430016277 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-bar.c: Scroll bar actor * * Copyright 2008 OpenedHand * Copyright 2008, 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010 Maxim Ermilov * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-scroll-bar * @short_description: a user interface element to control scrollable areas. * * The #StScrollBar allows users to scroll scrollable actors, either by * the step or page amount, or by manually dragging the handle. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "st-scroll-bar.h" #include "st-bin.h" #include "st-marshal.h" #include "st-enum-types.h" #include "st-private.h" #include "st-button.h" G_DEFINE_TYPE (StScrollBar, st_scroll_bar, ST_TYPE_WIDGET) #define ST_SCROLL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_SCROLL_BAR, StScrollBarPrivate)) #define PAGING_INITIAL_REPEAT_TIMEOUT 500 #define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200 struct _StScrollBarPrivate { StAdjustment *adjustment; gulong capture_handler; gfloat x_origin; gfloat y_origin; ClutterActor *bw_stepper; ClutterActor *fw_stepper; ClutterActor *trough; ClutterActor *handle; gfloat move_x; gfloat move_y; /* Trough-click handling. */ enum { NONE, UP, DOWN } paging_direction; guint paging_source_id; guint paging_event_no; gboolean stepper_forward; guint stepper_source_id; ClutterAnimation *paging_animation; guint vertical : 1; }; enum { PROP_0, PROP_ADJUSTMENT, PROP_VERTICAL }; enum { SCROLL_START, SCROLL_STOP, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; extern gfloat st_slow_down_factor; static gboolean handle_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *bar); static void stop_scrolling (StScrollBar *bar); static void st_scroll_bar_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StScrollBarPrivate *priv = ST_SCROLL_BAR (gobject)->priv; switch (prop_id) { case PROP_ADJUSTMENT: g_value_set_object (value, priv->adjustment); break; case PROP_VERTICAL: g_value_set_boolean (value, priv->vertical); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_scroll_bar_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StScrollBar *bar = ST_SCROLL_BAR (gobject); switch (prop_id) { case PROP_ADJUSTMENT: st_scroll_bar_set_adjustment (bar, g_value_get_object (value)); break; case PROP_VERTICAL: bar->priv->vertical = g_value_get_boolean (value); if (bar->priv->vertical) { clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper), "up-stepper"); clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper), "down-stepper"); clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle), "vhandle"); } else { clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper), "forward-stepper"); clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper), "backward-stepper"); clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle), "hhandle"); } clutter_actor_queue_relayout ((ClutterActor*) gobject); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_scroll_bar_dispose (GObject *gobject) { StScrollBar *bar = ST_SCROLL_BAR (gobject); StScrollBarPrivate *priv = bar->priv; if (priv->adjustment) st_scroll_bar_set_adjustment (bar, NULL); if (priv->handle) { g_signal_handlers_disconnect_by_func (priv->handle, G_CALLBACK (handle_button_press_event_cb), bar); clutter_actor_unparent (priv->handle); priv->handle = NULL; } if (priv->bw_stepper) { clutter_actor_unparent (priv->bw_stepper); priv->bw_stepper = NULL; } if (priv->fw_stepper) { clutter_actor_unparent (priv->fw_stepper); priv->fw_stepper = NULL; } if (priv->trough) { clutter_actor_unparent (priv->trough); priv->trough = NULL; } G_OBJECT_CLASS (st_scroll_bar_parent_class)->dispose (gobject); } static void st_scroll_bar_paint (ClutterActor *actor) { StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->paint (actor); clutter_actor_paint (priv->bw_stepper); clutter_actor_paint (priv->fw_stepper); clutter_actor_paint (priv->trough); if (priv->handle && CLUTTER_ACTOR_IS_VISIBLE (priv->handle)) clutter_actor_paint (priv->handle); } static void st_scroll_bar_pick (ClutterActor *actor, const ClutterColor *pick_color) { StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->pick (actor, pick_color); clutter_actor_paint (priv->bw_stepper); clutter_actor_paint (priv->fw_stepper); clutter_actor_paint (priv->trough); if (priv->handle && priv->adjustment) clutter_actor_paint (priv->handle); } static void st_scroll_bar_unmap (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor); stop_scrolling (ST_SCROLL_BAR (actor)); } static void scroll_bar_allocate_children (StScrollBar *bar, const ClutterActorBox *box, ClutterAllocationFlags flags) { StScrollBarPrivate *priv = bar->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (bar)); ClutterActorBox content_box, bw_box, fw_box, trough_box; gfloat bw_stepper_size, fw_stepper_size, min_size, natural_size; st_theme_node_get_content_box (theme_node, box, &content_box); if (priv->vertical) { gfloat width = content_box.x2 - content_box.x1; clutter_actor_get_preferred_height (priv->bw_stepper, width, &min_size, &natural_size); bw_stepper_size = MAX (min_size, natural_size); /* Backward stepper */ bw_box.x1 = content_box.x1; bw_box.y1 = content_box.y1; bw_box.x2 = content_box.x2; bw_box.y2 = bw_box.y1 + bw_stepper_size; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); clutter_actor_get_preferred_height (priv->fw_stepper, width, &min_size, &natural_size); fw_stepper_size = MAX (min_size, natural_size); /* Forward stepper */ fw_box.x1 = content_box.x1; fw_box.y1 = content_box.y2 - fw_stepper_size; fw_box.x2 = content_box.x2; fw_box.y2 = content_box.y2; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = content_box.x1; trough_box.y1 = content_box.y1 + bw_stepper_size; trough_box.x2 = content_box.x2; trough_box.y2 = content_box.y2 - fw_stepper_size; clutter_actor_allocate (priv->trough, &trough_box, flags); } else { gfloat height = content_box.y2 - content_box.y1; clutter_actor_get_preferred_width (priv->bw_stepper, height, &min_size, &natural_size); bw_stepper_size = MAX (min_size, natural_size); /* Backward stepper */ bw_box.x1 = content_box.x1; bw_box.y1 = content_box.y1; bw_box.x2 = bw_box.x1 + bw_stepper_size; bw_box.y2 = content_box.y2; clutter_actor_allocate (priv->bw_stepper, &bw_box, flags); clutter_actor_get_preferred_width (priv->fw_stepper, height, &min_size, &natural_size); fw_stepper_size = MAX (min_size, natural_size); /* Forward stepper */ fw_box.x1 = content_box.x2 - fw_stepper_size; fw_box.y1 = content_box.y1; fw_box.x2 = content_box.x2; fw_box.y2 = content_box.y2; clutter_actor_allocate (priv->fw_stepper, &fw_box, flags); /* Trough */ trough_box.x1 = content_box.x1 + bw_stepper_size; trough_box.y1 = content_box.y1; trough_box.x2 = content_box.x2 - fw_stepper_size; trough_box.y2 = content_box.y2; clutter_actor_allocate (priv->trough, &trough_box, flags); } if (priv->adjustment) { float handle_size, position, avail_size, stepper_size; gdouble value, lower, upper, page_size, increment, min_size, max_size; ClutterActorBox handle_box = { 0, }; stepper_size = bw_stepper_size + fw_stepper_size; st_adjustment_get_values (priv->adjustment, &value, &lower, &upper, NULL, NULL, &page_size); if ((upper == lower) || (page_size >= (upper - lower))) increment = 1.0; else increment = page_size / (upper - lower); min_size = 32.; st_theme_node_lookup_length (theme_node, "min-size", FALSE, &min_size); max_size = G_MAXINT16; st_theme_node_lookup_length (theme_node, "max-size", FALSE, &max_size); if (upper - lower - page_size <= 0) position = 0; else position = (value - lower) / (upper - lower - page_size); if (priv->vertical) { avail_size = content_box.y2 - content_box.y1 - stepper_size; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_box.x1 = content_box.x1; handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size); handle_box.x2 = content_box.x2; handle_box.y2 = handle_box.y1 + handle_size; } else { avail_size = content_box.x2 - content_box.x1 - stepper_size; handle_size = increment * avail_size; handle_size = CLAMP (handle_size, min_size, max_size); handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size); handle_box.y1 = content_box.y1; handle_box.x2 = handle_box.x1 + handle_size; handle_box.y2 = content_box.y2; } /* snap to pixel */ handle_box.x1 = (int) handle_box.x1; handle_box.y1 = (int) handle_box.y1; handle_box.x2 = (int) handle_box.x2; handle_box.y2 = (int) handle_box.y2; clutter_actor_allocate (priv->handle, &handle_box, flags); } } static void st_scroll_bar_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StScrollBar *bar = ST_SCROLL_BAR (self); StScrollBarPrivate *priv = bar->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_height (theme_node, &for_height); if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; if (priv->vertical) { gfloat tmin_width_p, tnatural_width_p; #define ADJUST_WIDTH_IF_LARGER(actor) \ _st_actor_get_preferred_width (actor, for_height, TRUE, \ &tmin_width_p, &tnatural_width_p); \ if (min_width_p && tmin_width_p > *min_width_p) \ *min_width_p = tmin_width_p; \ if (natural_width_p && tnatural_width_p > *natural_width_p) \ *natural_width_p = tnatural_width_p; ADJUST_WIDTH_IF_LARGER (priv->bw_stepper); ADJUST_WIDTH_IF_LARGER (priv->fw_stepper); ADJUST_WIDTH_IF_LARGER (priv->trough); ADJUST_WIDTH_IF_LARGER (priv->handle); #undef ADJUST_WIDTH_IF_LARGER } else { gfloat tmin_width_p, tnatural_width_p; #define ADD_TO_WIDTH(actor) \ _st_actor_get_preferred_width (actor, for_height, TRUE, \ &tmin_width_p, &tnatural_width_p); \ if (min_width_p) \ *min_width_p += tmin_width_p; \ if (natural_width_p ) \ *natural_width_p += tnatural_width_p; ADD_TO_WIDTH (priv->bw_stepper); ADD_TO_WIDTH (priv->fw_stepper); ADD_TO_WIDTH (priv->trough); ADD_TO_WIDTH (priv->handle); #undef ADD_TO_WIDTH } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_scroll_bar_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StScrollBar *bar = ST_SCROLL_BAR (self); StScrollBarPrivate *priv = bar->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_width (theme_node, &for_width); if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; if (priv->vertical) { gfloat tmin_height_p, tnatural_height_p; #define ADD_TO_HEIGHT(actor) \ _st_actor_get_preferred_height (actor, for_width, FALSE, \ &tmin_height_p, &tnatural_height_p); \ if (min_height_p) \ *min_height_p += tmin_height_p; \ if (natural_height_p) \ *natural_height_p += tnatural_height_p; ADD_TO_HEIGHT (priv->bw_stepper); ADD_TO_HEIGHT (priv->fw_stepper); ADD_TO_HEIGHT (priv->trough); ADD_TO_HEIGHT (priv->handle); #undef ADD_TO_HEIGHT } else { gfloat tmin_height_p, tnatural_height_p; #define ADJUST_HEIGHT_IF_LARGER(actor) \ _st_actor_get_preferred_height (actor, for_width, FALSE, \ &tmin_height_p, &tnatural_height_p); \ if (min_height_p && tmin_height_p > *min_height_p) \ *min_height_p = tmin_height_p; \ if (natural_height_p && tnatural_height_p > *natural_height_p) \ *natural_height_p = tnatural_height_p; ADJUST_HEIGHT_IF_LARGER (priv->bw_stepper); ADJUST_HEIGHT_IF_LARGER (priv->fw_stepper); ADJUST_HEIGHT_IF_LARGER (priv->trough); ADJUST_HEIGHT_IF_LARGER (priv->handle); #undef ADJUST_HEIGHT_IF_LARGER } st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_scroll_bar_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StScrollBar *bar = ST_SCROLL_BAR (actor); /* Chain up */ CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->allocate (actor, box, flags); scroll_bar_allocate_children (bar, box, flags); } static void scroll_bar_update_positions (StScrollBar *bar) { ClutterActorBox box; /* Due to a change in the adjustments, we need to reposition our * children; since adjustments changes can come from allocation * changes in the scrolled area, we can't just queue a new relayout - * we may already be in a relayout cycle. On the other hand, if * a relayout is already queued, we can't just go ahead and allocate * our children, since we don't have a valid allocation, and calling * clutter_actor_get_allocation_box() will trigger an immediate * stage relayout. So what we do is go ahead and immediately * allocate our children if we already have a valid allocation, and * otherwise just wait for the queued relayout. */ if (!clutter_actor_has_allocation (CLUTTER_ACTOR (bar))) return; clutter_actor_get_allocation_box (CLUTTER_ACTOR (bar), &box); scroll_bar_allocate_children (bar, &box, CLUTTER_ALLOCATION_NONE); } static void st_scroll_bar_style_changed (StWidget *widget) { StScrollBarPrivate *priv = ST_SCROLL_BAR (widget)->priv; st_widget_style_changed (ST_WIDGET (priv->bw_stepper)); st_widget_style_changed (ST_WIDGET (priv->fw_stepper)); st_widget_style_changed (ST_WIDGET (priv->trough)); st_widget_style_changed (ST_WIDGET (priv->handle)); ST_WIDGET_CLASS (st_scroll_bar_parent_class)->style_changed (widget); } static void bar_reactive_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data) { StScrollBar *bar = ST_SCROLL_BAR (gobject); clutter_actor_set_reactive (bar->priv->handle, clutter_actor_get_reactive (CLUTTER_ACTOR (bar))); } static GObject* st_scroll_bar_constructor (GType type, guint n_properties, GObjectConstructParam *properties) { GObjectClass *gobject_class; GObject *obj; StScrollBar *bar; gobject_class = G_OBJECT_CLASS (st_scroll_bar_parent_class); obj = gobject_class->constructor (type, n_properties, properties); bar = ST_SCROLL_BAR (obj); g_signal_connect (bar, "notify::reactive", G_CALLBACK (bar_reactive_notify_cb), NULL); return obj; } static gboolean st_scroll_bar_scroll_event (ClutterActor *actor, ClutterScrollEvent *event) { StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; gdouble step, value, delta_x, delta_y; if (priv->adjustment) { g_object_get (priv->adjustment, "step-increment", &step, "value", &value, NULL); } else { return FALSE; } switch (event->direction) { case CLUTTER_SCROLL_SMOOTH: clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y); if (fabs (delta_x) > fabs(delta_y)) st_adjustment_set_value (priv->adjustment, value + delta_x); else st_adjustment_set_value (priv->adjustment, value + delta_y); break; case CLUTTER_SCROLL_UP: case CLUTTER_SCROLL_LEFT: st_adjustment_set_value (priv->adjustment, value - step); break; case CLUTTER_SCROLL_DOWN: case CLUTTER_SCROLL_RIGHT: st_adjustment_set_value (priv->adjustment, value + step); break; } return TRUE; } static void st_scroll_bar_class_init (StScrollBarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StScrollBarPrivate)); object_class->get_property = st_scroll_bar_get_property; object_class->set_property = st_scroll_bar_set_property; object_class->dispose = st_scroll_bar_dispose; object_class->constructor = st_scroll_bar_constructor; actor_class->get_preferred_width = st_scroll_bar_get_preferred_width; actor_class->get_preferred_height = st_scroll_bar_get_preferred_height; actor_class->allocate = st_scroll_bar_allocate; actor_class->paint = st_scroll_bar_paint; actor_class->pick = st_scroll_bar_pick; actor_class->scroll_event = st_scroll_bar_scroll_event; actor_class->unmap = st_scroll_bar_unmap; widget_class->style_changed = st_scroll_bar_style_changed; g_object_class_install_property (object_class, PROP_ADJUSTMENT, g_param_spec_object ("adjustment", "Adjustment", "The adjustment", ST_TYPE_ADJUSTMENT, ST_PARAM_READWRITE)); pspec = g_param_spec_boolean ("vertical", "Vertical Orientation", "Vertical Orientation", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VERTICAL, pspec); signals[SCROLL_START] = g_signal_new ("scroll-start", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StScrollBarClass, scroll_start), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[SCROLL_STOP] = g_signal_new ("scroll-stop", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StScrollBarClass, scroll_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void move_slider (StScrollBar *bar, gfloat x, gfloat y) { StScrollBarPrivate *priv = bar->priv; gdouble position, lower, upper, page_size; gfloat ux, uy, pos, size; if (!priv->adjustment) return; if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy)) return; if (priv->vertical) size = clutter_actor_get_height (priv->trough) - clutter_actor_get_height (priv->handle); else size = clutter_actor_get_width (priv->trough) - clutter_actor_get_width (priv->handle); if (size == 0) return; if (priv->vertical) pos = uy - priv->y_origin; else pos = ux - priv->x_origin; pos = CLAMP (pos, 0, size); st_adjustment_get_values (priv->adjustment, NULL, &lower, &upper, NULL, NULL, &page_size); position = ((pos / size) * (upper - lower - page_size)) + lower; st_adjustment_set_value (priv->adjustment, position); } static void stop_scrolling (StScrollBar *bar) { ClutterStage *stage; if (!bar->priv->capture_handler) return; stage = CLUTTER_STAGE (clutter_actor_get_stage (bar->priv->trough)); g_signal_handler_disconnect (stage, bar->priv->capture_handler); bar->priv->capture_handler = 0; clutter_stage_set_motion_events_enabled (stage, TRUE); g_signal_emit (bar, signals[SCROLL_STOP], 0); } static gboolean handle_capture_event_cb (ClutterActor *trough, ClutterEvent *event, StScrollBar *bar) { if (clutter_event_type (event) == CLUTTER_MOTION) { move_slider (bar, ((ClutterMotionEvent*) event)->x, ((ClutterMotionEvent*) event)->y); } else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && ((ClutterButtonEvent*) event)->button == 1) { ClutterActor *stage, *target; stop_scrolling (bar); /* check if the mouse pointer has left the handle during the drag and * remove the hover state if it has */ stage = clutter_actor_get_stage (bar->priv->trough); target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage, CLUTTER_PICK_REACTIVE, ((ClutterButtonEvent*) event)->x, ((ClutterButtonEvent*) event)->y); if (target != bar->priv->handle) { st_widget_remove_style_pseudo_class ((StWidget*) bar->priv->handle, "hover"); } } return TRUE; } static gboolean handle_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *bar) { ClutterStage *stage; StScrollBarPrivate *priv = bar->priv; if (event->button != 1) return FALSE; if (!clutter_actor_transform_stage_point (priv->handle, event->x, event->y, &priv->x_origin, &priv->y_origin)) return FALSE; /* Account for the scrollbar-trough-handle nesting. */ priv->x_origin += clutter_actor_get_x (priv->trough); priv->y_origin += clutter_actor_get_y (priv->trough); stage = CLUTTER_STAGE (clutter_actor_get_stage (bar->priv->trough)); /* Turn off picking for motion events */ clutter_stage_set_motion_events_enabled (stage, FALSE); priv->capture_handler = g_signal_connect_after ( clutter_actor_get_stage (priv->trough), "captured-event", G_CALLBACK (handle_capture_event_cb), bar); g_signal_emit (bar, signals[SCROLL_START], 0); return TRUE; } static void animation_completed_cb (ClutterAnimation *animation, StScrollBarPrivate *priv) { g_object_unref (priv->paging_animation); priv->paging_animation = NULL; } static gboolean trough_paging_cb (StScrollBar *self) { gfloat handle_pos, event_pos, tx, ty; gdouble value; gdouble page_increment; gboolean ret; gulong mode; ClutterAnimation *a; GValue v = { 0, }; ClutterTimeline *t; if (self->priv->paging_event_no == 0) { /* Scroll on after initial timeout. */ mode = CLUTTER_EASE_OUT_CUBIC; ret = FALSE; self->priv->paging_event_no = 1; self->priv->paging_source_id = g_timeout_add ( PAGING_INITIAL_REPEAT_TIMEOUT, (GSourceFunc) trough_paging_cb, self); } else if (self->priv->paging_event_no == 1) { /* Scroll on after subsequent timeout. */ ret = FALSE; mode = CLUTTER_EASE_IN_CUBIC; self->priv->paging_event_no = 2; self->priv->paging_source_id = g_timeout_add ( PAGING_SUBSEQUENT_REPEAT_TIMEOUT, (GSourceFunc) trough_paging_cb, self); } else { /* Keep scrolling. */ ret = TRUE; mode = CLUTTER_LINEAR; self->priv->paging_event_no++; } /* Do the scrolling */ st_adjustment_get_values (self->priv->adjustment, &value, NULL, NULL, NULL, &page_increment, NULL); if (self->priv->vertical) handle_pos = clutter_actor_get_y (self->priv->handle); else handle_pos = clutter_actor_get_x (self->priv->handle); clutter_actor_transform_stage_point (CLUTTER_ACTOR (self->priv->trough), self->priv->move_x, self->priv->move_y, &tx, &ty); if (self->priv->vertical) event_pos = ty; else event_pos = tx; if (event_pos > handle_pos) { if (self->priv->paging_direction == NONE) { /* Remember direction. */ self->priv->paging_direction = DOWN; } if (self->priv->paging_direction == UP) { /* Scrolled far enough. */ return FALSE; } value += page_increment; } else { if (self->priv->paging_direction == NONE) { /* Remember direction. */ self->priv->paging_direction = UP; } if (self->priv->paging_direction == DOWN) { /* Scrolled far enough. */ return FALSE; } value -= page_increment; } if (self->priv->paging_animation) { clutter_animation_completed (self->priv->paging_animation); } /* FIXME: Creating a new animation for each scroll is probably not the best * idea, but it's a lot less involved than extenind the current animation */ a = self->priv->paging_animation = g_object_new (CLUTTER_TYPE_ANIMATION, "object", self->priv->adjustment, "duration", (guint)(PAGING_SUBSEQUENT_REPEAT_TIMEOUT * st_slow_down_factor), "mode", mode, NULL); g_value_init (&v, G_TYPE_DOUBLE); g_value_set_double (&v, value); clutter_animation_bind (self->priv->paging_animation, "value", &v); t = clutter_animation_get_timeline (self->priv->paging_animation); g_signal_connect (a, "completed", G_CALLBACK (animation_completed_cb), self->priv); clutter_timeline_start (t); return ret; } static gboolean trough_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *self) { g_return_val_if_fail (self, FALSE); if (event->button != 1) return FALSE; if (self->priv->adjustment == NULL) return FALSE; self->priv->move_x = event->x; self->priv->move_y = event->y; self->priv->paging_direction = NONE; self->priv->paging_event_no = 0; trough_paging_cb (self); return TRUE; } static gboolean trough_button_release_event_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *self) { if (event->button != 1) return FALSE; if (self->priv->paging_source_id) { g_source_remove (self->priv->paging_source_id); self->priv->paging_source_id = 0; } return TRUE; } static gboolean trough_leave_event_cb (ClutterActor *actor, ClutterEvent *event, StScrollBar *self) { if (self->priv->paging_source_id) { g_source_remove (self->priv->paging_source_id); self->priv->paging_source_id = 0; return TRUE; } return FALSE; } static void stepper_animation_completed_cb (ClutterAnimation *a, gpointer data) { g_object_unref (a); } static void stepper_move_on (StScrollBarPrivate *priv, gint mode) { ClutterAnimation *a; ClutterTimeline *t; GValue v = { 0, }; double value, inc; a = g_object_new (CLUTTER_TYPE_ANIMATION, "object", priv->adjustment, "duration", (guint)(PAGING_SUBSEQUENT_REPEAT_TIMEOUT * st_slow_down_factor), "mode", mode, NULL); g_signal_connect (a, "completed", G_CALLBACK (stepper_animation_completed_cb), NULL); g_object_get (priv->adjustment, "step-increment", &inc, "value", &value, NULL); if (priv->stepper_forward) value = value + inc; else value = value - inc; g_value_init (&v, G_TYPE_DOUBLE); g_value_set_double (&v, value); clutter_animation_bind (a, "value", &v); t = clutter_animation_get_timeline (a); clutter_timeline_start (t); } static gboolean stepper_button_subsequent_timeout (StScrollBarPrivate *priv) { stepper_move_on (priv, CLUTTER_LINEAR); return TRUE; } static gboolean stepper_button_repeat_timeout (StScrollBarPrivate *priv) { priv->stepper_source_id = 0; stepper_move_on (priv, CLUTTER_EASE_IN_CUBIC); priv->stepper_source_id = g_timeout_add (PAGING_SUBSEQUENT_REPEAT_TIMEOUT, (GSourceFunc) stepper_button_subsequent_timeout, priv); return FALSE; } static gboolean stepper_button_press_event_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *bar) { StScrollBarPrivate *priv = bar->priv; if (event->button != 1) return FALSE; if (bar->priv->adjustment == NULL) return FALSE; bar->priv->stepper_forward = (actor == priv->fw_stepper); stepper_move_on (priv, CLUTTER_EASE_OUT_CUBIC); priv->stepper_source_id = g_timeout_add (PAGING_INITIAL_REPEAT_TIMEOUT, (GSourceFunc) stepper_button_repeat_timeout, priv); return TRUE; } static gboolean stepper_button_release_cb (ClutterActor *actor, ClutterButtonEvent *event, StScrollBar *self) { if (event->button != 1) return FALSE; if (self->priv->stepper_source_id) { g_source_remove (self->priv->stepper_source_id); self->priv->stepper_source_id = 0; } return FALSE; } static void st_scroll_bar_notify_reactive (StScrollBar *self) { StScrollBarPrivate *priv = self->priv; gboolean reactive = CLUTTER_ACTOR_IS_REACTIVE (self); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->bw_stepper), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->fw_stepper), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive); } static void st_scroll_bar_init (StScrollBar *self) { self->priv = ST_SCROLL_BAR_GET_PRIVATE (self); self->priv->bw_stepper = (ClutterActor *) st_button_new (); clutter_actor_set_name (CLUTTER_ACTOR (self->priv->bw_stepper), "backward-stepper"); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (self->priv->bw_stepper)); g_signal_connect (self->priv->bw_stepper, "button-press-event", G_CALLBACK (stepper_button_press_event_cb), self); g_signal_connect (self->priv->bw_stepper, "button-release-event", G_CALLBACK (stepper_button_release_cb), self); self->priv->fw_stepper = (ClutterActor *) st_button_new (); clutter_actor_set_name (CLUTTER_ACTOR (self->priv->fw_stepper), "forward-stepper"); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (self->priv->fw_stepper)); g_signal_connect (self->priv->fw_stepper, "button-press-event", G_CALLBACK (stepper_button_press_event_cb), self); g_signal_connect (self->priv->fw_stepper, "button-release-event", G_CALLBACK (stepper_button_release_cb), self); self->priv->trough = (ClutterActor *) st_bin_new (); clutter_actor_set_reactive ((ClutterActor *) self->priv->trough, TRUE); clutter_actor_set_name (CLUTTER_ACTOR (self->priv->trough), "trough"); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (self->priv->trough)); g_signal_connect (self->priv->trough, "button-press-event", G_CALLBACK (trough_button_press_event_cb), self); g_signal_connect (self->priv->trough, "button-release-event", G_CALLBACK (trough_button_release_event_cb), self); g_signal_connect (self->priv->trough, "leave-event", G_CALLBACK (trough_leave_event_cb), self); self->priv->handle = (ClutterActor *) st_button_new (); clutter_actor_set_name (CLUTTER_ACTOR (self->priv->handle), "hhandle"); clutter_actor_add_child (self->priv->trough, CLUTTER_ACTOR (self->priv->handle)); g_signal_connect (self->priv->handle, "button-press-event", G_CALLBACK (handle_button_press_event_cb), self); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); g_signal_connect (self, "notify::reactive", G_CALLBACK (st_scroll_bar_notify_reactive), NULL); } StWidget * st_scroll_bar_new (StAdjustment *adjustment) { return g_object_new (ST_TYPE_SCROLL_BAR, "adjustment", adjustment, NULL); } static void on_notify_value (GObject *object, GParamSpec *pspec, StScrollBar *bar) { scroll_bar_update_positions (bar); } static void on_changed (StAdjustment *adjustment, StScrollBar *bar) { scroll_bar_update_positions (bar); } void st_scroll_bar_set_adjustment (StScrollBar *bar, StAdjustment *adjustment) { StScrollBarPrivate *priv; g_return_if_fail (ST_IS_SCROLL_BAR (bar)); priv = bar->priv; if (adjustment == priv->adjustment) return; if (priv->adjustment) { g_signal_handlers_disconnect_by_func (priv->adjustment, on_notify_value, bar); g_signal_handlers_disconnect_by_func (priv->adjustment, on_changed, bar); g_object_unref (priv->adjustment); priv->adjustment = NULL; } if (adjustment) { priv->adjustment = g_object_ref (adjustment); g_signal_connect (priv->adjustment, "notify::value", G_CALLBACK (on_notify_value), bar); g_signal_connect (priv->adjustment, "changed", G_CALLBACK (on_changed), bar); clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); } g_object_notify (G_OBJECT (bar), "adjustment"); } /** * st_scroll_bar_get_adjustment: * @bar: a #StScrollbar * * Gets the adjustment object that stores the current position * of the scrollbar. * * Return value: (transfer none): the adjustment */ StAdjustment * st_scroll_bar_get_adjustment (StScrollBar *bar) { g_return_val_if_fail (ST_IS_SCROLL_BAR (bar), NULL); return bar->priv->adjustment; } Cinnamon-2.8.6/src/st/st-table.h0000664000175000017500000000544312625666430015336 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-table.h: Table layout widget * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_TABLE_H__ #define __ST_TABLE_H__ #include #include G_BEGIN_DECLS /** * StTableChildOptions: * @ST_KEEP_ASPECT_RATIO: whether to respect the widget's aspect ratio * @ST_X_EXPAND: whether to allocate extra space on the widget's x-axis * @ST_Y_EXPAND: whether to allocate extra space on the widget's y-axis * @ST_X_FILL: whether to stretch the child to fill the cell horizontally * @ST_Y_FILL: whether to stretch the child to fill the cell vertically * * Denotes the child properties an StTable child will have. */ typedef enum { ST_KEEP_ASPECT_RATIO = 1 << 0, ST_X_EXPAND = 1 << 1, ST_Y_EXPAND = 1 << 2, ST_X_FILL = 1 << 3, ST_Y_FILL = 1 << 4 } StTableChildOptions; #define ST_TYPE_TABLE (st_table_get_type ()) #define ST_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE, StTable)) #define ST_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE)) #define ST_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE, StTableClass)) #define ST_IS_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE)) #define ST_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE, StTableClass)) typedef struct _StTable StTable; typedef struct _StTablePrivate StTablePrivate; typedef struct _StTableClass StTableClass; /** * StTable: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _StTable { /*< private >*/ StContainer parent_instance; StTablePrivate *priv; }; struct _StTableClass { StContainerClass parent_class; }; GType st_table_get_type (void) G_GNUC_CONST; StWidget* st_table_new (void); gint st_table_get_row_count (StTable *table); gint st_table_get_column_count (StTable *table); G_END_DECLS #endif /* __ST_TABLE_H__ */ Cinnamon-2.8.6/src/st/st-button.c0000664000175000017500000005017412625666430015556 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-button.c: Plain button actor * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-button * @short_description: Button widget * * A button widget with support for either a text label or icon, toggle mode * and transitions effects between states. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "st-button.h" #include "st-enum-types.h" #include "st-marshal.h" #include "st-texture-cache.h" #include "st-private.h" #include enum { PROP_0, PROP_LABEL, PROP_BUTTON_MASK, PROP_TOGGLE_MODE, PROP_CHECKED, PROP_PRESSED }; enum { CLICKED, LAST_SIGNAL }; #define ST_BUTTON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BUTTON, StButtonPrivate)) struct _StButtonPrivate { gchar *text; guint button_mask : 3; guint is_toggle : 1; guint pressed : 3; guint grabbed : 3; guint is_checked : 1; gint spacing; }; static guint button_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (StButton, st_button, ST_TYPE_BIN); static GType st_button_accessible_get_type (void) G_GNUC_CONST; static void st_button_update_label_style (StButton *button) { ClutterActor *label; label = st_bin_get_child (ST_BIN (button)); /* check the child is really a label */ if (!CLUTTER_IS_TEXT (label)) return; _st_set_text_from_style (CLUTTER_TEXT (label), st_widget_get_theme_node (ST_WIDGET (button))); } static void st_button_style_changed (StWidget *widget) { StButton *button = ST_BUTTON (widget); StButtonPrivate *priv = button->priv; StButtonClass *button_class = ST_BUTTON_GET_CLASS (button); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (button)); double spacing; ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget); spacing = 6; st_theme_node_lookup_length (theme_node, "border-spacing", FALSE, &spacing); priv->spacing = (int)(0.5 + spacing); /* update the label styling */ st_button_update_label_style (button); /* run a transition if applicable */ if (button_class->transition) { button_class->transition (button); } } static void st_button_press (StButton *button, StButtonMask mask) { if (button->priv->pressed == 0) st_widget_add_style_pseudo_class (ST_WIDGET (button), "active"); button->priv->pressed |= mask; } static void st_button_release (StButton *button, StButtonMask mask, int clicked_button) { button->priv->pressed &= ~mask; if (button->priv->pressed != 0) return; st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active"); if (clicked_button) { if (button->priv->is_toggle) st_button_set_checked (button, !button->priv->is_checked); g_signal_emit (button, button_signals[CLICKED], 0, clicked_button); } } static gboolean st_button_button_press (ClutterActor *actor, ClutterButtonEvent *event) { StButton *button = ST_BUTTON (actor); StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button); if (button->priv->button_mask & mask) { if (button->priv->grabbed == 0) clutter_grab_pointer (actor); button->priv->grabbed |= mask; st_button_press (button, mask); return TRUE; } return FALSE; } static gboolean st_button_button_release (ClutterActor *actor, ClutterButtonEvent *event) { StButton *button = ST_BUTTON (actor); StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button); if (button->priv->button_mask & mask) { gboolean is_click; is_click = button->priv->grabbed && st_widget_get_hover (ST_WIDGET (button)); st_button_release (button, mask, is_click ? event->button : 0); button->priv->grabbed &= ~mask; if (button->priv->grabbed == 0) clutter_ungrab_pointer (); return TRUE; } return FALSE; } static gboolean st_button_key_press (ClutterActor *actor, ClutterKeyEvent *event) { StButton *button = ST_BUTTON (actor); if (button->priv->button_mask & ST_BUTTON_ONE) { if (event->keyval == CLUTTER_KEY_space || event->keyval == CLUTTER_KEY_Return) { st_button_press (button, ST_BUTTON_ONE); return TRUE; } } return CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_press_event (actor, event); } static gboolean st_button_key_release (ClutterActor *actor, ClutterKeyEvent *event) { StButton *button = ST_BUTTON (actor); if (button->priv->button_mask & ST_BUTTON_ONE) { if (event->keyval == CLUTTER_KEY_space || event->keyval == CLUTTER_KEY_Return) { gboolean is_click; is_click = (button->priv->pressed & ST_BUTTON_ONE); st_button_release (button, ST_BUTTON_ONE, is_click ? 1 : 0); return TRUE; } } return FALSE; } static void st_button_key_focus_out (ClutterActor *actor) { StButton *button = ST_BUTTON (actor); /* If we lose focus between a key press and release, undo the press */ if ((button->priv->pressed & ST_BUTTON_ONE) && !(button->priv->grabbed & ST_BUTTON_ONE)) st_button_release (button, ST_BUTTON_ONE, 0); CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor); } static gboolean st_button_enter (ClutterActor *actor, ClutterCrossingEvent *event) { StButton *button = ST_BUTTON (actor); gboolean ret; ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event); if (button->priv->grabbed) { if (st_widget_get_hover (ST_WIDGET (button))) st_button_press (button, button->priv->grabbed); else st_button_release (button, button->priv->grabbed, 0); } return ret; } static gboolean st_button_leave (ClutterActor *actor, ClutterCrossingEvent *event) { StButton *button = ST_BUTTON (actor); gboolean ret; ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event); if (button->priv->grabbed) { if (st_widget_get_hover (ST_WIDGET (button))) st_button_press (button, button->priv->grabbed); else st_button_release (button, button->priv->grabbed, 0); } return ret; } static void st_button_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StButton *button = ST_BUTTON (gobject); switch (prop_id) { case PROP_LABEL: st_button_set_label (button, g_value_get_string (value)); break; case PROP_BUTTON_MASK: st_button_set_button_mask (button, g_value_get_flags (value)); break; case PROP_TOGGLE_MODE: st_button_set_toggle_mode (button, g_value_get_boolean (value)); break; case PROP_CHECKED: st_button_set_checked (button, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_button_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StButtonPrivate *priv = ST_BUTTON (gobject)->priv; switch (prop_id) { case PROP_LABEL: g_value_set_string (value, priv->text); break; case PROP_BUTTON_MASK: g_value_set_flags (value, priv->button_mask); break; case PROP_TOGGLE_MODE: g_value_set_boolean (value, priv->is_toggle); break; case PROP_CHECKED: g_value_set_boolean (value, priv->is_checked); break; case PROP_PRESSED: g_value_set_boolean (value, priv->pressed != 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_button_finalize (GObject *gobject) { StButtonPrivate *priv = ST_BUTTON (gobject)->priv; g_free (priv->text); G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject); } static void st_button_class_init (StButtonClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StButtonPrivate)); gobject_class->set_property = st_button_set_property; gobject_class->get_property = st_button_get_property; gobject_class->finalize = st_button_finalize; actor_class->button_press_event = st_button_button_press; actor_class->button_release_event = st_button_button_release; actor_class->key_press_event = st_button_key_press; actor_class->key_release_event = st_button_key_release; actor_class->key_focus_out = st_button_key_focus_out; actor_class->enter_event = st_button_enter; actor_class->leave_event = st_button_leave; widget_class->style_changed = st_button_style_changed; widget_class->get_accessible_type = st_button_accessible_get_type; pspec = g_param_spec_string ("label", "Label", "Label of the button", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LABEL, pspec); pspec = g_param_spec_flags ("button-mask", "Button mask", "Which buttons trigger the 'clicked' signal", ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_BUTTON_MASK, pspec); pspec = g_param_spec_boolean ("toggle-mode", "Toggle Mode", "Enable or disable toggling", FALSE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TOGGLE_MODE, pspec); pspec = g_param_spec_boolean ("checked", "Checked", "Indicates if a toggle button is \"on\"" " or \"off\"", FALSE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHECKED, pspec); pspec = g_param_spec_boolean ("pressed", "Pressed", "Indicates if the button is pressed in", FALSE, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_PRESSED, pspec); /** * StButton::clicked: * @button: the object that received the signal * @clicked_button: the mouse button that was used * * Emitted when the user activates the button, either with a mouse press and * release or with the keyboard. */ button_signals[CLICKED] = g_signal_new ("clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StButtonClass, clicked), NULL, NULL, _st_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); } static void st_button_init (StButton *button) { button->priv = ST_BUTTON_GET_PRIVATE (button); button->priv->spacing = 6; button->priv->button_mask = ST_BUTTON_ONE; clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE); st_widget_set_track_hover (ST_WIDGET (button), TRUE); } /** * st_button_new: * * Create a new button * * Returns: a new #StButton */ StWidget * st_button_new (void) { return g_object_new (ST_TYPE_BUTTON, NULL); } /** * st_button_new_with_label: * @text: text to set the label to * * Create a new #StButton with the specified label * * Returns: a new #StButton */ StWidget * st_button_new_with_label (const gchar *text) { return g_object_new (ST_TYPE_BUTTON, "label", text, NULL); } /** * st_button_get_label: * @button: a #StButton * * Get the text displayed on the button * * Returns: the text for the button. This must not be freed by the application */ const gchar * st_button_get_label (StButton *button) { g_return_val_if_fail (ST_IS_BUTTON (button), NULL); return button->priv->text; } /** * st_button_set_label: * @button: a #Stbutton * @text: text to set the label to * * Sets the text displayed on the button */ void st_button_set_label (StButton *button, const gchar *text) { StButtonPrivate *priv; ClutterActor *label; g_return_if_fail (ST_IS_BUTTON (button)); priv = button->priv; g_free (priv->text); if (text) priv->text = g_strdup (text); else priv->text = g_strdup (""); label = st_bin_get_child (ST_BIN (button)); if (label && CLUTTER_IS_TEXT (label)) { clutter_text_set_text (CLUTTER_TEXT (label), priv->text); } else { label = g_object_new (CLUTTER_TYPE_TEXT, "text", priv->text, "line-alignment", PANGO_ALIGN_CENTER, "ellipsize", PANGO_ELLIPSIZE_END, "use-markup", TRUE, NULL); st_bin_set_child (ST_BIN (button), label); } /* Fake a style change so that we reset the style properties on the label */ st_widget_style_changed (ST_WIDGET (button)); g_object_notify (G_OBJECT (button), "label"); } /** * st_button_get_button_mask: * @button: a #StButton * * Gets the mask of mouse buttons that @button emits the * #StButton::clicked signal for. * * Returns: the mask of mouse buttons that @button emits the * #StButton::clicked signal for. */ StButtonMask st_button_get_button_mask (StButton *button) { g_return_val_if_fail (ST_IS_BUTTON (button), 0); return button->priv->button_mask; } /** * st_button_set_button_mask: * @button: a #Stbutton * @mask: the mask of mouse buttons that @button responds to * * Sets which mouse buttons @button emits #StButton::clicked for. */ void st_button_set_button_mask (StButton *button, StButtonMask mask) { g_return_if_fail (ST_IS_BUTTON (button)); button->priv->button_mask = mask; g_object_notify (G_OBJECT (button), "button-mask"); } /** * st_button_get_toggle_mode: * @button: a #StButton * * Get the toggle mode status of the button. * * Returns: %TRUE if toggle mode is set, otherwise %FALSE */ gboolean st_button_get_toggle_mode (StButton *button) { g_return_val_if_fail (ST_IS_BUTTON (button), FALSE); return button->priv->is_toggle; } /** * st_button_set_toggle_mode: * @button: a #Stbutton * @toggle: %TRUE or %FALSE * * Enables or disables toggle mode for the button. In toggle mode, the active * state will be "toggled" when the user clicks the button. */ void st_button_set_toggle_mode (StButton *button, gboolean toggle) { g_return_if_fail (ST_IS_BUTTON (button)); button->priv->is_toggle = toggle; g_object_notify (G_OBJECT (button), "toggle-mode"); } /** * st_button_get_checked: * @button: a #StButton * * Get the state of the button that is in toggle mode. * * Returns: %TRUE if the button is checked, or %FALSE if not */ gboolean st_button_get_checked (StButton *button) { g_return_val_if_fail (ST_IS_BUTTON (button), FALSE); return button->priv->is_checked; } /** * st_button_set_checked: * @button: a #Stbutton * @checked: %TRUE or %FALSE * * Sets the pressed state of the button. This is only really useful if the * button has #toggle-mode mode set to %TRUE. */ void st_button_set_checked (StButton *button, gboolean checked) { g_return_if_fail (ST_IS_BUTTON (button)); if (button->priv->is_checked != checked) { button->priv->is_checked = checked; st_widget_change_style_pseudo_class (ST_WIDGET (button), "checked", checked); } g_object_notify (G_OBJECT (button), "checked"); } /** * st_button_fake_release: * @button: an #StButton * * If this widget is holding a pointer grab, this function will * will ungrab it, and reset the pressed state. The effect is * similar to if the user had released the mouse button, but without * emitting the clicked signal. * * This function is useful if for example you want to do something * after the user is holding the mouse button for a given period of * time, breaking the grab. */ void st_button_fake_release (StButton *button) { if (button->priv->pressed) st_button_release (button, button->priv->pressed, 0); if (button->priv->grabbed) { button->priv->grabbed = 0; clutter_ungrab_pointer (); } } /******************************************************************************/ /*************************** ACCESSIBILITY SUPPORT ****************************/ /******************************************************************************/ #define ST_TYPE_BUTTON_ACCESSIBLE st_button_accessible_get_type () #define ST_BUTTON_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessible)) #define ST_IS_BUTTON_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_BUTTON_ACCESSIBLE)) #define ST_BUTTON_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass)) #define ST_IS_BUTTON_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_BUTTON_ACCESSIBLE)) #define ST_BUTTON_ACCESSIBLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass)) typedef struct _StButtonAccessible StButtonAccessible; typedef struct _StButtonAccessibleClass StButtonAccessibleClass; struct _StButtonAccessible { StWidgetAccessible parent; }; struct _StButtonAccessibleClass { StWidgetAccessibleClass parent_class; }; static void st_button_accessible_class_init (StButtonAccessibleClass *klass); static void st_button_accessible_init (StButtonAccessible *button); /* AtkObject */ static void st_button_accessible_initialize (AtkObject *obj, gpointer data); G_DEFINE_TYPE (StButtonAccessible, st_button_accessible, ST_TYPE_WIDGET_ACCESSIBLE) static const gchar * st_button_accessible_get_name (AtkObject *obj) { StButton *button = NULL; const gchar *name = NULL; button = ST_BUTTON (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (button == NULL) return NULL; name = ATK_OBJECT_CLASS (st_button_accessible_parent_class)->get_name (obj); if (name != NULL) return name; return button->priv->text; } static void st_button_accessible_class_init (StButtonAccessibleClass *klass) { AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = st_button_accessible_initialize; atk_class->get_name = st_button_accessible_get_name; } static void st_button_accessible_init (StButtonAccessible *self) { /* initialization done on AtkObject->initialize */ } static void st_button_accessible_notify_label_cb (StButton *button, GParamSpec *psec, AtkObject *accessible) { g_object_notify (G_OBJECT (accessible), "accessible-name"); } static void st_button_accessible_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (st_button_accessible_parent_class)->initialize (obj, data); obj->role = ATK_ROLE_PUSH_BUTTON; g_signal_connect (data, "notify::label", G_CALLBACK (st_button_accessible_notify_label_cb), obj); } Cinnamon-2.8.6/src/st/st-focus-manager.c0000664000175000017500000001361412625666430016770 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-focus-manager.c: Keyboard focus manager * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-focus-manager * @short_description: Keyboard focus management * * #StFocusManager handles keyboard focus for all actors on the stage. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-focus-manager.h" #define ST_FOCUS_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManagerPrivate)) struct _StFocusManagerPrivate { GHashTable *groups; }; G_DEFINE_TYPE (StFocusManager, st_focus_manager, G_TYPE_OBJECT) static void st_focus_manager_dispose (GObject *object) { StFocusManager *manager = ST_FOCUS_MANAGER (object); if (manager->priv->groups) { g_hash_table_destroy (manager->priv->groups); manager->priv->groups = NULL; } G_OBJECT_CLASS (st_focus_manager_parent_class)->dispose (object); } static void st_focus_manager_class_init (StFocusManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (StFocusManagerPrivate)); object_class->dispose = st_focus_manager_dispose; } static void st_focus_manager_init (StFocusManager *manager) { manager->priv = ST_FOCUS_MANAGER_GET_PRIVATE (manager); manager->priv->groups = g_hash_table_new (NULL, NULL); } static gboolean st_focus_manager_stage_event (ClutterActor *stage, ClutterEvent *event, gpointer user_data) { StFocusManager *manager = user_data; GtkDirectionType direction; gboolean wrap_around = FALSE; ClutterActor *focused, *group; if (event->type != CLUTTER_KEY_PRESS) return FALSE; switch (event->key.keyval) { case CLUTTER_KEY_Up: direction = GTK_DIR_UP; break; case CLUTTER_KEY_Down: direction = GTK_DIR_DOWN; break; case CLUTTER_KEY_Left: direction = GTK_DIR_LEFT; break; case CLUTTER_KEY_Right: direction = GTK_DIR_RIGHT; break; case CLUTTER_KEY_Tab: if (event->key.modifier_state & CLUTTER_SHIFT_MASK) direction = GTK_DIR_TAB_BACKWARD; else direction = GTK_DIR_TAB_FORWARD; wrap_around = TRUE; break; case CLUTTER_KEY_ISO_Left_Tab: direction = GTK_DIR_TAB_BACKWARD; wrap_around = TRUE; break; default: return FALSE; } focused = clutter_stage_get_key_focus (CLUTTER_STAGE (stage)); if (!focused) return FALSE; for (group = focused; group != stage; group = clutter_actor_get_parent (group)) { if (g_hash_table_lookup (manager->priv->groups, group)) { return st_widget_navigate_focus (ST_WIDGET (group), focused, direction, wrap_around); } } return FALSE; } /** * st_focus_manager_get_for_stage: * @stage: a #ClutterStage * * Gets the #StFocusManager for @stage, creating it if necessary. * * Return value: (transfer none): the focus manager for @stage */ StFocusManager * st_focus_manager_get_for_stage (ClutterStage *stage) { StFocusManager *manager; manager = g_object_get_data (G_OBJECT (stage), "st-focus-manager"); if (!manager) { manager = g_object_new (ST_TYPE_FOCUS_MANAGER, NULL); g_object_set_data_full (G_OBJECT (stage), "st-focus-manager", manager, g_object_unref); g_signal_connect (stage, "event", G_CALLBACK (st_focus_manager_stage_event), manager); } return manager; } static void remove_destroyed_group (ClutterActor *actor, gpointer user_data) { StFocusManager *manager = user_data; st_focus_manager_remove_group (manager, ST_WIDGET (actor)); } /** * st_focus_manager_add_group: * @manager: the #StFocusManager * @root: the root container of the group * * Adds a new focus group to @manager. When the focus is in an actor * that is a descendant of @root, @manager will handle moving focus * from one actor to another within @root based on keyboard events. */ void st_focus_manager_add_group (StFocusManager *manager, StWidget *root) { g_signal_connect (root, "destroy", G_CALLBACK (remove_destroyed_group), manager); g_hash_table_insert (manager->priv->groups, root, GINT_TO_POINTER (1)); } /** * st_focus_manager_remove_group: * @manager: the #StFocusManager * @root: the root container of the group * * Removes the group rooted at @root from @manager */ void st_focus_manager_remove_group (StFocusManager *manager, StWidget *root) { g_hash_table_remove (manager->priv->groups, root); } /** * st_focus_manager_get_group: * @manager: the #StFocusManager * @widget: an #StWidget * * Checks if @widget is inside a focus group, and if so, returns * the root of that group. * * Return value: (transfer none): the focus group root, or %NULL if * @widget is not in a focus group */ StWidget * st_focus_manager_get_group (StFocusManager *manager, StWidget *widget) { ClutterActor *actor = CLUTTER_ACTOR (widget); while (actor && !g_hash_table_lookup (manager->priv->groups, actor)) actor = clutter_actor_get_parent (actor); return ST_WIDGET (actor); } Cinnamon-2.8.6/src/st/st-cogl-wrapper.c0000664000175000017500000001146212625666430016642 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-cogl-wrapper.c: compatibility wrappers for various cogl api calls * */ #include "st-cogl-wrapper.h" static CoglContext *cogl_context = NULL; static gboolean supports_npot = FALSE; inline static gboolean hardware_supports_npot_sizes (void) { if (cogl_context != NULL) return supports_npot; ClutterBackend *backend = clutter_get_default_backend (); cogl_context = clutter_backend_get_cogl_context (backend); supports_npot = cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT); g_message ("cogl npot texture sizes %s", supports_npot ? "SUPPORTED" : "NOT supported"); return supports_npot; } /** * st_cogl_texture_new_from_data_wrapper: (skip) * * Decides whether to use the newer (apparently safer) * cogl_texture_2d_new_from_data or the older cogl_texture_new_from_data * depending on if the GPU supports it. */ CoglTexture * st_cogl_texture_new_from_data_wrapper (int width, int height, CoglTextureFlags flags, CoglPixelFormat format, CoglPixelFormat internal_format, int rowstride, const uint8_t *data) { CoglTexture *texture = NULL; if (hardware_supports_npot_sizes ()) { texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (cogl_context, width, height, format, #if COGL_VERSION < COGL_VERSION_ENCODE (1, 18, 0) COGL_PIXEL_FORMAT_ANY, #endif rowstride, data, NULL)); } else { texture = cogl_texture_new_from_data (width, height, flags, format, internal_format, rowstride, data); } return texture; } /** * st_cogl_texture_new_from_file_wrapper: (skip) * * Decides whether to use the newer (apparently safer) * cogl_texture_2d_new_from_file or the older cogl_texture_new_from_file * depending on if the GPU supports it. */ CoglTexture * st_cogl_texture_new_from_file_wrapper (const char *filename, CoglTextureFlags flags, CoglPixelFormat internal_format) { CoglTexture *texture = NULL; if (hardware_supports_npot_sizes ()) { texture = COGL_TEXTURE (cogl_texture_2d_new_from_file (cogl_context, filename, #if COGL_VERSION < COGL_VERSION_ENCODE (1, 18, 0) COGL_PIXEL_FORMAT_ANY, #endif NULL)); } else { texture = cogl_texture_new_from_file (filename, flags, internal_format, NULL); } return texture; } /** * st_cogl_texture_new_with_size_wrapper: (skip) * * Decides whether to use the newer (apparently safer) * cogl_texture_2d_new_with_size or the older cogl_texture_new_with_size * depending on if the GPU supports it. */ CoglTexture * st_cogl_texture_new_with_size_wrapper (int width, int height, CoglTextureFlags flags, CoglPixelFormat internal_format) { CoglTexture *texture = NULL; if (hardware_supports_npot_sizes ()) { texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context, width, height #if COGL_VERSION < COGL_VERSION_ENCODE (1, 18, 0) ,CLUTTER_CAIRO_FORMAT_ARGB32 #endif )); } else { texture = cogl_texture_new_with_size (width, height, flags, internal_format); } return texture; } Cinnamon-2.8.6/src/st/st-focus-manager.h0000664000175000017500000000555212625666430016777 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-focus-manager.h: Keyboard focus manager * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_FOCUS_MANAGER_H__ #define __ST_FOCUS_MANAGER_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_FOCUS_MANAGER (st_focus_manager_get_type ()) #define ST_FOCUS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManager)) #define ST_IS_FOCUS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_FOCUS_MANAGER)) #define ST_FOCUS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_FOCUS_MANAGER, StFocusManagerClass)) #define ST_IS_FOCUS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_FOCUS_MANAGER)) #define ST_FOCUS_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManagerClass)) typedef struct _StFocusManager StFocusManager; typedef struct _StFocusManagerPrivate StFocusManagerPrivate; typedef struct _StFocusManagerClass StFocusManagerClass; /** * StFocusManager: * * The #StFocusManager struct contains only private data */ struct _StFocusManager { /*< private >*/ GObject parent_instance; StFocusManagerPrivate *priv; }; /** * StFocusManagerClass: * * The #StFocusManagerClass struct contains only private data */ struct _StFocusManagerClass { /*< private >*/ GObjectClass parent_class; }; GType st_focus_manager_get_type (void) G_GNUC_CONST; StFocusManager *st_focus_manager_get_for_stage (ClutterStage *stage); void st_focus_manager_add_group (StFocusManager *manager, StWidget *root); void st_focus_manager_remove_group (StFocusManager *manager, StWidget *root); StWidget *st_focus_manager_get_group (StFocusManager *manager, StWidget *widget); G_END_DECLS #endif /* __ST_FOCUS_MANAGER_H__ */ Cinnamon-2.8.6/src/st/st-container.h0000664000175000017500000000530212625666430016223 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-container.h: Base class for St container actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_CONTAINER_H__ #define __ST_CONTAINER_H__ #include G_BEGIN_DECLS #define ST_TYPE_CONTAINER (st_container_get_type ()) #define ST_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_CONTAINER, StContainer)) #define ST_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_CONTAINER)) #define ST_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_CONTAINER, StContainerClass)) #define ST_IS_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_CONTAINER)) #define ST_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_CONTAINER, StContainerClass)) typedef struct _StContainer StContainer; typedef struct _StContainerClass StContainerClass; typedef struct _StContainerPrivate StContainerPrivate; struct _StContainer { StWidget parent; StContainerPrivate *priv; }; struct _StContainerClass { StWidgetClass parent_class; GList * (*get_focus_chain) (StContainer *container); }; GType st_container_get_type (void) G_GNUC_CONST; void st_container_destroy_children (StContainer *container); GList * st_container_get_focus_chain (StContainer *container); /* Only to be used by subclasses of StContainer */ void st_container_move_child (StContainer *container, ClutterActor *actor, int pos); void st_container_move_before (StContainer *container, ClutterActor *actor, ClutterActor *sibling); GList * st_container_get_children_list (StContainer *container); G_END_DECLS #endif /* __ST_CONTAINER_H__ */ Cinnamon-2.8.6/src/st/st-table.c0000664000175000017500000010317512625666430015332 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-table.c: Table layout widget * * Copyright 2008, 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Abderrahim Kitouni * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-table * @short_description: A multi-child layout container based on rows * and columns * * #StTable is a mult-child layout container based on a table arrangement * with rows and columns. #StTable adds several child properties to it's * children that control their position and size in the table. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "st-table.h" #include #include #include #include #include "st-enum-types.h" #include "st-marshal.h" #include "st-private.h" #include "st-table-child.h" #include "st-table-private.h" enum { PROP_0, PROP_HOMOGENEOUS, PROP_ROW_COUNT, PROP_COL_COUNT, }; #define ST_TABLE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TABLE, StTablePrivate)) struct _StTablePrivate { gint col_spacing; gint row_spacing; gint n_rows; gint n_cols; gint active_row; gint active_col; GArray *min_widths; GArray *pref_widths; GArray *min_heights; GArray *pref_heights; GArray *is_expand_col; GArray *is_expand_row; GArray *col_widths; GArray *row_heights; guint homogeneous : 1; }; static void st_table_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (StTable, st_table, ST_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, st_table_container_iface_init)); /* * ClutterContainer Implementation */ static void st_table_actor_removed (ClutterContainer *container, ClutterActor *actor) { StTablePrivate *priv = ST_TABLE (container)->priv; GList *list, *children; gint n_rows = 0; gint n_cols = 0; /* Calculate and update the number of rows / columns */ children = st_container_get_children_list (ST_CONTAINER (container)); for (list = children; list; list = list->next) { ClutterActor *child = CLUTTER_ACTOR (list->data); StTableChild *meta; if (child == actor) continue; meta = (StTableChild *) clutter_container_get_child_meta (container, child); n_rows = MAX (n_rows, meta->row + 1); n_cols = MAX (n_cols, meta->col + 1); } g_object_freeze_notify (G_OBJECT (container)); if (priv->n_rows != n_rows) { priv->n_rows = n_rows; g_object_notify (G_OBJECT (container), "row-count"); } if (priv->n_cols != n_cols) { priv->n_cols = n_cols; g_object_notify (G_OBJECT (container), "column-count"); } g_object_thaw_notify (G_OBJECT (container)); } static void st_table_container_iface_init (ClutterContainerIface *iface) { iface->actor_removed = st_table_actor_removed; iface->child_meta_type = ST_TYPE_TABLE_CHILD; } /* StTable Class Implementation */ static void st_table_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StTable *table = ST_TABLE (gobject); switch (prop_id) { case PROP_HOMOGENEOUS: if (table->priv->homogeneous != g_value_get_boolean (value)) { table->priv->homogeneous = g_value_get_boolean (value); clutter_actor_queue_relayout ((ClutterActor *) gobject); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_table_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StTablePrivate *priv = ST_TABLE (gobject)->priv; switch (prop_id) { case PROP_HOMOGENEOUS: g_value_set_boolean (value, priv->homogeneous); break; case PROP_COL_COUNT: g_value_set_int (value, priv->n_cols); break; case PROP_ROW_COUNT: g_value_set_int (value, priv->n_rows); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_table_finalize (GObject *gobject) { StTablePrivate *priv = ST_TABLE (gobject)->priv; g_array_free (priv->min_widths, TRUE); g_array_free (priv->pref_widths, TRUE); g_array_free (priv->min_heights, TRUE); g_array_free (priv->pref_heights, TRUE); g_array_free (priv->is_expand_col, TRUE); g_array_free (priv->is_expand_row, TRUE); g_array_free (priv->col_widths, TRUE); g_array_free (priv->row_heights, TRUE); G_OBJECT_CLASS (st_table_parent_class)->finalize (gobject); } static void st_table_homogeneous_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { GList *list, *children; gfloat col_width, row_height; gint row_spacing, col_spacing; StTablePrivate *priv = ST_TABLE (self)->priv; gboolean ltr = st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR; col_spacing = priv->col_spacing; row_spacing = priv->row_spacing; col_width = (int) ((content_box->x2 - content_box->x1 - (col_spacing * (priv->n_cols - 1))) / priv->n_cols + 0.5); row_height = (int) ((content_box->y2 - content_box->y1 - (row_spacing * (priv->n_rows - 1))) / priv->n_rows + 0.5); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint row, col, row_span, col_span; StTableChild *meta; ClutterActor *child; ClutterActorBox childbox; StAlign x_align, y_align; gboolean x_fill, y_fill; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; x_align = meta->x_align; y_align = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; if (ltr) { childbox.x1 = content_box->x1 + (col_width + col_spacing) * col; childbox.x2 = childbox.x1 + (col_width * col_span) + (col_spacing * (col_span - 1)); } else { childbox.x2 = content_box->x2 - (col_width + col_spacing) * col; childbox.x1 = childbox.x2 - (col_width * col_span) - (col_spacing * (col_span - 1)); } childbox.y1 = content_box->y1 + (row_height + row_spacing) * row; childbox.y2 = childbox.y1 + (row_height * row_span) + (row_spacing * (row_span - 1)); _st_allocate_fill (ST_WIDGET (self), child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } } static gint * st_table_calculate_col_widths (StTable *table, gint for_width) { gint total_min_width, i; StTablePrivate *priv = table->priv; gboolean *is_expand_col; gint extra_col_width, n_expanded_cols = 0, expanded_cols = 0; gint *pref_widths, *min_widths; GList *list, *children; g_array_set_size (priv->is_expand_col, 0); g_array_set_size (priv->is_expand_col, priv->n_cols); is_expand_col = (gboolean *) priv->is_expand_col->data; g_array_set_size (priv->pref_widths, 0); g_array_set_size (priv->pref_widths, priv->n_cols); pref_widths = (gint *) priv->pref_widths->data; g_array_set_size (priv->min_widths, 0); g_array_set_size (priv->min_widths, priv->n_cols); min_widths = (gint *) priv->min_widths->data; children = st_container_get_children_list (ST_CONTAINER (table)); for (list = children; list; list = list->next) { gint col; gfloat w_min, w_pref; gboolean x_expand; StTableChild *meta; ClutterActor *child; gint col_span; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; x_expand = meta->x_expand; col_span = meta->col_span; if (x_expand) is_expand_col[col] = TRUE; _st_actor_get_preferred_width (child, -1, meta->y_fill, &w_min, &w_pref); if (col_span == 1 && w_pref > pref_widths[col]) { pref_widths[col] = w_pref; } if (col_span == 1 && w_min > min_widths[col]) { min_widths[col] = w_min; } } total_min_width = priv->col_spacing * (priv->n_cols - 1); for (i = 0; i < priv->n_cols; i++) total_min_width += pref_widths[i]; /* calculate the remaining space and distribute it evenly onto all rows/cols * with the x/y expand property set. */ for (i = 0; i < priv->n_cols; i++) if (is_expand_col[i]) { expanded_cols += pref_widths[i]; n_expanded_cols++; } /* for_width - total_min_width */ extra_col_width = for_width - total_min_width; if (extra_col_width) for (i = 0; i < priv->n_cols; i++) if (is_expand_col[i]) { if (extra_col_width < 0) { pref_widths[i] = MAX (min_widths[i], pref_widths[i] + (extra_col_width * (pref_widths[i] / (float) expanded_cols))); /* if we reached the minimum width for this column, we need to * stop counting it as expanded */ if (pref_widths[i] == min_widths[i]) { /* restart calculations :-( */ expanded_cols -= pref_widths[i]; is_expand_col[i] = 0; n_expanded_cols--; i = -1; } } else pref_widths[i] += extra_col_width / n_expanded_cols; } return pref_widths; } static gint * st_table_calculate_row_heights (StTable *table, gint for_height, gint * col_widths) { StTablePrivate *priv = ST_TABLE (table)->priv; GList *list, *children; gint *is_expand_row, *min_heights, *pref_heights, *row_heights, extra_row_height; gint i, total_min_height; gint expanded_rows = 0; gint n_expanded_rows = 0; g_array_set_size (priv->row_heights, 0); g_array_set_size (priv->row_heights, priv->n_rows); row_heights = (gboolean *) priv->row_heights->data; g_array_set_size (priv->is_expand_row, 0); g_array_set_size (priv->is_expand_row, priv->n_rows); is_expand_row = (gboolean *) priv->is_expand_row->data; g_array_set_size (priv->min_heights, 0); g_array_set_size (priv->min_heights, priv->n_rows); min_heights = (gboolean *) priv->min_heights->data; g_array_set_size (priv->pref_heights, 0); g_array_set_size (priv->pref_heights, priv->n_rows); pref_heights = (gboolean *) priv->pref_heights->data; children = st_container_get_children_list (ST_CONTAINER (table)); for (list = children; list; list = list->next) { gint row, col, cell_width; gfloat h_min, h_pref; gboolean y_expand; StTableChild *meta; ClutterActor *child; gint col_span, row_span; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; y_expand = meta->y_expand; col_span = meta->col_span; row_span = meta->row_span; if (y_expand) is_expand_row[row] = TRUE; /* calculate the cell width by including any spanned columns */ cell_width = 0; for (i = 0; i < col_span && col + i < priv->n_cols; i++) cell_width += (float)(col_widths[col + i]); if (!meta->x_fill) { gfloat width; _st_actor_get_preferred_width (child, -1, meta->y_fill, NULL, &width); cell_width = MIN (cell_width, width); } _st_actor_get_preferred_height (child, cell_width, meta->x_fill, &h_min, &h_pref); if (row_span == 1 && h_pref > pref_heights[row]) { pref_heights[row] = (int)(h_pref); } if (row_span == 1 && h_min > min_heights[row]) { min_heights[row] = (int)(h_min); } } total_min_height = 0; // priv->row_spacing * (priv->n_rows - 1); for (i = 0; i < priv->n_rows; i++) total_min_height += pref_heights[i]; /* calculate the remaining space and distribute it evenly onto all rows/cols * with the x/y expand property set. */ for (i = 0; i < priv->n_rows; i++) if (is_expand_row[i]) { expanded_rows += pref_heights[i]; n_expanded_rows++; } /* extra row height = for height - row spacings - total_min_height */ for_height -= (priv->row_spacing * (priv->n_rows - 1)); extra_row_height = for_height - total_min_height; if (extra_row_height < 0) { gint *skip = g_slice_alloc0 (sizeof (gint) * priv->n_rows); gint total_shrink_height; /* If we need to shrink rows, we need to do multiple passes. * * We start by assuming all rows can shrink. All rows are sized * proportional to their height in the total table size. If a row would be * sized smaller than its minimum size, we mark it as non-shrinkable, and * reduce extra_row_height by the amount it has been shrunk. The amount * it has been shrunk by is the difference between the preferred and * minimum height, since all rows start at their preferred height. We * also then reduce the total table size (stored in total_shrink_height) by the height * of the row we are going to be skipping. * */ /* We start by assuming all rows can shrink */ total_shrink_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { if (!skip[i]) { gint tmp; /* Calculate the height of the row by starting with the preferred * height and taking away the extra row height proportional to * the preferred row height over the rows that are being shrunk */ tmp = pref_heights[i] + (extra_row_height * (pref_heights[i] / (float) total_shrink_height)); if (tmp < min_heights[i]) { /* This was a row we *were* set to shrink, but we now find it would have * been shrunk too much. We remove it from the list of rows to shrink and * adjust extra_row_height and total_shrink_height appropriately */ skip[i] = TRUE; row_heights[i] = min_heights[i]; /* Reduce extra_row_height by the amount we have reduced this * actor by */ extra_row_height += (pref_heights[i] - min_heights[i]); /* now take off the row from the total shrink height */ total_shrink_height -= pref_heights[i]; /* restart the loop */ i = -1; } else { skip[i] = FALSE; row_heights[i] = tmp; } } } g_slice_free1 (sizeof (gint) * priv->n_rows, skip); } else { for (i = 0; i < priv->n_rows; i++) { if (is_expand_row[i]) row_heights[i] = pref_heights[i] + (extra_row_height / n_expanded_rows); else row_heights[i] = pref_heights[i]; } } return row_heights; } static void st_table_preferred_allocate (ClutterActor *self, const ClutterActorBox *content_box, gboolean flags) { GList *list, *children; gint row_spacing, col_spacing; gint i; gint *col_widths, *row_heights; StTable *table; StTablePrivate *priv; gboolean ltr; table = ST_TABLE (self); priv = ST_TABLE (self)->priv; col_spacing = (priv->col_spacing); row_spacing = (priv->row_spacing); col_widths = st_table_calculate_col_widths (table, (int) (content_box->x2 - content_box->x1)); row_heights = st_table_calculate_row_heights (table, (int) (content_box->y2 - content_box->y1), col_widths); ltr = (st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint row, col, row_span, col_span; gint col_width, row_height; StTableChild *meta; ClutterActor *child; ClutterActorBox childbox; gint child_x, child_y; StAlign x_align, y_align; gboolean x_fill, y_fill; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; row = meta->row; row_span = meta->row_span; col_span = meta->col_span; x_align = meta->x_align; y_align = meta->y_align; x_fill = meta->x_fill; y_fill = meta->y_fill; /* initialise the width and height */ col_width = col_widths[col]; row_height = row_heights[row]; /* Add the widths of the spanned columns: * * First check that we have a non-zero span. Then we loop over each of * the columns that we're spanning but we stop short if we go past the * number of columns in the table. This is necessary to avoid accessing * uninitialised memory. We add the spacing in here too since we only * want to add as much spacing as times we successfully span. */ if (col + col_span > priv->n_cols) g_warning ("StTable: col-span exceeds number of columns"); #if 0 if (row + row_span > priv->n_rows) g_warning ("StTable: row-span exceeds number of rows"); #endif if (col_span > 1) { for (i = col + 1; i < col + col_span && i < priv->n_cols; i++) { col_width += col_widths[i]; col_width += col_spacing; } } /* add the height of the spanned rows */ if (row_span > 1) { for (i = row + 1; i < row + row_span && i < priv->n_rows; i++) { row_height += row_heights[i]; row_height += row_spacing; } } /* calculate child x */ if (ltr) { child_x = (int) content_box->x1 + col_spacing * col; for (i = 0; i < col; i++) child_x += col_widths[i]; } else { child_x = (int) content_box->x2 - col_spacing * col; for (i = 0; i < col; i++) child_x -= col_widths[i]; } /* calculate child y */ child_y = (int) content_box->y1 + row_spacing * row; for (i = 0; i < row; i++) child_y += row_heights[i]; /* set up childbox */ if (ltr) { childbox.x1 = (float) child_x; childbox.x2 = (float) MAX (0, child_x + col_width); } else { childbox.x2 = (float) child_x; childbox.x1 = (float) MAX (0, child_x - col_width); } childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); _st_allocate_fill (ST_WIDGET (self), child, &childbox, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &childbox, flags); } } static void st_table_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox content_box; CLUTTER_ACTOR_CLASS (st_table_parent_class)->allocate (self, box, flags); if (priv->n_cols < 1 || priv->n_rows < 1) { return; }; st_theme_node_get_content_box (theme_node, box, &content_box); if (priv->homogeneous) st_table_homogeneous_allocate (self, &content_box, flags); else st_table_preferred_allocate (self, &content_box, flags); } static void st_table_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gint *min_widths, *pref_widths; gfloat total_min_width, total_pref_width; StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); GList *list, *children; gint i; if (priv->n_cols < 1) { *min_width_p = 0; *natural_width_p = 0; return; } /* Setting size to zero and then what we want it to be causes a clear if * clear flag is set (which it should be.) */ g_array_set_size (priv->min_widths, 0); g_array_set_size (priv->pref_widths, 0); g_array_set_size (priv->min_widths, priv->n_cols); g_array_set_size (priv->pref_widths, priv->n_cols); min_widths = (gint *) priv->min_widths->data; pref_widths = (gint *) priv->pref_widths->data; /* calculate minimum row widths */ children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint col, col_span; gfloat w_min, w_pref; StTableChild *meta; ClutterActor *child; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ col = meta->col; col_span = meta->col_span; _st_actor_get_preferred_width (child, -1, meta->y_fill, &w_min, &w_pref); if (col_span == 1 && w_min > min_widths[col]) min_widths[col] = w_min; if (col_span == 1 && w_pref > pref_widths[col]) pref_widths[col] = w_pref; } total_min_width = (priv->n_cols - 1) * (float) priv->col_spacing; total_pref_width = total_min_width; for (i = 0; i < priv->n_cols; i++) { total_min_width += min_widths[i]; total_pref_width += pref_widths[i]; } /* If we were requested width-for-height, then we reported minimum/natural * heights based on our natural width. If we were allocated less than our * natural width, then we need more height. So in the width-for-height * case we need to disable shrinking. */ if (for_height >= 0) total_min_width = total_pref_width; if (min_width_p) *min_width_p = total_min_width; if (natural_width_p) *natural_width_p = total_pref_width; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_table_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { gint *min_heights, *pref_heights; gfloat total_min_height, total_pref_height; StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); GList *list, *children; gint i; gint *min_widths; /* We only support height-for-width allocation. So if we are called * width-for-height, calculate heights based on our natural width */ if (for_width < 0) { float natural_width; clutter_actor_get_preferred_width (self, -1, NULL, &natural_width); for_width = natural_width; } if (priv->n_rows < 1) { *min_height_p = 0; *natural_height_p = 0; return; } st_theme_node_adjust_for_width (theme_node, &for_width); /* Setting size to zero and then what we want it to be causes a clear if * clear flag is set (which it should be.) */ g_array_set_size (priv->min_heights, 0); g_array_set_size (priv->pref_heights, 0); g_array_set_size (priv->min_heights, priv->n_rows); g_array_set_size (priv->pref_heights, priv->n_rows); /* use min_widths to help allocation of height-for-width widgets */ min_widths = st_table_calculate_col_widths (ST_TABLE (self), for_width); min_heights = (gint *) priv->min_heights->data; pref_heights = (gint *) priv->pref_heights->data; /* calculate minimum row heights */ children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { gint row, col, col_span, cell_width, row_span; gfloat min, pref; StTableChild *meta; ClutterActor *child; child = CLUTTER_ACTOR (list->data); meta = (StTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child); if (!meta->allocate_hidden && !CLUTTER_ACTOR_IS_VISIBLE (child)) continue; /* get child properties */ row = meta->row; col = meta->col; col_span = meta->col_span; row_span = meta->row_span; cell_width = 0; for (i = 0; i < col_span && col + i < priv->n_cols; i++) cell_width += min_widths[col + i]; _st_actor_get_preferred_height (child, (float) cell_width, meta->x_fill, &min, &pref); if (row_span == 1 && min > min_heights[row]) min_heights[row] = min; if (row_span == 1 && pref > pref_heights[row]) pref_heights[row] = pref; } /* start off with row spacing */ total_min_height = (priv->n_rows - 1) * (float) (priv->row_spacing); total_pref_height = total_min_height; for (i = 0; i < priv->n_rows; i++) { total_min_height += min_heights[i]; total_pref_height += pref_heights[i]; } if (min_height_p) *min_height_p = total_min_height; if (natural_height_p) *natural_height_p = total_pref_height; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_table_paint (ClutterActor *self) { GList *list, *children; /* make sure the background gets painted first */ CLUTTER_ACTOR_CLASS (st_table_parent_class)->paint (self); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { ClutterActor *child = CLUTTER_ACTOR (list->data); if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } } static void st_table_pick (ClutterActor *self, const ClutterColor *color) { GList *list, *children; /* Chain up so we get a bounding box painted (if we are reactive) */ CLUTTER_ACTOR_CLASS (st_table_parent_class)->pick (self, color); children = st_container_get_children_list (ST_CONTAINER (self)); for (list = children; list; list = list->next) { if (CLUTTER_ACTOR_IS_VISIBLE (list->data)) clutter_actor_paint (CLUTTER_ACTOR (list->data)); } } static void st_table_show_all (ClutterActor *table) { GList *l, *children; children = st_container_get_children_list (ST_CONTAINER (table)); for (l = children; l; l = l->next) clutter_actor_show_all (CLUTTER_ACTOR (l->data)); clutter_actor_show (table); } static void st_table_hide_all (ClutterActor *table) { GList *l, *children; clutter_actor_hide (table); children = st_container_get_children_list (ST_CONTAINER (table)); for (l = children; l; l = l->next) clutter_actor_hide_all (CLUTTER_ACTOR (l->data)); } static void st_table_style_changed (StWidget *self) { StTablePrivate *priv = ST_TABLE (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (self); int old_row_spacing = priv->row_spacing; int old_col_spacing = priv->col_spacing; double row_spacing, col_spacing; row_spacing = st_theme_node_get_length (theme_node, "spacing-rows"); priv->row_spacing = (int)(row_spacing + 0.5); col_spacing = st_theme_node_get_length (theme_node, "spacing-columns"); priv->col_spacing = (int)(col_spacing + 0.5); if (priv->row_spacing != old_row_spacing || priv->col_spacing != old_col_spacing) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); ST_WIDGET_CLASS (st_table_parent_class)->style_changed (self); } static void st_table_class_init (StTableClass *klass) { GParamSpec *pspec; GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (StTablePrivate)); gobject_class->set_property = st_table_set_property; gobject_class->get_property = st_table_get_property; gobject_class->finalize = st_table_finalize; actor_class->paint = st_table_paint; actor_class->pick = st_table_pick; actor_class->allocate = st_table_allocate; actor_class->get_preferred_width = st_table_get_preferred_width; actor_class->get_preferred_height = st_table_get_preferred_height; actor_class->show_all = st_table_show_all; actor_class->hide_all = st_table_hide_all; widget_class->style_changed = st_table_style_changed; pspec = g_param_spec_boolean ("homogeneous", "Homogeneous", "Homogeneous rows and columns", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_HOMOGENEOUS, pspec); pspec = g_param_spec_int ("row-count", "Row Count", "The number of rows in the table", 0, G_MAXINT, 0, ST_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_ROW_COUNT, pspec); pspec = g_param_spec_int ("column-count", "Column Count", "The number of columns in the table", 0, G_MAXINT, 0, ST_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_COL_COUNT, pspec); } static void st_table_init (StTable *table) { table->priv = ST_TABLE_GET_PRIVATE (table); table->priv->n_cols = 0; table->priv->n_rows = 0; table->priv->min_widths = g_array_new (FALSE, TRUE, sizeof (gint)); table->priv->pref_widths = g_array_new (FALSE, TRUE, sizeof (gint)); table->priv->min_heights = g_array_new (FALSE, TRUE, sizeof (gint)); table->priv->pref_heights = g_array_new (FALSE, TRUE, sizeof (gint)); table->priv->is_expand_col = g_array_new (FALSE, TRUE, sizeof (gboolean)); table->priv->is_expand_row = g_array_new (FALSE, TRUE, sizeof (gboolean)); table->priv->col_widths = g_array_new (FALSE, TRUE, sizeof (gint)); table->priv->row_heights = g_array_new (FALSE, TRUE, sizeof (gint)); } /* used by StTableChild to update row/column count */ void _st_table_update_row_col (StTable *table, gint row, gint col) { if (col > -1) table->priv->n_cols = MAX (table->priv->n_cols, col + 1); if (row > -1) table->priv->n_rows = MAX (table->priv->n_rows, row + 1); } /*** Public Functions ***/ /** * st_table_new: * * Create a new #StTable * * Returns: a new #StTable */ StWidget* st_table_new (void) { return g_object_new (ST_TYPE_TABLE, NULL); } /** * st_table_get_row_count: * @table: A #StTable * * Retrieve the current number rows in the @table * * Returns: the number of rows */ gint st_table_get_row_count (StTable *table) { g_return_val_if_fail (ST_IS_TABLE (table), -1); return ST_TABLE (table)->priv->n_rows; } /** * st_table_get_column_count: * @table: A #StTable * * Retrieve the current number of columns in @table * * Returns: the number of columns */ gint st_table_get_column_count (StTable *table) { g_return_val_if_fail (ST_IS_TABLE (table), -1); return ST_TABLE (table)->priv->n_cols; } Cinnamon-2.8.6/src/st/st-background-effect.c0000664000175000017500000006035312625666430017614 0ustar fabiofabio#include "st-background-effect.h" #include "st-cogl-wrapper.h" #define ST_BACKGROUND_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BACKGROUND_EFFECT, StBackgroundEffectClass)) #define ST_IS_BACKGROUND_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BACKGROUND_EFFECT)) #define ST_BACKGROUND_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BACKGROUND_EFFECT, StBackgroundEffectClass)) G_DEFINE_TYPE (StBackgroundEffect, st_background_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); #include #include enum { PROP_0, PROP_BUMPMAP, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static const gchar *box_blur_glsl_declarations = "uniform vec3 pixel_step;\n" "uniform vec2 bump_step;\n" "uniform sampler2D BumpTex;\n"; static const gchar *box_blur_glsl_shader = "vec2 vTexCoord = cogl_tex_coord.st;\n" "vec4 displtex = vec4(0.0);\n" "if (pixel_step.z > 1.5) {\n" " vec4 previous = texture2D(cogl_sampler, vec2(vTexCoord.x, vTexCoord.y));\n" " if (previous.w > 0.004) {\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x - pixel_step.x, vTexCoord.y - pixel_step.y));\n" " displtex -= previous;\n" " }\n" " cogl_texel = displtex;\n" "} else if (pixel_step.z > 0.5) {\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x - pixel_step.x, vTexCoord.y + pixel_step.y));\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x, vTexCoord.y + pixel_step.y)) * 2.0;\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x + pixel_step.x, vTexCoord.y + pixel_step.y));\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x - pixel_step.x, vTexCoord.y)) * 2.0;\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x, vTexCoord.y)) * 4.0;\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x + pixel_step.x, vTexCoord.y)) * 2.0;\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x - pixel_step.x, vTexCoord.y - pixel_step.y));\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x, vTexCoord.y - pixel_step.y)) * 2.0;\n" " displtex += texture2D(cogl_sampler, vec2(vTexCoord.x + pixel_step.x, vTexCoord.y - pixel_step.y));\n" " cogl_texel = displtex / 16.0;\n" "} else {\n" " float factx = bump_step.x / pixel_step.x;\n" " float facty = bump_step.y / pixel_step.y;\n" " vec4 bump = texture2D(BumpTex, vec2(vTexCoord.x * factx, vTexCoord.y * facty));\n" " float displx = (bump.r - 0.5) * pixel_step.x * 256.0;\n" " float disply = (bump.g - 0.5) * pixel_step.y * 256.0;\n" " displtex = texture2D(cogl_sampler, vec2(vTexCoord.x + displx, vTexCoord.y + disply)) * bump.b;\n" " cogl_texel = displtex;\n" "}\n"; static gboolean st_background_effect_pre_paint (ClutterEffect *effect) { StBackgroundEffect *self = ST_BACKGROUND_EFFECT (effect); ClutterEffectClass *parent_class; gfloat width; gfloat height; gfloat posx; gfloat posy; guchar *data; guint size; guint rowstride; glong new_time; gdouble time_used; ClutterActor *stage; gfloat stage_width; gfloat stage_height; if (self->bg_bumpmap == NULL) return FALSE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; self->actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); if (self->actor == NULL) return FALSE; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ShaderEffect: the graphics hardware " "or the current GL driver does not implement support " "for the GLSL shading language."); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); return FALSE; } new_time = clock(); time_used = ((double) (new_time - self->old_time)*100) / (double) CLOCKS_PER_SEC; self->old_time = new_time; posx = 0.0f; posy = 0.0f; width = 0.0f; height = 0.0f; stage_width = 0.0f; stage_height = 0.0f; clutter_actor_get_transformed_position (self->actor, &posx, &posy); clutter_actor_get_transformed_size (self->actor, &width, &height); self->opacity = clutter_actor_get_paint_opacity (self->actor); stage = clutter_actor_get_stage (self->actor); clutter_actor_get_size (stage, &stage_width, &stage_height); if ((posx < 0) || (posy < 0) || ((posx + width) > stage_width) || ((posy + height) > stage_height)) return FALSE; if (( posx != self->posx_old) || ( posy != self->posy_old) || ( width != self->width_old) || ( height != self->height_old) || (time_used > 50.0)) { self->posx_old = posx; self->posy_old = posy; self->width_old = width; self->height_old = height; self->bg_posx_i = round(posx)+2; self->bg_posy_i = round(posy)+2; self->bg_width_i = round(width)-4; self->bg_height_i = round(height)-4; size = (self->bg_width_i) * (self->bg_height_i) * 4; if (((self->opacity == 0xff) || (self->bg_texture == NULL)) && (size > 400)) { rowstride = (self->bg_width_i) * 4; data = g_malloc (size); cogl_read_pixels (self->bg_posx_i, self->bg_posy_i, self->bg_width_i, self->bg_height_i, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); if (data != NULL) { if (self->bg_texture != NULL) { cogl_handle_unref (self->bg_texture); self->bg_texture = NULL; } self->bg_texture = st_cogl_texture_new_from_data_wrapper (self->bg_width_i, self->bg_height_i, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_RGBA_8888_PRE, rowstride, data); g_free (data); } } } parent_class = CLUTTER_EFFECT_CLASS (st_background_effect_parent_class); if (parent_class->pre_paint (effect)) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect); CoglHandle fg_texture; fg_texture = clutter_offscreen_effect_get_texture (offscreen_effect); if (fg_texture != COGL_INVALID_HANDLE) { self->fg_width_i = cogl_texture_get_width (fg_texture); self->fg_height_i = cogl_texture_get_height (fg_texture); if ((self->bg_texture != NULL) && (self->opacity == 0xff)) { if (self->pixel_step_uniform0 > -1) { gfloat pixel_step[3]; pixel_step[0] = 1.0f / (self->bg_width_i); pixel_step[1] = 1.0f / (self->bg_height_i); pixel_step[2] = 0.0f; cogl_pipeline_set_uniform_float (self->pipeline0, self->pixel_step_uniform0, 3, 1, pixel_step); } if (self->BumpTex_uniform > -1) { cogl_pipeline_set_uniform_1i (self->pipeline0, self->BumpTex_uniform, 1); } if (self->bump_step_uniform > -1) { gfloat bump_step[2]; bump_step[0] = 1.0f / (self->bumptex_width_i); bump_step[1] = 1.0f / (self->bumptex_height_i); cogl_pipeline_set_uniform_float (self->pipeline0, self->bump_step_uniform, 2, 1, bump_step); } if (self->bg_sub_texture != NULL) { cogl_handle_unref (self->bg_sub_texture); self->bg_sub_texture = NULL; } self->bg_sub_texture = st_cogl_texture_new_with_size_wrapper (self->bg_width_i, self->bg_height_i, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); cogl_pipeline_set_layer_texture (self->pipeline0, 0, self->bg_texture); if (self->pixel_step_uniform1 > -1) { gfloat pixel_step[3]; pixel_step[0] = 1.0f / (self->bg_width_i); pixel_step[1] = 1.0f / (self->bg_height_i); pixel_step[2] = 1.0f; cogl_pipeline_set_uniform_float (self->pipeline1, self->pixel_step_uniform1, 3, 1, pixel_step); } if (self->pixel_step_uniform2 > -1) { gfloat pixel_step[3]; pixel_step[0] = 1.0f / (self->fg_width_i); pixel_step[1] = 1.0f / (self->fg_height_i); pixel_step[2] = 2.0f; cogl_pipeline_set_uniform_float (self->pipeline3, self->pixel_step_uniform2, 3, 1, pixel_step); } } cogl_pipeline_set_layer_texture (self->pipeline2, 0, fg_texture); cogl_pipeline_set_layer_texture (self->pipeline3, 0, fg_texture); cogl_pipeline_set_layer_texture (self->pipeline4, 0, fg_texture); } return TRUE; } else { return FALSE; } } static void st_background_effect_paint_target (ClutterOffscreenEffect *effect) { StBackgroundEffect *self = ST_BACKGROUND_EFFECT (effect); if ((self->bg_texture != NULL) && (self->opacity == 0xff)) { cogl_push_source (self->pipeline2); cogl_rectangle (0.0f, 0.0f, (self->fg_width_i), (self->fg_height_i)); cogl_pop_source (); CoglOffscreen *vertical_FBO; vertical_FBO = cogl_offscreen_new_to_texture (self->bg_sub_texture); cogl_push_framebuffer ((CoglFramebuffer*)vertical_FBO); cogl_push_source (self->pipeline0); cogl_rectangle (-1.0f, 1.0f, 1.0f, -1.0f); cogl_pop_source (); cogl_pop_framebuffer (); cogl_handle_unref (vertical_FBO); cogl_pipeline_set_layer_texture (self->pipeline1, 0, self->bg_sub_texture); cogl_push_source (self->pipeline1); cogl_rectangle (4.0f, 4.0f, (self->bg_width_i) + 4.0f, (self->bg_height_i) + 4.0f); cogl_pop_source (); } cogl_pipeline_set_color4ub (self->pipeline3, 0x00, 0x00, 0x00, 0x80); cogl_push_source (self->pipeline3); cogl_rectangle (0.0f, 0.0f, (self->fg_width_i), (self->fg_height_i)); cogl_pop_source (); clutter_actor_queue_redraw (self->actor); cogl_pipeline_set_color4ub (self->pipeline4, self->opacity, self->opacity, self->opacity, self->opacity); cogl_push_source (self->pipeline4); cogl_rectangle (0.0f, 0.0f, (self->fg_width_i), (self->fg_height_i)); cogl_pop_source (); clutter_actor_queue_redraw (self->actor); } static void st_background_effect_dispose (GObject *gobject) { StBackgroundEffect *self = ST_BACKGROUND_EFFECT (gobject); if (self->pipeline0 != NULL) { cogl_object_unref (self->pipeline0); self->pipeline0 = NULL; } if (self->pipeline1 != NULL) { cogl_object_unref (self->pipeline1); self->pipeline1 = NULL; } if (self->pipeline2 != NULL) { cogl_object_unref (self->pipeline2); self->pipeline2 = NULL; } if (self->pipeline3 != NULL) { cogl_object_unref (self->pipeline3); self->pipeline3 = NULL; } if (self->pipeline4 != NULL) { cogl_object_unref (self->pipeline4); self->pipeline4 = NULL; } if (self->bg_texture != NULL) { cogl_handle_unref (self->bg_texture); self->bg_texture = NULL; } if (self->bg_sub_texture != NULL) { cogl_handle_unref (self->bg_sub_texture); self->bg_sub_texture = NULL; } if (self->bg_bumpmap != NULL) { cogl_handle_unref (self->bg_bumpmap); self->bg_bumpmap = NULL; } G_OBJECT_CLASS (st_background_effect_parent_class)->dispose (gobject); } static void st_background_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StBackgroundEffect *self = ST_BACKGROUND_EFFECT (gobject); switch (prop_id) { case PROP_BUMPMAP: self->bumpmap_location = g_value_dup_string (value); if (self->bg_bumpmap != NULL) { cogl_handle_unref (self->bg_bumpmap); self->bg_bumpmap = NULL; } if (self->bumpmap_location == NULL) { break; } GFile *file = g_file_new_for_path (g_strdup (self->bumpmap_location)); if (g_file_query_exists (file, NULL)) { self->bg_bumpmap = st_cogl_texture_new_from_file_wrapper (self->bumpmap_location, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); } g_object_unref (file); if (self->bg_bumpmap != NULL) { self->bumptex_width_i = cogl_texture_get_width (self->bg_bumpmap); self->bumptex_height_i = cogl_texture_get_height (self->bg_bumpmap); cogl_pipeline_set_layer_texture (self->pipeline0, 1, self->bg_bumpmap); } else { cogl_pipeline_set_layer_null_texture (self->pipeline0, 1, COGL_TEXTURE_TYPE_2D); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_background_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StBackgroundEffect *self = ST_BACKGROUND_EFFECT (gobject); switch (prop_id) { case PROP_BUMPMAP: g_value_set_string (value, self->bumpmap_location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_background_effect_class_init (StBackgroundEffectClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; effect_class->pre_paint = st_background_effect_pre_paint; gobject_class->set_property = st_background_effect_set_property; gobject_class->get_property = st_background_effect_get_property; gobject_class->dispose = st_background_effect_dispose; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->paint_target = st_background_effect_paint_target; obj_props[PROP_BUMPMAP] = g_param_spec_string ("bumpmap", "Background effect construct prop", "Set bumpmap path", "/usr/share/cinnamon/bumpmaps/bumpmap.png", G_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void st_background_effect_init (StBackgroundEffect *self) { CoglContext *ctx; StBackgroundEffectClass *klass = ST_BACKGROUND_EFFECT_GET_CLASS (self); if (G_UNLIKELY (klass->base_pipeline == NULL)) { ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); klass->base_pipeline = cogl_pipeline_new (ctx); } self->pipeline0 = cogl_pipeline_copy (klass->base_pipeline); self->pipeline1 = cogl_pipeline_copy (klass->base_pipeline); self->pipeline2 = cogl_pipeline_copy (klass->base_pipeline); self->pipeline3 = cogl_pipeline_copy (klass->base_pipeline); self->pipeline4 = cogl_pipeline_copy (klass->base_pipeline); CoglSnippet *snippet; snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, box_blur_glsl_declarations, NULL); cogl_snippet_set_replace (snippet, box_blur_glsl_shader); cogl_pipeline_add_layer_snippet (self->pipeline0, 0, snippet); cogl_pipeline_add_layer_snippet (self->pipeline1, 0, snippet); cogl_pipeline_add_layer_snippet (self->pipeline3, 0, snippet); cogl_object_unref (snippet); cogl_pipeline_set_layer_wrap_mode (self->pipeline0, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode (self->pipeline0, 1, COGL_PIPELINE_WRAP_MODE_REPEAT); cogl_pipeline_set_layer_wrap_mode (self->pipeline1, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode (self->pipeline2, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode (self->pipeline3, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode (self->pipeline4, 0, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_cull_face_mode (self->pipeline0, COGL_PIPELINE_CULL_FACE_MODE_NONE); cogl_pipeline_set_cull_face_mode (self->pipeline1, COGL_PIPELINE_CULL_FACE_MODE_NONE); cogl_pipeline_set_cull_face_mode (self->pipeline2, COGL_PIPELINE_CULL_FACE_MODE_NONE); cogl_pipeline_set_cull_face_mode (self->pipeline3, COGL_PIPELINE_CULL_FACE_MODE_NONE); cogl_pipeline_set_cull_face_mode (self->pipeline4, COGL_PIPELINE_CULL_FACE_MODE_NONE); cogl_pipeline_set_layer_filters (self->pipeline0, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_pipeline_set_layer_filters (self->pipeline0, 1, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_filters (self->pipeline1, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_pipeline_set_layer_filters (self->pipeline2, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_pipeline_set_layer_filters (self->pipeline3, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_pipeline_set_layer_filters (self->pipeline4, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_pipeline_set_layer_null_texture (self->pipeline0, 0, COGL_TEXTURE_TYPE_2D); cogl_pipeline_set_layer_null_texture (self->pipeline1, 0, COGL_TEXTURE_TYPE_2D); cogl_pipeline_set_layer_null_texture (self->pipeline2, 0, COGL_TEXTURE_TYPE_2D); cogl_pipeline_set_layer_null_texture (self->pipeline3, 0, COGL_TEXTURE_TYPE_2D); cogl_pipeline_set_layer_null_texture (self->pipeline4, 0, COGL_TEXTURE_TYPE_2D); self->pixel_step_uniform0 = cogl_pipeline_get_uniform_location (self->pipeline0, "pixel_step"); self->BumpTex_uniform = cogl_pipeline_get_uniform_location (self->pipeline0, "BumpTex"); self->bump_step_uniform = cogl_pipeline_get_uniform_location (self->pipeline0, "bump_step"); self->pixel_step_uniform1 = cogl_pipeline_get_uniform_location (self->pipeline1, "pixel_step"); self->pixel_step_uniform2 = cogl_pipeline_get_uniform_location (self->pipeline3, "pixel_step"); cogl_pipeline_set_blend (self->pipeline0, "RGBA = ADD(SRC_COLOR, DST_COLOR*0)", NULL); cogl_pipeline_set_blend (self->pipeline1, "RGBA = ADD (SRC_COLOR*DST_COLOR[A], DST_COLOR*(1-DST_COLOR[A]))", NULL); cogl_pipeline_set_color4ub (self->pipeline1, 0xff, 0xff, 0xff, 0xff); cogl_pipeline_set_alpha_test_function (self->pipeline2, COGL_PIPELINE_ALPHA_FUNC_LESS, 0.004f); cogl_pipeline_set_color_mask (self->pipeline2, COGL_COLOR_MASK_ALPHA); cogl_pipeline_set_blend (self->pipeline2, "RGBA = ADD(SRC_COLOR, DST_COLOR*0)", NULL); cogl_pipeline_set_alpha_test_function (self->pipeline3, COGL_PIPELINE_ALPHA_FUNC_GEQUAL, 0.004f); cogl_pipeline_set_color_mask (self->pipeline3, COGL_COLOR_MASK_ALL); cogl_pipeline_set_blend (self->pipeline3, "RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))", NULL); cogl_pipeline_set_alpha_test_function (self->pipeline4, COGL_PIPELINE_ALPHA_FUNC_GEQUAL, 0.004f); cogl_pipeline_set_color_mask (self->pipeline4, COGL_COLOR_MASK_ALL); cogl_pipeline_set_blend (self->pipeline4, "RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))", NULL); self->bg_texture = NULL; self->bg_sub_texture = NULL; self->bumpmap_location = "/usr/share/cinnamon/bumpmaps/frost.png"; self->bg_bumpmap = st_cogl_texture_new_from_file_wrapper (self->bumpmap_location, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); if (self->bg_bumpmap != NULL) { self->bumptex_width_i = cogl_texture_get_width (self->bg_bumpmap); self->bumptex_height_i = cogl_texture_get_height (self->bg_bumpmap); cogl_pipeline_set_layer_texture (self->pipeline0, 1, self->bg_bumpmap); } else { cogl_pipeline_set_layer_null_texture (self->pipeline0, 1, COGL_TEXTURE_TYPE_2D); } cogl_pipeline_set_layer_combine (self->pipeline0,1, "RGBA = REPLACE (PREVIOUS)", NULL); self->old_time = 0; self->opacity = 0; } ClutterEffect * st_background_effect_new () { return g_object_new (ST_TYPE_BACKGROUND_EFFECT, NULL); } Cinnamon-2.8.6/src/st/st-theme-node.h0000664000175000017500000002661412625666430016277 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node.h: style information for one node in a tree of themed objects * * Copyright 2008-2010 Red Hat, Inc. * Copyright 2009, 2010 Florian Müllner * Copyright 2010 Giovanni Campagna * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_NODE_H__ #define __ST_THEME_NODE_H__ #include #include "st-border-image.h" #include "st-icon-colors.h" #include "st-shadow.h" G_BEGIN_DECLS /** * SECTION:StThemeNode * @short_description: style information for one node in a tree of themed objects * * A #StThemeNode represents the CSS style information (the set of CSS properties) for one * node in a tree of themed objects. In typical usage, it represents the style information * for a single #ClutterActor. A #StThemeNode is immutable: attributes such as the * CSS classes for the node are passed in at construction. If the attributes of the node * or any parent node change, the node should be discarded and a new node created. * #StThemeNode has generic accessors to look up properties by name and specific * accessors for standard CSS properties that add caching and handling of various * details of the CSS specification. #StThemeNode also has convenience functions to help * in implementing a #ClutterActor with borders and padding. */ typedef struct _StTheme StTheme; typedef struct _StThemeContext StThemeContext; typedef struct _StThemeNode StThemeNode; typedef struct _StThemeNodeClass StThemeNodeClass; #define ST_TYPE_THEME_NODE (st_theme_node_get_type ()) #define ST_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_NODE, StThemeNode)) #define ST_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_NODE, StThemeNodeClass)) #define ST_IS_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_NODE)) #define ST_IS_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_NODE)) #define ST_THEME_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_NODE, StThemeNodeClass)) typedef enum { ST_SIDE_TOP, ST_SIDE_RIGHT, ST_SIDE_BOTTOM, ST_SIDE_LEFT } StSide; typedef enum { ST_CORNER_TOPLEFT, ST_CORNER_TOPRIGHT, ST_CORNER_BOTTOMRIGHT, ST_CORNER_BOTTOMLEFT } StCorner; /* These are the CSS values; that doesn't mean we have to implement blink... */ typedef enum { ST_TEXT_DECORATION_UNDERLINE = 1 << 0, ST_TEXT_DECORATION_OVERLINE = 1 << 1, ST_TEXT_DECORATION_LINE_THROUGH = 1 << 2, ST_TEXT_DECORATION_BLINK = 1 << 3 } StTextDecoration; typedef enum { ST_TEXT_ALIGN_LEFT = PANGO_ALIGN_LEFT, ST_TEXT_ALIGN_CENTER = PANGO_ALIGN_CENTER, ST_TEXT_ALIGN_RIGHT = PANGO_ALIGN_RIGHT, ST_TEXT_ALIGN_JUSTIFY } StTextAlign; typedef enum { ST_GRADIENT_NONE, ST_GRADIENT_VERTICAL, ST_GRADIENT_HORIZONTAL, ST_GRADIENT_RADIAL } StGradientType; GType st_theme_node_get_type (void) G_GNUC_CONST; StThemeNode *st_theme_node_new (StThemeContext *context, StThemeNode *parent_node, /* can be null */ StTheme *theme, /* can be null */ GType element_type, const char *element_id, const char *element_class, const char *pseudo_class, const char *inline_style, gboolean important); StThemeNode *st_theme_node_get_parent (StThemeNode *node); StTheme *st_theme_node_get_theme (StThemeNode *node); gboolean st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b); guint st_theme_node_hash (StThemeNode *node); GType st_theme_node_get_element_type (StThemeNode *node); const char *st_theme_node_get_element_id (StThemeNode *node); GStrv st_theme_node_get_element_classes (StThemeNode *node); GStrv st_theme_node_get_pseudo_classes (StThemeNode *node); /* Generic getters ... these are not cached so are less efficient. The other * reason for adding the more specific version is that we can handle the * details of the actual CSS rules, which can be complicated, especially * for fonts */ gboolean st_theme_node_lookup_color (StThemeNode *node, const char *property_name, gboolean inherit, ClutterColor *color); gboolean st_theme_node_lookup_double (StThemeNode *node, const char *property_name, gboolean inherit, double *value); gboolean st_theme_node_lookup_length (StThemeNode *node, const char *property_name, gboolean inherit, gdouble *length); gboolean st_theme_node_lookup_shadow (StThemeNode *node, const char *property_name, gboolean inherit, StShadow **shadow); /* Easier-to-use variants of the above, for application-level use */ void st_theme_node_get_color (StThemeNode *node, const char *property_name, ClutterColor *color); gdouble st_theme_node_get_double (StThemeNode *node, const char *property_name); gdouble st_theme_node_get_length (StThemeNode *node, const char *property_name); StShadow *st_theme_node_get_shadow (StThemeNode *node, const char *property_name); /* Specific getters for particular properties: cached */ void st_theme_node_get_background_color (StThemeNode *node, ClutterColor *color); void st_theme_node_get_foreground_color (StThemeNode *node, ClutterColor *color); void st_theme_node_get_background_gradient (StThemeNode *node, StGradientType *type, ClutterColor *start, ClutterColor *end); const char *st_theme_node_get_background_image (StThemeNode *node); const char *st_theme_node_get_background_bumpmap (StThemeNode *node); int st_theme_node_get_border_width (StThemeNode *node, StSide side); int st_theme_node_get_border_radius (StThemeNode *node, StCorner corner); void st_theme_node_get_border_color (StThemeNode *node, StSide side, ClutterColor *color); int st_theme_node_get_outline_width (StThemeNode *node); void st_theme_node_get_outline_color (StThemeNode *node, ClutterColor *color); double st_theme_node_get_padding (StThemeNode *node, StSide side); double st_theme_node_get_horizontal_padding (StThemeNode *node); double st_theme_node_get_vertical_padding (StThemeNode *node); int st_theme_node_get_width (StThemeNode *node); int st_theme_node_get_height (StThemeNode *node); int st_theme_node_get_min_width (StThemeNode *node); int st_theme_node_get_min_height (StThemeNode *node); int st_theme_node_get_max_width (StThemeNode *node); int st_theme_node_get_max_height (StThemeNode *node); int st_theme_node_get_transition_duration (StThemeNode *node); StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node); StTextAlign st_theme_node_get_text_align (StThemeNode *node); /* Font rule processing is pretty complicated, so we just hardcode it * under the standard font/font-family/font-size/etc names. This means * you can't have multiple separate styled fonts for a single item, * but that should be OK. */ const PangoFontDescription *st_theme_node_get_font (StThemeNode *node); StBorderImage *st_theme_node_get_border_image (StThemeNode *node); StShadow *st_theme_node_get_box_shadow (StThemeNode *node); StShadow *st_theme_node_get_text_shadow (StThemeNode *node); StShadow *st_theme_node_get_background_image_shadow (StThemeNode *node); StIconColors *st_theme_node_get_icon_colors (StThemeNode *node); /* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */ void st_theme_node_adjust_for_height (StThemeNode *node, float *for_height); void st_theme_node_adjust_preferred_width (StThemeNode *node, float *min_width_p, float *natural_width_p); void st_theme_node_adjust_for_width (StThemeNode *node, float *for_width); void st_theme_node_adjust_preferred_height (StThemeNode *node, float *min_height_p, float *natural_height_p); /* Helper for allocate() ClutterActor vfunc */ void st_theme_node_get_content_box (StThemeNode *node, const ClutterActorBox *allocation, ClutterActorBox *content_box); /* Helper for StThemeNodeTransition */ void st_theme_node_get_paint_box (StThemeNode *node, const ClutterActorBox *allocation, ClutterActorBox *paint_box); /* Helper for background prerendering */ void st_theme_node_get_background_paint_box (StThemeNode *node, const ClutterActorBox *allocation, ClutterActorBox *paint_box); gboolean st_theme_node_geometry_equal (StThemeNode *node, StThemeNode *other); gboolean st_theme_node_paint_equal (StThemeNode *node, StThemeNode *other); void st_theme_node_paint (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity); void st_theme_node_copy_cached_paint_state (StThemeNode *node, StThemeNode *other); G_END_DECLS #endif /* __ST_THEME_NODE_H__ */ Cinnamon-2.8.6/src/st/st-box-layout.c0000664000175000017500000011335612625666430016350 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-box-layout.h: box layout actor * * Copyright 2009 Intel Corporation. * Copyright 2009 Abderrahim Kitouni * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010 Florian Muellner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /* Portions copied from Clutter: * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. */ /** * SECTION:st-box-layout * @short_description: a layout container arranging children in a single line * * The #StBoxLayout arranges its children along a single line, where each * child can be allocated either its preferred size or larger if the expand * option is set. If the fill option is set, the actor will be allocated more * than its requested size. If the fill option is not set, but the expand option * is enabled, then the position of the actor within the available space can * be determined by the alignment child property. * */ #include #include "st-box-layout.h" #include "st-private.h" #include "st-scrollable.h" #include "st-box-layout-child.h" static void st_box_container_iface_init (ClutterContainerIface *iface); static void st_box_scrollable_interface_init (StScrollableInterface *iface); G_DEFINE_TYPE_WITH_CODE (StBoxLayout, st_box_layout, ST_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, st_box_container_iface_init) G_IMPLEMENT_INTERFACE (ST_TYPE_SCROLLABLE, st_box_scrollable_interface_init)); #define BOX_LAYOUT_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT, StBoxLayoutPrivate)) enum { PROP_0, PROP_VERTICAL, PROP_PACK_START, PROP_ALIGN_END, PROP_HADJUST, PROP_VADJUST }; struct _StBoxLayoutPrivate { guint spacing; guint is_vertical : 1; guint is_pack_start : 1; guint is_align_end : 1; StAdjustment *hadjustment; StAdjustment *vadjustment; }; /* * ClutterContainer Interface Implementation */ static void st_box_sort_depth_order (ClutterContainer *container) { /* The parent class' implementation would mess up the * left-to-right order of the children - do nothing instead */ } /* * StScrollable Interface Implementation */ static void adjustment_value_notify_cb (StAdjustment *adjustment, GParamSpec *pspec, StBoxLayout *box) { clutter_actor_queue_redraw (CLUTTER_ACTOR (box)); } static void scrollable_set_adjustments (StScrollable *scrollable, StAdjustment *hadjustment, StAdjustment *vadjustment) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (scrollable)->priv; g_object_freeze_notify (G_OBJECT (scrollable)); if (hadjustment != priv->hadjustment) { if (priv->hadjustment) { g_signal_handlers_disconnect_by_func (priv->hadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->hadjustment); } if (hadjustment) { g_object_ref (hadjustment); g_signal_connect (hadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->hadjustment = hadjustment; g_object_notify (G_OBJECT (scrollable), "hadjustment"); } if (vadjustment != priv->vadjustment) { if (priv->vadjustment) { g_signal_handlers_disconnect_by_func (priv->vadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->vadjustment); } if (vadjustment) { g_object_ref (vadjustment); g_signal_connect (vadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->vadjustment = vadjustment; g_object_notify (G_OBJECT (scrollable), "vadjustment"); } g_object_thaw_notify (G_OBJECT (scrollable)); } static void scrollable_get_adjustments (StScrollable *scrollable, StAdjustment **hadjustment, StAdjustment **vadjustment) { StBoxLayoutPrivate *priv; priv = (ST_BOX_LAYOUT (scrollable))->priv; if (hadjustment) *hadjustment = priv->hadjustment; if (vadjustment) *vadjustment = priv->vadjustment; } static void st_box_scrollable_interface_init (StScrollableInterface *iface) { iface->set_adjustments = scrollable_set_adjustments; iface->get_adjustments = scrollable_get_adjustments; } static void st_box_container_iface_init (ClutterContainerIface *iface) { iface->sort_depth_order = st_box_sort_depth_order; iface->child_meta_type = ST_TYPE_BOX_LAYOUT_CHILD; } static void st_box_layout_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; StAdjustment *adjustment; switch (property_id) { case PROP_VERTICAL: g_value_set_boolean (value, priv->is_vertical); break; case PROP_PACK_START: g_value_set_boolean (value, priv->is_pack_start); break; case PROP_ALIGN_END: g_value_set_boolean (value, priv->is_align_end); break; case PROP_HADJUST: scrollable_get_adjustments (ST_SCROLLABLE (object), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: scrollable_get_adjustments (ST_SCROLLABLE (object), NULL, &adjustment); g_value_set_object (value, adjustment); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_box_layout_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StBoxLayout *box = ST_BOX_LAYOUT (object); switch (property_id) { case PROP_VERTICAL: st_box_layout_set_vertical (box, g_value_get_boolean (value)); break; case PROP_PACK_START: st_box_layout_set_pack_start (box, g_value_get_boolean (value)); break; case PROP_ALIGN_END: st_box_layout_set_align_end (box, g_value_get_boolean (value)); break; case PROP_HADJUST: scrollable_set_adjustments (ST_SCROLLABLE (object), g_value_get_object (value), box->priv->vadjustment); break; case PROP_VADJUST: scrollable_set_adjustments (ST_SCROLLABLE (object), box->priv->hadjustment, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_box_layout_dispose (GObject *object) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; if (priv->hadjustment) { g_object_unref (priv->hadjustment); priv->hadjustment = NULL; } if (priv->vadjustment) { g_object_unref (priv->vadjustment); priv->vadjustment = NULL; } G_OBJECT_CLASS (st_box_layout_parent_class)->dispose (object); } static void get_content_preferred_width (StBoxLayout *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StBoxLayoutPrivate *priv = self->priv; gint n_children = 0; gint n_fixed = 0; gfloat min_width, natural_width; GList *l, *children; min_width = 0; natural_width = 0; children = st_container_get_children_list (ST_CONTAINER (self)); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = l->data; gfloat child_min = 0, child_nat = 0; gboolean child_fill; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; n_children++; if (clutter_actor_get_fixed_position_set (child)) { n_fixed++; continue; } if (priv->is_vertical) { _st_actor_get_preferred_width (child, -1, FALSE, &child_min, &child_nat); min_width = MAX (child_min, min_width); natural_width = MAX (child_nat, natural_width); } else { clutter_container_child_get (CLUTTER_CONTAINER (self), child, "y-fill", &child_fill, NULL); _st_actor_get_preferred_width (child, for_height, child_fill, &child_min, &child_nat); min_width += child_min; natural_width += child_nat; } } if (!priv->is_vertical && (n_children - n_fixed) > 1) { min_width += priv->spacing * (n_children - n_fixed - 1); natural_width += priv->spacing * (n_children - n_fixed - 1); } if (min_width_p) *min_width_p = min_width; if (natural_width_p) *natural_width_p = natural_width; } static void st_box_layout_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_height (theme_node, &for_height); get_content_preferred_width (ST_BOX_LAYOUT (actor), for_height, min_width_p, natural_width_p); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void get_content_preferred_height (StBoxLayout *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBoxLayoutPrivate *priv = self->priv; gint n_children = 0; gint n_fixed = 0; gfloat min_height, natural_height; GList *l, *children; min_height = 0; natural_height = 0; children = st_container_get_children_list (ST_CONTAINER (self)); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = l->data; gfloat child_min = 0, child_nat = 0; gboolean child_fill = FALSE; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; n_children++; if (clutter_actor_get_fixed_position_set (child)) { n_fixed++; continue; } if (priv->is_vertical) { clutter_container_child_get ((ClutterContainer*) self, child, "x-fill", &child_fill, NULL); } _st_actor_get_preferred_height (child, (priv->is_vertical) ? for_width : -1, child_fill, &child_min, &child_nat); if (!priv->is_vertical) { min_height = MAX (child_min, min_height); natural_height = MAX (child_nat, natural_height); } else { min_height += child_min; natural_height += child_nat; } } if (priv->is_vertical && (n_children - n_fixed) > 1) { min_height += priv->spacing * (n_children - n_fixed - 1); natural_height += priv->spacing * (n_children - n_fixed - 1); } if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; } static void st_box_layout_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_width (theme_node, &for_width); if (priv->hadjustment) { /* If we're scrolled, the parent calls us with the width that * we'll actually get, which can be smaller than the minimum * width that we give our contents. */ gfloat min_width; get_content_preferred_width (self, -1, &min_width, NULL); for_width = MAX (for_width, min_width); } get_content_preferred_height (self, for_width, min_height_p, natural_height_p); st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } typedef struct { int child_index; gfloat shrink_amount; } BoxChildShrink; /* Sort with the greatest shrink amount first */ static int compare_by_shrink_amount (const void *a, const void *b) { float diff = ((const BoxChildShrink *)a)->shrink_amount - ((const BoxChildShrink *)b)->shrink_amount; return diff < 0 ? 1 : (diff == 0 ? 0 : -1); } /* Sort in ascending order by child index */ static int compare_by_child_index (const void *a, const void *b) { return ((const BoxChildShrink *)a)->child_index - ((const BoxChildShrink *)b)->child_index; } static BoxChildShrink * compute_shrinks (StBoxLayout *self, gfloat for_length, gfloat total_shrink) { StBoxLayoutPrivate *priv = self->priv; GList *children = st_container_get_children_list (ST_CONTAINER (self)); int n_children = g_list_length (children); BoxChildShrink *shrinks = g_new0 (BoxChildShrink, n_children); gfloat shrink_so_far; gfloat base_shrink = 0; /* the "= 0" is just to make gcc happy */ int n_shrink_children; GList *l; int i; /* The effect that we want is that all the children get an equal chance * to expand from their minimum size up to the natural size. Or to put * it a different way, we want to start by shrinking only the child that * can shrink most, then shrink that and the next most shrinkable child, * to the point where we are shrinking everything. */ /* Find the amount of possible shrink for each child */ int n_possible_shrink_children = 0; for (l = children, i = 0; l; l = l->next, i++) { ClutterActor *child; gfloat child_min, child_nat; gboolean child_fill; gboolean fixed; child = (ClutterActor*) l->data; fixed = clutter_actor_get_fixed_position_set (child); shrinks[i].child_index = i; if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed) { if (priv->is_vertical) { clutter_container_child_get ((ClutterContainer*) self, child, "x-fill", &child_fill, NULL); _st_actor_get_preferred_height (child, for_length, child_fill, &child_min, &child_nat); } else { clutter_container_child_get ((ClutterContainer*) self, child, "y-fill", &child_fill, NULL); _st_actor_get_preferred_width (child, for_length, child_fill, &child_min, &child_nat); } shrinks[i].shrink_amount = MAX (0., child_nat - child_min); n_possible_shrink_children++; } else { shrinks[i].shrink_amount = -1.; } } /* We want to process children starting from the child with the maximum available * shrink, so sort in this order; !visible children end up at the end */ qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_shrink_amount); /* +--+ * | | * | | +-- * | | | | * | | | | +-+ * --+--+-+-+-+-+---------- * | | | | | | +-+ +-+ * | | | | | | | | | | * --+--+-+-+-+-+-+-+------ * * We are trying to find the correct position for the upper line the "water mark" * so that total of the portion of the bars above the line is equal to the total * amount we want to shrink. */ /* Start by moving the line downward, top-of-bar by top-of-bar */ shrink_so_far = 0; for (n_shrink_children = 1; n_shrink_children <= n_possible_shrink_children; n_shrink_children++) { if (n_shrink_children < n_possible_shrink_children) base_shrink = shrinks[n_shrink_children].shrink_amount; else base_shrink = 0; shrink_so_far += n_shrink_children * (shrinks[n_shrink_children - 1].shrink_amount - base_shrink); if (shrink_so_far >= total_shrink || n_shrink_children == n_possible_shrink_children) break; } /* OK, we found enough shrinkage, move it back upwards to the right position */ base_shrink += (shrink_so_far - total_shrink) / n_shrink_children; if (base_shrink < 0) /* can't shrink that much, probably round-off error */ base_shrink = 0; /* Assign the portion above the base shrink line to the shrink_amount */ for (i = 0; i < n_shrink_children; i++) shrinks[i].shrink_amount -= base_shrink; for (; i < n_children; i++) shrinks[i].shrink_amount = 0; /* And sort back to their original order */ qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_child_index); return shrinks; } static void st_box_layout_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorBox content_box; gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height; gfloat position, next_position; GList *l, *children; gint n_expand_children = 0, i; gfloat expand_amount, shrink_amount; BoxChildShrink *shrinks = NULL; // Home-made logical xor gboolean flip = (!(st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) != !priv->is_align_end) && (!priv->is_vertical); gboolean reverse_order = (!priv->is_align_end != !priv->is_pack_start); CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, flags); children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; st_theme_node_get_content_box (theme_node, box, &content_box); avail_width = content_box.x2 - content_box.x1; avail_height = content_box.y2 - content_box.y1; get_content_preferred_width (ST_BOX_LAYOUT (actor), avail_height, &min_width, &natural_width); get_content_preferred_height (ST_BOX_LAYOUT (actor), MAX (avail_width, min_width), &min_height, &natural_height); /* update adjustments for scrolling */ if (priv->vadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", MAX (min_height, avail_height), "page-size", avail_height, "step-increment", avail_height / 6, "page-increment", avail_height - avail_height / 6, NULL); prev_value = st_adjustment_get_value (priv->vadjustment); st_adjustment_set_value (priv->vadjustment, prev_value); } if (priv->hadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", MAX (min_width, avail_width), "page-size", avail_width, "step-increment", avail_width / 6, "page-increment", avail_width - avail_width / 6, NULL); prev_value = st_adjustment_get_value (priv->hadjustment); st_adjustment_set_value (priv->hadjustment, prev_value); } if (avail_height < min_height) { avail_height = min_height; content_box.y2 = content_box.y1 + avail_height; } if (avail_width < min_width) { avail_width = min_width; content_box.x2 = content_box.x1 + avail_width; } if (priv->is_vertical) { expand_amount = MAX (0, avail_height - natural_height); shrink_amount = MAX (0, natural_height - avail_height); } else { expand_amount = MAX (0, avail_width - natural_width); shrink_amount = MAX (0, natural_width - avail_width); } if (expand_amount > 0) { /* count the number of children with expand set to TRUE */ n_expand_children = 0; for (l = children; l; l = l->next) { ClutterActor *child = l->data; gboolean expand; if (!CLUTTER_ACTOR_IS_VISIBLE (child) || clutter_actor_get_fixed_position_set (child)) continue; clutter_container_child_get ((ClutterContainer *) actor, child, "expand", &expand, NULL); if (expand) n_expand_children++; } if (n_expand_children == 0) expand_amount = 0; } else if (shrink_amount > 0) { shrinks = compute_shrinks (ST_BOX_LAYOUT (actor), priv->is_vertical ? avail_width : avail_height, shrink_amount); } if (priv->is_vertical) position = content_box.y1; else if (flip) position = content_box.x2; else position = content_box.x1; if (reverse_order) { l = g_list_last (children); i = g_list_length (children) - 1; } else { l = children; i = 0; } gfloat init_padding = (avail_width/2) - (natural_width/2); while (l) { ClutterActor *child = (ClutterActor*) l->data; ClutterActorBox child_box; gfloat child_min, child_nat, child_allocated; gboolean xfill, yfill, expand, fixed; StAlign xalign, yalign; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) goto next_child; fixed = clutter_actor_get_fixed_position_set (child); if (fixed) { clutter_actor_allocate_preferred_size (child, flags); goto next_child; } clutter_container_child_get ((ClutterContainer*) actor, child, "x-fill", &xfill, "y-fill", &yfill, "x-align", &xalign, "y-align", &yalign, "expand", &expand, NULL); if (priv->is_vertical) { _st_actor_get_preferred_height (child, avail_width, xfill, &child_min, &child_nat); } else { _st_actor_get_preferred_width (child, avail_height, yfill, &child_min, &child_nat); } child_allocated = child_nat; if (expand_amount > 0 && expand) child_allocated += expand_amount / n_expand_children; else if (shrink_amount > 0) child_allocated -= shrinks[i].shrink_amount; if (flip) { next_position = position - child_allocated; } else { next_position = position + child_allocated; } if (priv->is_vertical) { child_box.y1 = (int)(0.5 + position); child_box.y2 = (int)(0.5 + next_position); child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } else { if (flip) { child_box.x1 = (int)(0.5 + next_position); child_box.x2 = (int)(0.5 + position); } else { child_box.x1 = (int)(0.5 + position); child_box.x2 = (int)(0.5 + next_position); } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } if (flip) position = next_position - priv->spacing; else position = next_position + priv->spacing; next_child: if (reverse_order) { l = l->prev; i--; } else { l = l->next; i++; } } if (shrinks) g_free (shrinks); } static void st_box_layout_apply_transform (ClutterActor *a, CoglMatrix *m) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (a)->priv; gdouble x, y; CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->apply_transform (a, m); if (priv->hadjustment) x = st_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = st_adjustment_get_value (priv->vadjustment); else y = 0; cogl_matrix_translate (m, (int) -x, (int) -y, 0); } /* If we are translated, then we need to translate back before chaining * up or the background and borders will be drawn in the wrong place */ static void get_border_paint_offsets (StBoxLayout *self, double *x, double *y) { StBoxLayoutPrivate *priv = self->priv; if (priv->hadjustment) *x = st_adjustment_get_value (priv->hadjustment); else *x = 0; if (priv->vadjustment) *y = st_adjustment_get_value (priv->vadjustment); else *y = 0; } static void st_box_layout_paint (ClutterActor *actor) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); GList *l, *children; gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->paint (actor); if (x != 0 || y != 0) { cogl_pop_matrix (); } children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; /* The content area forms the viewport into the scrolled contents, while * the borders and background stay in place; after drawing the borders and * background, we clip to the content area */ if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = (ClutterActor*) l->data; if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); } static void st_box_layout_pick (ClutterActor *actor, const ClutterColor *color) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); GList *l, *children; gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->pick (actor, color); if (x != 0 || y != 0) { cogl_pop_matrix (); } children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = (ClutterActor*) l->data; if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); } static gboolean st_box_layout_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { StBoxLayout *self = ST_BOX_LAYOUT (actor); gdouble x, y; if (!CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->get_paint_volume (actor, volume)) return FALSE; /* When scrolled, st_box_layout_apply_transform() includes the scroll offset * and affects paint volumes. This is right for our children, but our paint volume * is determined by our allocation and borders and doesn't scroll, so we need * to reverse-compensate here, the same as we do when painting. */ get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { ClutterVertex origin; clutter_paint_volume_get_origin (volume, &origin); origin.x += x; origin.y += y; clutter_paint_volume_set_origin (volume, &origin); } return TRUE; } static void st_box_layout_style_changed (StWidget *self) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (self); int old_spacing = priv->spacing; double spacing; spacing = st_theme_node_get_length (theme_node, "spacing"); priv->spacing = (int)(spacing + 0.5); if (priv->spacing != old_spacing) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); ST_WIDGET_CLASS (st_box_layout_parent_class)->style_changed (self); } static void st_box_layout_class_init (StBoxLayoutClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StBoxLayoutPrivate)); object_class->get_property = st_box_layout_get_property; object_class->set_property = st_box_layout_set_property; object_class->dispose = st_box_layout_dispose; actor_class->allocate = st_box_layout_allocate; actor_class->get_preferred_width = st_box_layout_get_preferred_width; actor_class->get_preferred_height = st_box_layout_get_preferred_height; actor_class->apply_transform = st_box_layout_apply_transform; actor_class->paint = st_box_layout_paint; actor_class->get_paint_volume = st_box_layout_get_paint_volume; actor_class->pick = st_box_layout_pick; widget_class->style_changed = st_box_layout_style_changed; pspec = g_param_spec_boolean ("vertical", "Vertical", "Whether the layout should be vertical, rather" "than horizontal", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VERTICAL, pspec); pspec = g_param_spec_boolean ("pack-start", "Pack Start", "Whether to pack items at the start of the box", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PACK_START, pspec); pspec = g_param_spec_boolean ("align-end", "Align End", "Whether the children should be flushed to the end", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ALIGN_END, pspec); /* StScrollable properties */ g_object_class_override_property (object_class, PROP_HADJUST, "hadjustment"); g_object_class_override_property (object_class, PROP_VADJUST, "vadjustment"); } static void st_box_layout_init (StBoxLayout *self) { self->priv = BOX_LAYOUT_PRIVATE (self); } /** * st_box_layout_new: * * Create a new #StBoxLayout. * * Returns: a newly allocated #StBoxLayout */ StWidget * st_box_layout_new (void) { return g_object_new (ST_TYPE_BOX_LAYOUT, NULL); } /** * st_box_layout_set_vertical: * @box: A #StBoxLayout * @vertical: %TRUE if the layout should be vertical * * Set the value of the #StBoxLayout::vertical property * */ void st_box_layout_set_vertical (StBoxLayout *box, gboolean vertical) { g_return_if_fail (ST_IS_BOX_LAYOUT (box)); if (box->priv->is_vertical != vertical) { box->priv->is_vertical = vertical; clutter_actor_queue_relayout ((ClutterActor*) box); g_object_notify (G_OBJECT (box), "vertical"); } } /** * st_box_layout_get_vertical: * @box: A #StBoxLayout * * Get the value of the #StBoxLayout::vertical property. * * Returns: %TRUE if the layout is vertical */ gboolean st_box_layout_get_vertical (StBoxLayout *box) { g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE); return box->priv->is_vertical; } /** * st_box_layout_set_pack_start: * @box: A #StBoxLayout * @pack_start: %TRUE if the layout should use pack-start * * Set the value of the #StBoxLayout::pack-start property. * */ void st_box_layout_set_pack_start (StBoxLayout *box, gboolean pack_start) { g_return_if_fail (ST_IS_BOX_LAYOUT (box)); if (box->priv->is_pack_start != pack_start) { box->priv->is_pack_start = pack_start; clutter_actor_queue_relayout ((ClutterActor*) box); g_object_notify (G_OBJECT (box), "pack-start"); } } /** * st_box_layout_get_pack_start: * @box: A #StBoxLayout * * Get the value of the #StBoxLayout::pack-start property. * * Returns: %TRUE if pack-start is enabled */ gboolean st_box_layout_get_pack_start (StBoxLayout *box) { g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE); return box->priv->is_pack_start; } /** * st_box_layout_set_align_end: * @box: A #StBoxLayout * @align_end: %TRUE if the layout should use align-end * * Set the value of the #StBoxLayout::align-end property. * */ void st_box_layout_set_align_end (StBoxLayout *box, gboolean align_end) { g_return_if_fail (ST_IS_BOX_LAYOUT (box)); if (box->priv->is_align_end != align_end) { box->priv->is_align_end = align_end; clutter_actor_queue_relayout ((ClutterActor*) box); g_object_notify (G_OBJECT (box), "align-end"); } } /** * st_box_layout_get_align_end: * @box: A #StBoxLayout * * Get the value of the #StBoxLayout::align-end property. * * Returns: %TRUE if align-end is enabled */ gboolean st_box_layout_get_align_end (StBoxLayout *box) { g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE); return box->priv->is_align_end; } /** * st_box_layout_insert_actor: * @self: A #StBoxLayout * @actor: A #ClutterActor * @pos: position to insert actor * * Adds @actor to @self at position @pos. If @pos is * negative or larger than the number of elements in the * list then @actor is added after all the others previously * added. */ void st_box_layout_insert_actor (StBoxLayout *self, ClutterActor *actor, int pos) { clutter_container_add_actor((ClutterContainer*) self, actor); st_container_move_child (ST_CONTAINER (self), actor, pos); } /** * st_box_layout_insert_before: * @self: A #StBoxLayout * @actor: A #ClutterActor * @sibling: A previously added #ClutterActor * * Adds @actor to @self at the position before @sibling. * @sibling cannot be %NULL and must be already a child * of @self. */ void st_box_layout_insert_before (StBoxLayout *self, ClutterActor *actor, ClutterActor *sibling) { g_return_if_fail (ST_IS_BOX_LAYOUT (self)); clutter_container_add_actor(CLUTTER_CONTAINER (self), actor); st_container_move_before (ST_CONTAINER (self), actor, sibling); } Cinnamon-2.8.6/src/st/st-adjustment.c0000664000175000017500000004141112625666430016413 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-adjustment.c: Adjustment object * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-adjustment * @short_description: A GObject representing an adjustable bounded value * * The #StAdjustment object represents a range of values bounded between a * minimum and maximum, together with step and page increments and a page size. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "st-adjustment.h" #include "st-marshal.h" #include "st-private.h" G_DEFINE_TYPE (StAdjustment, st_adjustment, G_TYPE_OBJECT) #define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_ADJUSTMENT, StAdjustmentPrivate)) struct _StAdjustmentPrivate { /* Do not sanity-check values while constructing, * not all properties may be set yet. */ gboolean is_constructing : 1; gdouble lower; gdouble upper; gdouble value; gdouble step_increment; gdouble page_increment; gdouble page_size; }; enum { PROP_0, PROP_LOWER, PROP_UPPER, PROP_VALUE, PROP_STEP_INC, PROP_PAGE_INC, PROP_PAGE_SIZE, }; enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static gboolean st_adjustment_set_lower (StAdjustment *adjustment, gdouble lower); static gboolean st_adjustment_set_upper (StAdjustment *adjustment, gdouble upper); static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment, gdouble step); static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment, gdouble page); static gboolean st_adjustment_set_page_size (StAdjustment *adjustment, gdouble size); static void st_adjustment_constructed (GObject *object) { GObjectClass *g_class; StAdjustment *self = ST_ADJUSTMENT (object); g_class = G_OBJECT_CLASS (st_adjustment_parent_class); /* The docs say we're suppose to chain up, but would crash without * some extra care. */ if (g_class && g_class->constructed && g_class->constructed != st_adjustment_constructed) { g_class->constructed (object); } ST_ADJUSTMENT (self)->priv->is_constructing = FALSE; st_adjustment_clamp_page (self, self->priv->lower, self->priv->upper); } static void st_adjustment_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StAdjustmentPrivate *priv = ST_ADJUSTMENT (gobject)->priv; switch (prop_id) { case PROP_LOWER: g_value_set_double (value, priv->lower); break; case PROP_UPPER: g_value_set_double (value, priv->upper); break; case PROP_VALUE: g_value_set_double (value, priv->value); break; case PROP_STEP_INC: g_value_set_double (value, priv->step_increment); break; case PROP_PAGE_INC: g_value_set_double (value, priv->page_increment); break; case PROP_PAGE_SIZE: g_value_set_double (value, priv->page_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_adjustment_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StAdjustment *adj = ST_ADJUSTMENT (gobject); switch (prop_id) { case PROP_LOWER: st_adjustment_set_lower (adj, g_value_get_double (value)); break; case PROP_UPPER: st_adjustment_set_upper (adj, g_value_get_double (value)); break; case PROP_VALUE: st_adjustment_set_value (adj, g_value_get_double (value)); break; case PROP_STEP_INC: st_adjustment_set_step_increment (adj, g_value_get_double (value)); break; case PROP_PAGE_INC: st_adjustment_set_page_increment (adj, g_value_get_double (value)); break; case PROP_PAGE_SIZE: st_adjustment_set_page_size (adj, g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_adjustment_class_init (StAdjustmentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (StAdjustmentPrivate)); object_class->constructed = st_adjustment_constructed; object_class->get_property = st_adjustment_get_property; object_class->set_property = st_adjustment_set_property; g_object_class_install_property (object_class, PROP_LOWER, g_param_spec_double ("lower", "Lower", "Lower bound", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_UPPER, g_param_spec_double ("upper", "Upper", "Upper bound", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_VALUE, g_param_spec_double ("value", "Value", "Current value", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_STEP_INC, g_param_spec_double ("step-increment", "Step Increment", "Step increment", 0.0, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_PAGE_INC, g_param_spec_double ("page-increment", "Page Increment", "Page increment", 0.0, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_PAGE_SIZE, g_param_spec_double ("page-size", "Page Size", "Page size", 0.0, G_MAXDOUBLE, 0.0, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT)); /** * StAdjustment::changed: * * Emitted when any of the adjustment values have changed */ signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StAdjustmentClass, changed), NULL, NULL, _st_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void st_adjustment_init (StAdjustment *self) { self->priv = ADJUSTMENT_PRIVATE (self); self->priv->is_constructing = TRUE; } StAdjustment * st_adjustment_new (gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size) { return g_object_new (ST_TYPE_ADJUSTMENT, "value", value, "lower", lower, "upper", upper, "step-increment", step_increment, "page-increment", page_increment, "page-size", page_size, NULL); } gdouble st_adjustment_get_value (StAdjustment *adjustment) { StAdjustmentPrivate *priv; g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0); priv = adjustment->priv; return priv->value; } void st_adjustment_set_value (StAdjustment *adjustment, gdouble value) { StAdjustmentPrivate *priv; g_return_if_fail (ST_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; /* Defer clamp until after construction. */ if (!priv->is_constructing) { value = CLAMP (value, priv->lower, MAX (priv->lower, priv->upper - priv->page_size)); } if (priv->value != value) { priv->value = value; g_object_notify (G_OBJECT (adjustment), "value"); } } void st_adjustment_clamp_page (StAdjustment *adjustment, gdouble lower, gdouble upper) { StAdjustmentPrivate *priv; gboolean changed; g_return_if_fail (ST_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size); upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper); changed = FALSE; if (priv->value + priv->page_size > upper) { priv->value = upper - priv->page_size; changed = TRUE; } if (priv->value < lower) { priv->value = lower; changed = TRUE; } if (changed) g_object_notify (G_OBJECT (adjustment), "value"); } static gboolean st_adjustment_set_lower (StAdjustment *adjustment, gdouble lower) { StAdjustmentPrivate *priv = adjustment->priv; if (priv->lower != lower) { priv->lower = lower; g_signal_emit (adjustment, signals[CHANGED], 0); g_object_notify (G_OBJECT (adjustment), "lower"); /* Defer clamp until after construction. */ if (!priv->is_constructing) st_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } static gboolean st_adjustment_set_upper (StAdjustment *adjustment, gdouble upper) { StAdjustmentPrivate *priv = adjustment->priv; if (priv->upper != upper) { priv->upper = upper; g_signal_emit (adjustment, signals[CHANGED], 0); g_object_notify (G_OBJECT (adjustment), "upper"); /* Defer clamp until after construction. */ if (!priv->is_constructing) st_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment, gdouble step) { StAdjustmentPrivate *priv = adjustment->priv; if (priv->step_increment != step) { priv->step_increment = step; g_signal_emit (adjustment, signals[CHANGED], 0); g_object_notify (G_OBJECT (adjustment), "step-increment"); return TRUE; } return FALSE; } static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment, gdouble page) { StAdjustmentPrivate *priv = adjustment->priv; if (priv->page_increment != page) { priv->page_increment = page; g_signal_emit (adjustment, signals[CHANGED], 0); g_object_notify (G_OBJECT (adjustment), "page-increment"); return TRUE; } return FALSE; } static gboolean st_adjustment_set_page_size (StAdjustment *adjustment, gdouble size) { StAdjustmentPrivate *priv = adjustment->priv; if (priv->page_size != size) { priv->page_size = size; g_signal_emit (adjustment, signals[CHANGED], 0); g_object_notify (G_OBJECT (adjustment), "page_size"); /* Well explicitely clamp after construction. */ if (!priv->is_constructing) st_adjustment_clamp_page (adjustment, priv->lower, priv->upper); return TRUE; } return FALSE; } void st_adjustment_set_values (StAdjustment *adjustment, gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size) { StAdjustmentPrivate *priv; gboolean emit_changed = FALSE; g_return_if_fail (ST_IS_ADJUSTMENT (adjustment)); g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE); g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE); g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE); priv = adjustment->priv; emit_changed = FALSE; g_object_freeze_notify (G_OBJECT (adjustment)); emit_changed |= st_adjustment_set_lower (adjustment, lower); emit_changed |= st_adjustment_set_upper (adjustment, upper); emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment); emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment); emit_changed |= st_adjustment_set_page_size (adjustment, page_size); if (value != priv->value) { st_adjustment_set_value (adjustment, value); emit_changed = TRUE; } if (emit_changed) g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0); g_object_thaw_notify (G_OBJECT (adjustment)); } /** * st_adjustment_get_values: * @adjustment: an #StAdjustment * @value: (out): the current value * @lower: (out): the lower bound * @upper: (out): the upper bound * @step_increment: (out): the step increment * @page_increment: (out): the page increment * @page_size: (out): the page size * * Gets all of @adjustment's values at once. */ void st_adjustment_get_values (StAdjustment *adjustment, gdouble *value, gdouble *lower, gdouble *upper, gdouble *step_increment, gdouble *page_increment, gdouble *page_size) { StAdjustmentPrivate *priv; g_return_if_fail (ST_IS_ADJUSTMENT (adjustment)); priv = adjustment->priv; if (lower) *lower = priv->lower; if (upper) *upper = priv->upper; if (value) *value = st_adjustment_get_value (adjustment); if (step_increment) *step_increment = priv->step_increment; if (page_increment) *page_increment = priv->page_increment; if (page_size) *page_size = priv->page_size; } Cinnamon-2.8.6/src/st/st-background-effect.h0000664000175000017500000000321512625666430017613 0ustar fabiofabio#ifndef __ST_BACKGROUND_EFFECT_H__ #define __ST_BACKGROUND_EFFECT_H__ #include G_BEGIN_DECLS #define ST_TYPE_BACKGROUND_EFFECT (st_background_effect_get_type ()) #define ST_BACKGROUND_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BACKGROUND_EFFECT, StBackgroundEffect)) #define ST_IS_BACKGROUND_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BACKGROUND_EFFECT)) typedef struct _StBackgroundEffect StBackgroundEffect; typedef struct _StBackgroundEffectClass StBackgroundEffectClass; /* object */ struct _StBackgroundEffect { ClutterOffscreenEffect parent_instance; ClutterActor *actor; CoglHandle bg_texture; CoglHandle bg_sub_texture; CoglHandle bg_bumpmap; gchar* bumpmap_location; gint pixel_step_uniform0; gint pixel_step_uniform1; gint pixel_step_uniform2; gint BumpTex_uniform; gint bump_step_uniform; gint bg_posx_i; gint bg_posy_i; gint bg_width_i; gint bg_height_i; gint fg_width_i; gint fg_height_i; gint bumptex_width_i; gint bumptex_height_i; gfloat posx_old; gfloat posy_old; gfloat width_old; gfloat height_old; CoglPipeline *pipeline0; CoglPipeline *pipeline1; CoglPipeline *pipeline2; CoglPipeline *pipeline3; CoglPipeline *pipeline4; glong old_time; guint8 opacity; }; /* class */ struct _StBackgroundEffectClass { ClutterOffscreenEffectClass parent_class; CoglPipeline *base_pipeline; }; GType st_background_effect_get_type (void) G_GNUC_CONST; ClutterEffect *st_background_effect_new (void); G_END_DECLS #endif /* __ST_BACKGROUND_EFFECT_H__ */ Cinnamon-2.8.6/src/st/st-table-child.h0000664000175000017500000001237412625666430016420 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-table-child.h: Table child implementation * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_TABLE_CHILD_H__ #define __ST_TABLE_CHILD_H__ #include #include #include #include G_BEGIN_DECLS #define ST_TYPE_TABLE_CHILD (st_table_child_get_type ()) #define ST_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE_CHILD, StTableChild)) #define ST_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE_CHILD)) #define ST_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE_CHILD, StTableChildClass)) #define ST_IS_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE_CHILD)) #define ST_TABLE_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE_CHILD, StTableChildClass)) typedef struct _StTableChild StTableChild; typedef struct _StTableChildClass StTableChildClass; /** * StTableChild: * * The contents of the this structure are private and should only be accessed * through the public API. */ struct _StTableChild { /*< private >*/ ClutterChildMeta parent_instance; gint col; gint row; gint col_span; gint row_span; StAlign x_align; StAlign y_align; guint allocate_hidden : 1; guint x_expand : 1; guint y_expand : 1; guint x_fill : 1; guint y_fill : 1; }; struct _StTableChildClass { ClutterChildMetaClass parent_class; }; GType st_table_child_get_type (void) G_GNUC_CONST; gint st_table_child_get_col_span (StTable *table, ClutterActor *child); void st_table_child_set_col_span (StTable *table, ClutterActor *child, gint span); gint st_table_child_get_row_span (StTable *table, ClutterActor *child); void st_table_child_set_row_span (StTable *table, ClutterActor *child, gint span); gboolean st_table_child_get_x_fill (StTable *table, ClutterActor *child); void st_table_child_set_x_fill (StTable *table, ClutterActor *child, gboolean fill); gboolean st_table_child_get_y_fill (StTable *table, ClutterActor *child); void st_table_child_set_y_fill (StTable *table, ClutterActor *child, gboolean fill); gboolean st_table_child_get_x_expand (StTable *table, ClutterActor *child); void st_table_child_set_x_expand (StTable *table, ClutterActor *child, gboolean expand); gboolean st_table_child_get_y_expand (StTable *table, ClutterActor *child); void st_table_child_set_y_expand (StTable *table, ClutterActor *child, gboolean expand); StAlign st_table_child_get_x_align (StTable *table, ClutterActor *child); void st_table_child_set_x_align (StTable *table, ClutterActor *child, StAlign align); StAlign st_table_child_get_y_align (StTable *table, ClutterActor *child); void st_table_child_set_y_align (StTable *table, ClutterActor *child, StAlign align); void st_table_child_set_allocate_hidden (StTable *table, ClutterActor *child, gboolean value); gboolean st_table_child_get_allocate_hidden (StTable *table, ClutterActor *child); G_END_DECLS #endif /* __ST_TABLE_H__ */ Cinnamon-2.8.6/src/st/st-theme.c0000664000175000017500000010440012625666430015335 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme.c: A set of CSS stylesheets used for rule matching * * Copyright 2003-2004 Dodji Seketeli * Copyright 2008, 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * This file started as a cut-and-paste of cr-sel-eng.c from libcroco. * * In moving it to hippo-canvas: * - Reformatted and otherwise edited to match our coding style * - Switched from handling xmlNode to handling HippoStyle * - Simplified by removing things that we don't need or that don't * make sense in our context. * - The code to get a list of matching properties works quite differently; * we order things in priority order, but we don't actually try to * coalesce properties with the same name. * * In moving it to Cinnamon: * - Renamed again to StTheme * - Reformatted to match the cinnamon coding style * - Removed notion of "theme engine" from hippo-canvas * - pseudo-class matching changed from link enum to strings * - Some code simplification */ #include #include #include #include "st-theme-node.h" #include "st-theme-private.h" static GObject *st_theme_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void st_theme_finalize (GObject *object); static void st_theme_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void st_theme_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); struct _StTheme { GObject parent; char *application_stylesheet; char *default_stylesheet; char *theme_stylesheet; char *fallback_stylesheet; GSList *custom_stylesheets; GHashTable *stylesheets_by_filename; GHashTable *filenames_by_stylesheet; CRCascade *cascade; CRStyleSheet *fallback_cr_stylesheet; }; struct _StThemeClass { GObjectClass parent_class; }; enum { PROP_0, PROP_APPLICATION_STYLESHEET, PROP_THEME_STYLESHEET, PROP_DEFAULT_STYLESHEET, PROP_FALLBACK_STYLESHEET }; enum { STYLESHEETS_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (StTheme, st_theme, G_TYPE_OBJECT) /* Quick strcmp. Test only for == 0 or != 0, not < 0 or > 0. */ #define strqcmp(str,lit,lit_len) \ (strlen (str) != (lit_len) || memcmp (str, lit, lit_len)) static void st_theme_init (StTheme *theme) { theme->stylesheets_by_filename = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)cr_stylesheet_unref); theme->filenames_by_stylesheet = g_hash_table_new (g_direct_hash, g_direct_equal); } static void st_theme_class_init (StThemeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = st_theme_constructor; object_class->finalize = st_theme_finalize; object_class->set_property = st_theme_set_property; object_class->get_property = st_theme_get_property; /** * StTheme:application-stylesheet: * * The highest priority stylesheet, representing application-specific * styling; this is associated with the CSS "author" stylesheet. */ g_object_class_install_property (object_class, PROP_APPLICATION_STYLESHEET, g_param_spec_string ("application-stylesheet", "Application Stylesheet", "Stylesheet with application-specific styling", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); /** * StTheme:theme-stylesheet: * * The second priority stylesheet, representing theme-specific styling; * this is associated with the CSS "user" stylesheet. */ g_object_class_install_property (object_class, PROP_THEME_STYLESHEET, g_param_spec_string ("theme-stylesheet", "Theme Stylesheet", "Stylesheet with theme-specific styling", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); /** * StTheme:default-stylesheet: * * The lowest priority stylesheet, representing global default * styling; this is associated with the CSS "user agent" stylesheet. */ g_object_class_install_property (object_class, PROP_DEFAULT_STYLESHEET, g_param_spec_string ("default-stylesheet", "Default Stylesheet", "Stylesheet with global default styling", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); /** * StTheme:fallback-stylesheet: * * Fallback stylesheet - non-cascading. It is applied only if the user-selected stylesheets * fail to return any properties, and the StWidget has its "important" property set. */ g_object_class_install_property (object_class, PROP_FALLBACK_STYLESHEET, g_param_spec_string ("fallback-stylesheet", "Fallback Stylesheet", "Fallback stylesheet for important system widgets.", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); signals[STYLESHEETS_CHANGED] = g_signal_new ("custom-stylesheets-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, /* no default handler slot */ NULL, NULL, NULL, G_TYPE_NONE, 0); } static CRStyleSheet * parse_stylesheet (const char *filename, GError **error) { enum CRStatus status; CRStyleSheet *stylesheet; if (filename == NULL) return NULL; status = cr_om_parser_simply_parse_file ((const guchar *) filename, CR_UTF_8, &stylesheet); if (status != CR_OK) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error parsing stylesheet '%s'; errcode:%d", filename, status); return NULL; } return stylesheet; } CRDeclaration * _st_theme_parse_declaration_list (const char *str) { return cr_declaration_parse_list_from_buf ((const guchar *)str, CR_UTF_8); } /* Just g_warning for now until we have something nicer to do */ static CRStyleSheet * parse_stylesheet_nofail (const char *filename) { GError *error = NULL; CRStyleSheet *result; result = parse_stylesheet (filename, &error); if (error) { g_warning ("%s", error->message); g_clear_error (&error); } return result; } static void insert_stylesheet (StTheme *theme, const char *filename, CRStyleSheet *stylesheet) { char *filename_copy; if (stylesheet == NULL) return; filename_copy = g_strdup(filename); cr_stylesheet_ref (stylesheet); g_hash_table_insert (theme->stylesheets_by_filename, filename_copy, stylesheet); g_hash_table_insert (theme->filenames_by_stylesheet, stylesheet, filename_copy); } gboolean st_theme_load_stylesheet (StTheme *theme, const char *path, GError **error) { CRStyleSheet *stylesheet; stylesheet = parse_stylesheet_nofail (path); if (!stylesheet) return FALSE; insert_stylesheet (theme, path, stylesheet); cr_stylesheet_ref (stylesheet); theme->custom_stylesheets = g_slist_prepend (theme->custom_stylesheets, stylesheet); g_signal_emit (theme, signals[STYLESHEETS_CHANGED], 0); return TRUE; } void st_theme_unload_stylesheet (StTheme *theme, const char *path) { CRStyleSheet *stylesheet; stylesheet = g_hash_table_lookup (theme->stylesheets_by_filename, path); if (!stylesheet) return; if (!g_slist_find (theme->custom_stylesheets, stylesheet)) return; theme->custom_stylesheets = g_slist_remove (theme->custom_stylesheets, stylesheet); g_hash_table_remove (theme->stylesheets_by_filename, path); g_hash_table_remove (theme->filenames_by_stylesheet, stylesheet); cr_stylesheet_unref (stylesheet); g_signal_emit (theme, signals[STYLESHEETS_CHANGED], 0); } /** * st_theme_get_custom_stylesheets: * @theme: an #StTheme * * Returns: (transfer full) (element-type utf8): the list of stylesheet filenames * that were loaded with st_theme_load_stylesheet() */ GSList* st_theme_get_custom_stylesheets (StTheme *theme) { GSList *result = NULL; GSList *iter; for (iter = theme->custom_stylesheets; iter; iter = iter->next) { CRStyleSheet *stylesheet = iter->data; gchar *filename = g_hash_table_lookup (theme->filenames_by_stylesheet, stylesheet); result = g_slist_prepend (result, g_strdup (filename)); } return result; } static GObject * st_theme_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; StTheme *theme; CRStyleSheet *application_stylesheet; CRStyleSheet *theme_stylesheet; CRStyleSheet *default_stylesheet; object = (*G_OBJECT_CLASS (st_theme_parent_class)->constructor) (type, n_construct_properties, construct_properties); theme = ST_THEME (object); application_stylesheet = parse_stylesheet_nofail (theme->application_stylesheet); theme_stylesheet = parse_stylesheet_nofail (theme->theme_stylesheet); default_stylesheet = parse_stylesheet_nofail (theme->default_stylesheet); theme->fallback_cr_stylesheet = parse_stylesheet_nofail (theme->fallback_stylesheet); theme->cascade = cr_cascade_new (application_stylesheet, theme_stylesheet, default_stylesheet); if (theme->cascade == NULL) g_error ("Out of memory when creating cascade object"); insert_stylesheet (theme, theme->application_stylesheet, application_stylesheet); insert_stylesheet (theme, theme->theme_stylesheet, theme_stylesheet); insert_stylesheet (theme, theme->default_stylesheet, default_stylesheet); return object; } static void st_theme_finalize (GObject * object) { StTheme *theme = ST_THEME (object); g_slist_foreach (theme->custom_stylesheets, (GFunc) cr_stylesheet_unref, NULL); g_slist_free (theme->custom_stylesheets); theme->custom_stylesheets = NULL; g_hash_table_destroy (theme->stylesheets_by_filename); g_hash_table_destroy (theme->filenames_by_stylesheet); g_free (theme->application_stylesheet); g_free (theme->theme_stylesheet); g_free (theme->default_stylesheet); g_free (theme->fallback_stylesheet); if (theme->cascade) { cr_cascade_unref (theme->cascade); theme->cascade = NULL; } G_OBJECT_CLASS (st_theme_parent_class)->finalize (object); } static void st_theme_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { StTheme *theme = ST_THEME (object); switch (prop_id) { case PROP_APPLICATION_STYLESHEET: { const char *path = g_value_get_string (value); if (path != theme->application_stylesheet) { g_free (theme->application_stylesheet); theme->application_stylesheet = g_strdup (path); } break; } case PROP_THEME_STYLESHEET: { const char *path = g_value_get_string (value); if (path != theme->theme_stylesheet) { g_free (theme->theme_stylesheet); theme->theme_stylesheet = g_strdup (path); } break; } case PROP_DEFAULT_STYLESHEET: { const char *path = g_value_get_string (value); if (path != theme->default_stylesheet) { g_free (theme->default_stylesheet); theme->default_stylesheet = g_strdup (path); } break; } case PROP_FALLBACK_STYLESHEET: { const char *path = g_value_get_string (value); if (path != theme->fallback_stylesheet) { g_free (theme->fallback_stylesheet); theme->fallback_stylesheet = g_strdup (path); } break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_theme_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { StTheme *theme = ST_THEME (object); switch (prop_id) { case PROP_APPLICATION_STYLESHEET: g_value_set_string (value, theme->application_stylesheet); break; case PROP_THEME_STYLESHEET: g_value_set_string (value, theme->theme_stylesheet); break; case PROP_DEFAULT_STYLESHEET: g_value_set_string (value, theme->default_stylesheet); break; case PROP_FALLBACK_STYLESHEET: g_value_set_string (value, theme->fallback_stylesheet); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * st_theme_new: * @application_stylesheet: The highest priority stylesheet, representing application-specific * styling; this is associated with the CSS "author" stylesheet, may be %NULL * @theme_stylesheet: The second priority stylesheet, representing theme-specific styling ; * this is associated with the CSS "user" stylesheet, may be %NULL * @default_stylesheet: The lowest priority stylesheet, representing global default styling; * this is associated with the CSS "user agent" stylesheet, may be %NULL * * Return value: the newly created theme object **/ StTheme * st_theme_new (const char *application_stylesheet, const char *theme_stylesheet, const char *default_stylesheet) { StTheme *theme = g_object_new (ST_TYPE_THEME, "application-stylesheet", application_stylesheet, "theme-stylesheet", theme_stylesheet, "default-stylesheet", default_stylesheet, NULL); return theme; } static gboolean string_in_list (GString *stryng, GStrv list) { gchar **it; if (list == NULL) return FALSE; for (it = list; *it != NULL; it++) { if (!strqcmp (*it, stryng->str, stryng->len)) return TRUE; } return FALSE; } static gboolean pseudo_class_add_sel_matches_style (StTheme *a_this, CRAdditionalSel *a_add_sel, StThemeNode *a_node) { GStrv node_pseudo_classes; g_return_val_if_fail (a_this && a_add_sel && a_add_sel->content.pseudo && a_add_sel->content.pseudo->name && a_add_sel->content.pseudo->name->stryng && a_add_sel->content.pseudo->name->stryng->str && a_node, FALSE); node_pseudo_classes = st_theme_node_get_pseudo_classes (a_node); return string_in_list (a_add_sel->content.pseudo->name->stryng, node_pseudo_classes); } /* *@param a_add_sel the class additional selector to consider. *@param a_node the style node to consider. *@return TRUE if the class additional selector matches *the style node given in argument, FALSE otherwise. */ static gboolean class_add_sel_matches_style (CRAdditionalSel *a_add_sel, StThemeNode *a_node) { GStrv element_classes; g_return_val_if_fail (a_add_sel && a_add_sel->type == CLASS_ADD_SELECTOR && a_add_sel->content.class_name && a_add_sel->content.class_name->stryng && a_add_sel->content.class_name->stryng->str && a_node, FALSE); element_classes = st_theme_node_get_element_classes (a_node); return string_in_list (a_add_sel->content.class_name->stryng, element_classes); } /* *@return TRUE if the additional attribute selector matches *the current style node given in argument, FALSE otherwise. *@param a_add_sel the additional attribute selector to consider. *@param a_node the style node to consider. */ static gboolean id_add_sel_matches_style (CRAdditionalSel *a_add_sel, StThemeNode *a_node) { gboolean result = FALSE; const char *id; g_return_val_if_fail (a_add_sel && a_add_sel->type == ID_ADD_SELECTOR && a_add_sel->content.id_name && a_add_sel->content.id_name->stryng && a_add_sel->content.id_name->stryng->str && a_node, FALSE); g_return_val_if_fail (a_add_sel && a_add_sel->type == ID_ADD_SELECTOR && a_node, FALSE); id = st_theme_node_get_element_id (a_node); if (id != NULL) { if (!strqcmp (id, a_add_sel->content.id_name->stryng->str, a_add_sel->content.id_name->stryng->len)) { result = TRUE; } } return result; } /** *additional_selector_matches_style: *Evaluates if a given additional selector matches an style node. *@param a_add_sel the additional selector to consider. *@param a_node the style node to consider. *@return TRUE is a_add_sel matches a_node, FALSE otherwise. */ static gboolean additional_selector_matches_style (StTheme *a_this, CRAdditionalSel *a_add_sel, StThemeNode *a_node) { CRAdditionalSel *cur_add_sel = NULL; g_return_val_if_fail (a_add_sel, FALSE); for (cur_add_sel = a_add_sel; cur_add_sel; cur_add_sel = cur_add_sel->next) { switch (cur_add_sel->type) { case NO_ADD_SELECTOR: return FALSE; case CLASS_ADD_SELECTOR: if (!class_add_sel_matches_style (cur_add_sel, a_node)) return FALSE; break; case ID_ADD_SELECTOR: if (!id_add_sel_matches_style (cur_add_sel, a_node)) return FALSE; break; case ATTRIBUTE_ADD_SELECTOR: g_warning ("Attribute selectors not supported"); return FALSE; case PSEUDO_CLASS_ADD_SELECTOR: if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node)) return FALSE; break; } } return TRUE; } static gboolean element_name_matches_type (const char *element_name, GType element_type) { if (element_type == G_TYPE_NONE) { return strcmp (element_name, "stage") == 0; } else { GType match_type = g_type_from_name (element_name); if (match_type == G_TYPE_INVALID) return FALSE; return g_type_is_a (element_type, match_type); } } /* *Evaluate a selector (a simple selectors list) and says *if it matches the style node given in parameter. *The algorithm used here is the following: *Walk the combinator separated list of simple selectors backward, starting *from the end of the list. For each simple selector, looks if *if matches the current style. * *@param a_this the selection engine. *@param a_sel the simple selection list. *@param a_node the style node. *@param a_result out parameter. Set to true if the *selector matches the style node, FALSE otherwise. *@param a_recurse if set to TRUE, the function will walk to *the next simple selector (after the evaluation of the current one) *and recursively evaluate it. Must be usually set to TRUE unless you *know what you are doing. */ static enum CRStatus sel_matches_style_real (StTheme *a_this, CRSimpleSel *a_sel, StThemeNode *a_node, gboolean *a_result, gboolean a_eval_sel_list_from_end, gboolean a_recurse) { CRSimpleSel *cur_sel = NULL; StThemeNode *cur_node = NULL; GType cur_type; *a_result = FALSE; if (a_eval_sel_list_from_end) { /*go and get the last simple selector of the list */ for (cur_sel = a_sel; cur_sel && cur_sel->next; cur_sel = cur_sel->next) ; } else { cur_sel = a_sel; } cur_node = a_node; cur_type = st_theme_node_get_element_type (cur_node); while (cur_sel) { if (((cur_sel->type_mask & TYPE_SELECTOR) && (cur_sel->name && cur_sel->name->stryng && cur_sel->name->stryng->str) && (element_name_matches_type (cur_sel->name->stryng->str, cur_type))) || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) { /* *this simple selector *matches the current style node *Let's see if the preceding *simple selectors also match *their style node counterpart. */ if (cur_sel->add_sel) { if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node)) goto walk_a_step_in_expr; else goto done; } else goto walk_a_step_in_expr; } if (!(cur_sel->type_mask & TYPE_SELECTOR) && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) { if (!cur_sel->add_sel) goto done; if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node)) goto walk_a_step_in_expr; else goto done; } else { goto done; } walk_a_step_in_expr: if (a_recurse == FALSE) { *a_result = TRUE; goto done; } /* *here, depending on the combinator of cur_sel *choose the axis of the element tree traversal *and walk one step in the element tree. */ if (!cur_sel->prev) break; switch (cur_sel->combinator) { case NO_COMBINATOR: break; case COMB_WS: /*descendant selector */ { StThemeNode *n = NULL; /* *walk the element tree upward looking for a parent *style that matches the preceding selector. */ for (n = st_theme_node_get_parent (a_node); n; n = st_theme_node_get_parent (n)) { enum CRStatus status; gboolean matches = FALSE; status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE); if (status != CR_OK) goto done; if (matches) { cur_node = n; cur_type = st_theme_node_get_element_type (cur_node); break; } } if (!n) { /* *didn't find any ancestor that matches *the previous simple selector. */ goto done; } /* *in this case, the preceding simple sel *will have been interpreted twice, which *is a cpu and mem waste ... I need to find *another way to do this. Anyway, this is *my first attempt to write this function and *I am a bit clueless. */ break; } case COMB_PLUS: g_warning ("+ combinators are not supported"); goto done; case COMB_GT: cur_node = st_theme_node_get_parent (cur_node); if (!cur_node) goto done; cur_type = st_theme_node_get_element_type (cur_node); break; default: goto done; } cur_sel = cur_sel->prev; } /* *if we reached this point, it means the selector matches *the style node. */ *a_result = TRUE; done: return CR_OK; } static void add_matched_properties (StTheme *a_this, CRStyleSheet *a_nodesheet, StThemeNode *a_node, GPtrArray *props) { CRStatement *cur_stmt = NULL; CRSelector *sel_list = NULL; CRSelector *cur_sel = NULL; gboolean matches = FALSE; enum CRStatus status = CR_OK; /* *walk through the list of statements and, *get the selectors list inside the statements that *contain some, and try to match our style node in these *selectors lists. */ for (cur_stmt = a_nodesheet->statements; cur_stmt; cur_stmt = cur_stmt->next) { /* *initialyze the selector list in which we will *really perform the search. */ sel_list = NULL; /* *get the the damn selector list in *which we have to look */ switch (cur_stmt->type) { case RULESET_STMT: if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list) { sel_list = cur_stmt->kind.ruleset->sel_list; } break; case AT_MEDIA_RULE_STMT: if (cur_stmt->kind.media_rule && cur_stmt->kind.media_rule->rulesets && cur_stmt->kind.media_rule->rulesets->kind.ruleset && cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list) { sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list; } break; case AT_IMPORT_RULE_STMT: { CRAtImportRule *import_rule = cur_stmt->kind.import_rule; if (import_rule->sheet == NULL) { char *filename = NULL; if (import_rule->url->stryng && import_rule->url->stryng->str) filename = _st_theme_resolve_url (a_this, a_nodesheet, import_rule->url->stryng->str); if (filename) import_rule->sheet = parse_stylesheet (filename, NULL); if (import_rule->sheet) { insert_stylesheet (a_this, filename, import_rule->sheet); /* refcount of stylesheets starts off at zero, so we don't need to unref! */ } else { /* Set a marker to avoid repeatedly trying to parse a non-existent or * broken stylesheet */ import_rule->sheet = (CRStyleSheet *) - 1; } if (filename) g_free (filename); } if (import_rule->sheet != (CRStyleSheet *) - 1) { add_matched_properties (a_this, import_rule->sheet, a_node, props); } } break; default: break; } if (!sel_list) continue; /* *now, we have a comma separated selector list to look in. *let's walk it and try to match the style node *on each item of the list. */ for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) { if (!cur_sel->simple_sel) continue; status = sel_matches_style_real (a_this, cur_sel->simple_sel, a_node, &matches, TRUE, TRUE); if (status == CR_OK && matches) { CRDeclaration *cur_decl = NULL; /* In order to sort the matching properties, we need to compute the * specificity of the selector that actually matched this * element. In a non-thread-safe fashion, we store it in the * ruleset. (Fixing this would mean cut-and-pasting * cr_simple_sel_compute_specificity(), and have no need for * thread-safety anyways.) * * Once we've sorted the properties, the specificity no longer * matters and it can be safely overriden. */ cr_simple_sel_compute_specificity (cur_sel->simple_sel); cur_stmt->specificity = cur_sel->simple_sel->specificity; for (cur_decl = cur_stmt->kind.ruleset->decl_list; cur_decl; cur_decl = cur_decl->next) g_ptr_array_add (props, cur_decl); } } } } #define ORIGIN_AUTHOR_IMPORTANT (ORIGIN_AUTHOR + 1) #define ORIGIN_USER_IMPORTANT (ORIGIN_AUTHOR + 2) static inline int get_origin (const CRDeclaration * decl) { enum CRStyleOrigin origin = decl->parent_statement->parent_sheet->origin; if (decl->important) { if (origin == ORIGIN_AUTHOR) return ORIGIN_AUTHOR_IMPORTANT; else if (origin == ORIGIN_USER) return ORIGIN_USER_IMPORTANT; } return origin; } /* Order of comparison is so that higher priority statements compare after * lower priority statements */ static int compare_declarations (gconstpointer a, gconstpointer b) { /* g_ptr_array_sort() is broooken */ CRDeclaration *decl_a = *(CRDeclaration **) a; CRDeclaration *decl_b = *(CRDeclaration **) b; int origin_a = get_origin (decl_a); int origin_b = get_origin (decl_b); if (origin_a != origin_b) return origin_a - origin_b; if (decl_a->parent_statement->specificity != decl_b->parent_statement->specificity) return decl_a->parent_statement->specificity - decl_b->parent_statement->specificity; return 0; } GPtrArray * _st_theme_get_matched_properties (StTheme *theme, StThemeNode *node) { enum CRStyleOrigin origin = 0; CRStyleSheet *sheet = NULL; GSList *iter; g_return_val_if_fail (ST_IS_THEME (theme), NULL); g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); GPtrArray *props = g_ptr_array_new (); for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) { sheet = cr_cascade_get_sheet (theme->cascade, origin); if (!sheet) continue; add_matched_properties (theme, sheet, node, props); } for (iter = theme->custom_stylesheets; iter; iter = iter->next) add_matched_properties (theme, iter->data, node, props); /* We count on a stable sort here so that later declarations come * after earlier declarations */ g_ptr_array_sort (props, compare_declarations); return props; } GPtrArray * _st_theme_get_matched_properties_fallback (StTheme *theme, StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME (theme), NULL); g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); GPtrArray *props = g_ptr_array_new (); if (theme->fallback_cr_stylesheet) add_matched_properties (theme, theme->fallback_cr_stylesheet, node, props); /* We count on a stable sort here so that later declarations come * after earlier declarations */ g_ptr_array_sort (props, compare_declarations); return props; } /* Resolve an url from an url() reference in a stylesheet into an absolute * local filename, if possible. The resolution here is distinctly lame and * will fail on many examples. */ char * _st_theme_resolve_url (StTheme *theme, CRStyleSheet *base_stylesheet, const char *url) { const char *base_filename = NULL; char *dirname; char *filename; /* Handle absolute file:/ URLs */ if (g_str_has_prefix (url, "file:") || g_str_has_prefix (url, "File:") || g_str_has_prefix (url, "FILE:")) { GError *error = NULL; char *filename; filename = g_filename_from_uri (url, NULL, &error); if (filename == NULL) { g_warning ("%s", error->message); g_error_free (error); } else { g_free (filename); } return NULL; } /* Guard against http:/ URLs */ if (g_str_has_prefix (url, "http:") || g_str_has_prefix (url, "Http:") || g_str_has_prefix (url, "HTTP:")) { g_warning ("Http URL '%s' in theme stylesheet is not supported", url); return NULL; } /* Assume anything else is a relative URL, and "resolve" it */ if (url[0] == '/') return g_strdup (url); base_filename = g_hash_table_lookup (theme->filenames_by_stylesheet, base_stylesheet); if (base_filename == NULL) { g_warning ("Can't get base to resolve url '%s'", url); return NULL; } dirname = g_path_get_dirname (base_filename); filename = g_build_filename (dirname, url, NULL); g_free (dirname); return filename; } Cinnamon-2.8.6/src/st/st-marshal.list0000664000175000017500000000033512625666430016415 0ustar fabiofabioVOID:OBJECT VOID:VOID VOID:PARAM VOID:POINTER VOID:INT VOID:UINT VOID:UINT,UINT VOID:OBJECT,OBJECT VOID:STRING,OBJECT VOID:OBJECT,OBJECT,INT,INT VOID:OBJECT,FLOAT,FLOAT,INT,ENUM VOID:FLOAT,FLOAT,INT,ENUM VOID:FLOAT,FLOAT Cinnamon-2.8.6/src/st/st-bin.h0000664000175000017500000000563312625666430015020 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-bin.h: Basic container actor * * Copyright 2009, 2008 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_BIN_H__ #define __ST_BIN_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_BIN (st_bin_get_type ()) #define ST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BIN, StBin)) #define ST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BIN)) #define ST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BIN, StBinClass)) #define ST_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BIN)) #define ST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BIN, StBinClass)) typedef struct _StBin StBin; typedef struct _StBinPrivate StBinPrivate; typedef struct _StBinClass StBinClass; /** * StBin: * * The #StBin struct contains only private data */ struct _StBin { /*< private >*/ StWidget parent_instance; StBinPrivate *priv; }; /** * StBinClass: * * The #StBinClass struct contains only private data */ struct _StBinClass { /*< private >*/ StWidgetClass parent_class; }; GType st_bin_get_type (void) G_GNUC_CONST; StWidget * st_bin_new (void); void st_bin_set_child (StBin *bin, ClutterActor *child); ClutterActor *st_bin_get_child (StBin *bin); void st_bin_set_alignment (StBin *bin, StAlign x_align, StAlign y_align); void st_bin_get_alignment (StBin *bin, StAlign *x_align, StAlign *y_align); void st_bin_set_fill (StBin *bin, gboolean x_fill, gboolean y_fill); void st_bin_get_fill (StBin *bin, gboolean *x_fill, gboolean *y_fill); G_END_DECLS #endif /* __ST_BIN_H__ */ Cinnamon-2.8.6/src/st/st-entry.c0000664000175000017500000011036012625666430015376 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-entry.c: Plain entry actor * * Copyright 2008, 2009 Intel Corporation * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-entry * @short_description: Widget for displaying text * * #StEntry is a simple widget for displaying text. It derives from * #StWidget to add extra style and placement functionality over * #ClutterText. The internal #ClutterText is publicly accessibly to allow * applications to set further properties. * * #StEntry supports the following pseudo style states: * * * focus: the widget has focus * * * indeterminate: the widget is showing the hint text * * * hover: the widget is showing the hint text and is underneath the * pointer * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "st-entry.h" #include "st-im-text.h" #include "st-icon.h" #include "st-widget.h" #include "st-texture-cache.h" #include "st-marshal.h" #include "st-clipboard.h" #include "st-private.h" #include "st-widget-accessible.h" #define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor) /* properties */ enum { PROP_0, PROP_CLUTTER_TEXT, PROP_HINT_TEXT, PROP_TEXT, }; /* signals */ enum { PRIMARY_ICON_CLICKED, SECONDARY_ICON_CLICKED, LAST_SIGNAL }; #define ST_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_ENTRY, StEntryPrivate)) #define ST_ENTRY_PRIV(x) ((StEntry *) x)->priv static void st_entry_check_cursor_blink (StEntry *entry); static void st_entry_pend_cursor_blink (StEntry *entry); static void st_entry_reset_blink_time (StEntry *entry); struct _StEntryPrivate { ClutterActor *entry; gchar *hint; ClutterActor *primary_icon; ClutterActor *secondary_icon; gfloat spacing; gboolean hint_visible; gboolean capslock_warning_shown; guint blink_time; guint blink_timeout; gboolean cursor_visible; }; static guint entry_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (StEntry, st_entry, ST_TYPE_WIDGET); static GType st_entry_accessible_get_type (void) G_GNUC_CONST; static void st_entry_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StEntry *entry = ST_ENTRY (gobject); switch (prop_id) { case PROP_HINT_TEXT: st_entry_set_hint_text (entry, g_value_get_string (value)); break; case PROP_TEXT: st_entry_set_text (entry, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_entry_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StEntryPrivate *priv = ST_ENTRY_PRIV (gobject); switch (prop_id) { case PROP_CLUTTER_TEXT: g_value_set_object (value, priv->entry); break; case PROP_HINT_TEXT: g_value_set_string (value, priv->hint); break; case PROP_TEXT: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void show_capslock_feedback (StEntry *entry) { if (entry->priv->secondary_icon == NULL) { ClutterActor *icon = g_object_new (ST_TYPE_ICON, "style-class", "capslock-warning", "icon-type", ST_ICON_SYMBOLIC, "icon-name", "dialog-warning", NULL); st_entry_set_secondary_icon (entry, icon); entry->priv->capslock_warning_shown = TRUE; } } static void remove_capslock_feedback (StEntry *entry) { if (entry->priv->capslock_warning_shown) { st_entry_set_secondary_icon (entry, NULL); entry->priv->capslock_warning_shown = FALSE; } } static void keymap_state_changed (GdkKeymap *keymap, gpointer user_data) { StEntry *entry = ST_ENTRY (user_data); if (clutter_text_get_password_char (CLUTTER_TEXT (entry->priv->entry)) != 0) { if (gdk_keymap_get_caps_lock_state (keymap)) show_capslock_feedback (entry); else remove_capslock_feedback (entry); } } static void st_entry_dispose (GObject *object) { StEntry *entry = ST_ENTRY (object); StEntryPrivate *priv = entry->priv; GdkKeymap *keymap; if (priv->blink_timeout) { g_source_remove (priv->blink_timeout); priv->blink_timeout = 0; } if (priv->entry) { clutter_actor_destroy (priv->entry); priv->entry = NULL; } keymap = gdk_keymap_get_for_display (gdk_display_get_default ()); g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry); G_OBJECT_CLASS (st_entry_parent_class)->dispose (object); } static void st_entry_finalize (GObject *object) { StEntryPrivate *priv = ST_ENTRY_PRIV (object); g_free (priv->hint); priv->hint = NULL; G_OBJECT_CLASS (st_entry_parent_class)->finalize (object); } static void st_entry_style_changed (StWidget *self) { StEntryPrivate *priv = ST_ENTRY_PRIV (self); StThemeNode *theme_node; ClutterColor color; gdouble size; theme_node = st_widget_get_theme_node (self); if (st_theme_node_lookup_length (theme_node, "caret-size", TRUE, &size)) clutter_text_set_cursor_size (CLUTTER_TEXT (priv->entry), (int)(.5 + size)); if (st_theme_node_lookup_color (theme_node, "caret-color", TRUE, &color)) clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color); if (st_theme_node_lookup_color (theme_node, "selection-background-color", TRUE, &color)) clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color); if (st_theme_node_lookup_color (theme_node, "selected-color", TRUE, &color)) clutter_text_set_selected_text_color (CLUTTER_TEXT (priv->entry), &color); _st_set_text_from_style ((ClutterText *)priv->entry, theme_node); ST_WIDGET_CLASS (st_entry_parent_class)->style_changed (self); } static gboolean st_entry_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction) { StEntryPrivate *priv = ST_ENTRY_PRIV (widget); /* This is basically the same as st_widget_real_navigate_focus(), * except that widget is behaving as a proxy for priv->entry (which * isn't an StWidget and so has no can-focus flag of its own). */ if (from == priv->entry) return FALSE; else if (st_widget_get_can_focus (widget)) { clutter_actor_grab_key_focus (priv->entry); return TRUE; } else return FALSE; } static void st_entry_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gfloat icon_w; st_theme_node_adjust_for_height (theme_node, &for_height); clutter_actor_get_preferred_width (priv->entry, for_height, min_width_p, natural_width_p); if (priv->primary_icon) { clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); if (min_width_p) *min_width_p += icon_w + priv->spacing; if (natural_width_p) *natural_width_p += icon_w + priv->spacing; } if (priv->secondary_icon) { clutter_actor_get_preferred_width (priv->secondary_icon, -1, NULL, &icon_w); if (min_width_p) *min_width_p += icon_w + priv->spacing; if (natural_width_p) *natural_width_p += icon_w + priv->spacing; } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_entry_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gfloat icon_h; st_theme_node_adjust_for_width (theme_node, &for_width); clutter_actor_get_preferred_height (priv->entry, for_width, min_height_p, natural_height_p); if (priv->primary_icon) { clutter_actor_get_preferred_height (priv->primary_icon, -1, NULL, &icon_h); if (min_height_p && icon_h > *min_height_p) *min_height_p = icon_h; if (natural_height_p && icon_h > *natural_height_p) *natural_height_p = icon_h; } if (priv->secondary_icon) { clutter_actor_get_preferred_height (priv->secondary_icon, -1, NULL, &icon_h); if (min_height_p && icon_h > *min_height_p) *min_height_p = icon_h; if (natural_height_p && icon_h > *natural_height_p) *natural_height_p = icon_h; } st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_entry_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorClass *parent_class; ClutterActorBox content_box, child_box, icon_box; gfloat icon_w, icon_h; gfloat entry_h, min_h, pref_h, avail_h; parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class); parent_class->allocate (actor, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); avail_h = content_box.y2 - content_box.y1; child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; if (priv->primary_icon) { clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->primary_icon, -1, NULL, &icon_h); icon_box.x1 = content_box.x1; icon_box.x2 = icon_box.x1 + icon_w; icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->primary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x1 += icon_w + priv->spacing; } if (priv->secondary_icon) { clutter_actor_get_preferred_width (priv->secondary_icon, -1, NULL, &icon_w); clutter_actor_get_preferred_height (priv->secondary_icon, -1, NULL, &icon_h); icon_box.x2 = content_box.x2; icon_box.x1 = icon_box.x2 - icon_w; icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2); icon_box.y2 = icon_box.y1 + icon_h; clutter_actor_allocate (priv->secondary_icon, &icon_box, flags); /* reduce the size for the entry */ child_box.x2 -= icon_w - priv->spacing; } clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1, &min_h, &pref_h); entry_h = CLAMP (pref_h, min_h, avail_h); child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2); child_box.y2 = child_box.y1 + entry_h; clutter_actor_allocate (priv->entry, &child_box, flags); } #define CURSOR_ON_MULTIPLIER 2 #define CURSOR_OFF_MULTIPLIER 1 #define CURSOR_PEND_MULTIPLIER 3 #define CURSOR_DIVIDER 3 static gboolean cursor_blinks (StEntry *entry) { StEntryPrivate *priv = entry->priv; if (clutter_actor_has_key_focus (CLUTTER_ACTOR (priv->entry)) && clutter_text_get_editable (CLUTTER_TEXT (priv->entry)) && clutter_text_get_selection_bound (CLUTTER_TEXT (priv->entry)) == clutter_text_get_cursor_position (CLUTTER_TEXT (priv->entry))) { gboolean blink; g_object_get (gtk_settings_get_default (), "gtk-cursor-blink", &blink, NULL); return blink; } else return FALSE; } static gint get_cursor_time (StEntry *entry) { gint time; g_object_get (gtk_settings_get_default (), "gtk-cursor-blink-time", &time, NULL); return time; } static gint get_cursor_blink_timeout (StEntry *entry) { gint timeout; g_object_get (gtk_settings_get_default (), "gtk-cursor-blink-timeout", &timeout, NULL); return timeout; } static void show_cursor (StEntry *entry) { StEntryPrivate *priv = entry->priv; if (!priv->cursor_visible) { priv->cursor_visible = TRUE; clutter_text_set_cursor_visible (CLUTTER_TEXT (priv->entry), TRUE); } } static void hide_cursor (StEntry *entry) { StEntryPrivate *priv = entry->priv; if (priv->cursor_visible) { priv->cursor_visible = FALSE; clutter_text_set_cursor_visible (CLUTTER_TEXT (priv->entry), FALSE); } } /* * Blink! */ static gint blink_cb (gpointer data) { StEntry *entry; StEntryPrivate *priv; gint blink_timeout; entry = ST_ENTRY (data); priv = entry->priv; if (!clutter_actor_has_key_focus (priv->entry)) { g_warning ("StEntry - did not receive key-focus-out event. If you\n" "connect a handler to this signal, it must return\n" "FALSE so the StEntry gets the event as well"); st_entry_check_cursor_blink (entry); return FALSE; } if (clutter_text_get_selection_bound (CLUTTER_TEXT (priv->entry)) != clutter_text_get_cursor_position (CLUTTER_TEXT (priv->entry))) { st_entry_check_cursor_blink (entry); return FALSE; } blink_timeout = get_cursor_blink_timeout (entry); if (priv->blink_time > 1000 * blink_timeout && blink_timeout < G_MAXINT/1000) { /* we've blinked enough without the user doing anything, stop blinking */ show_cursor (entry); priv->blink_timeout = 0; } else if (priv->cursor_visible) { hide_cursor (entry); priv->blink_timeout = clutter_threads_add_timeout (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER, blink_cb, entry); } else { show_cursor (entry); priv->blink_time += get_cursor_time (entry); priv->blink_timeout = clutter_threads_add_timeout (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER, blink_cb, entry); } /* Remove ourselves */ return FALSE; } static void st_entry_check_cursor_blink (StEntry *entry) { StEntryPrivate *priv = entry->priv; if (cursor_blinks (entry)) { if (!priv->blink_timeout) { show_cursor (entry); priv->blink_timeout = clutter_threads_add_timeout (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER, blink_cb, entry); } } else { if (priv->blink_timeout) { g_source_remove (priv->blink_timeout); priv->blink_timeout = 0; } show_cursor (entry); } } static void st_entry_pend_cursor_blink (StEntry *entry) { StEntryPrivate *priv = entry->priv; if (cursor_blinks (entry)) { if (priv->blink_timeout != 0) g_source_remove (priv->blink_timeout); priv->blink_timeout = clutter_threads_add_timeout (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER, blink_cb, entry); show_cursor (entry); } } static void st_entry_reset_blink_time (StEntry *entry) { StEntryPrivate *priv = entry->priv; priv->blink_time = 0; } static void clutter_text_focus_in_cb (ClutterText *text, ClutterActor *actor) { StEntry *entry = ST_ENTRY (actor); StEntryPrivate *priv = entry->priv; GdkKeymap *keymap; /* remove the hint if visible */ if (priv->hint && priv->hint_visible) { priv->hint_visible = FALSE; clutter_text_set_text (text, ""); } keymap = gdk_keymap_get_for_display (gdk_display_get_default ()); keymap_state_changed (keymap, entry); g_signal_connect (keymap, "state-changed", G_CALLBACK (keymap_state_changed), entry); st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "indeterminate"); st_widget_add_style_pseudo_class (ST_WIDGET (actor), "focus"); st_entry_reset_blink_time (entry); st_entry_check_cursor_blink (entry); } static void clutter_text_focus_out_cb (ClutterText *text, ClutterActor *actor) { StEntry *entry = ST_ENTRY (actor); StEntryPrivate *priv = entry->priv; GdkKeymap *keymap; st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "focus"); /* add a hint if the entry is empty */ if (priv->hint && !strcmp (clutter_text_get_text (text), "")) { priv->hint_visible = TRUE; clutter_text_set_text (text, priv->hint); st_widget_add_style_pseudo_class (ST_WIDGET (actor), "indeterminate"); } st_entry_check_cursor_blink (entry); remove_capslock_feedback (entry); keymap = gdk_keymap_get_for_display (gdk_display_get_default ()); g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry); } static void clutter_text_password_char_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { StEntry *entry = ST_ENTRY (user_data); if (clutter_text_get_password_char (CLUTTER_TEXT (entry->priv->entry)) == 0) remove_capslock_feedback (entry); } static void clutter_text_selection_bound_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { StEntry *entry = ST_ENTRY (user_data); st_entry_reset_blink_time (entry); st_entry_pend_cursor_blink (entry); } static void clutter_text_cursor_changed (ClutterText *text, ClutterActor *actor) { st_entry_reset_blink_time (ST_ENTRY (actor)); st_entry_pend_cursor_blink (ST_ENTRY (actor)); } static void st_entry_paint (ClutterActor *actor) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); ClutterActorClass *parent_class; parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class); parent_class->paint (actor); clutter_actor_paint (priv->entry); if (priv->primary_icon) clutter_actor_paint (priv->primary_icon); if (priv->secondary_icon) clutter_actor_paint (priv->secondary_icon); } static void st_entry_pick (ClutterActor *actor, const ClutterColor *c) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); CLUTTER_ACTOR_CLASS (st_entry_parent_class)->pick (actor, c); clutter_actor_paint (priv->entry); if (priv->primary_icon) clutter_actor_paint (priv->primary_icon); if (priv->secondary_icon) clutter_actor_paint (priv->secondary_icon); } static void st_entry_clipboard_callback (StClipboard *clipboard, const gchar *text, gpointer data) { ClutterText *ctext = (ClutterText*)((StEntry *) data)->priv->entry; gint cursor_pos; if (!text) return; /* delete the current selection before pasting */ clutter_text_delete_selection (ctext); /* "paste" the clipboard text into the entry */ cursor_pos = clutter_text_get_cursor_position (ctext); clutter_text_insert_text (ctext, text, cursor_pos); } static gboolean st_entry_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); st_entry_reset_blink_time (ST_ENTRY (actor)); st_entry_pend_cursor_blink (ST_ENTRY (actor)); /* This is expected to handle events that were emitted for the inner ClutterText. They only reach this function if the ClutterText didn't handle them */ /* paste */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_v) { StClipboard *clipboard; clipboard = st_clipboard_get_default (); st_clipboard_get_text (clipboard, st_entry_clipboard_callback, actor); return TRUE; } /* copy */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_c) { StClipboard *clipboard; gchar *text; clipboard = st_clipboard_get_default (); text = clutter_text_get_selection ((ClutterText*) priv->entry); if (text && strlen (text)) st_clipboard_set_text (clipboard, text); return TRUE; } /* cut */ if ((event->modifier_state & CLUTTER_CONTROL_MASK) && event->keyval == CLUTTER_x) { StClipboard *clipboard; gchar *text; clipboard = st_clipboard_get_default (); text = clutter_text_get_selection ((ClutterText*) priv->entry); if (text && strlen (text)) { st_clipboard_set_text (clipboard, text); /* now delete the text */ clutter_text_delete_selection ((ClutterText *) priv->entry); } return TRUE; } return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->key_press_event (actor, event); } static void st_entry_key_focus_in (ClutterActor *actor) { StEntryPrivate *priv = ST_ENTRY_PRIV (actor); /* We never want key focus. The ClutterText should be given first pass for all key events */ clutter_actor_grab_key_focus (priv->entry); } static void st_entry_class_init (StEntryClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StEntryPrivate)); gobject_class->set_property = st_entry_set_property; gobject_class->get_property = st_entry_get_property; gobject_class->finalize = st_entry_finalize; gobject_class->dispose = st_entry_dispose; actor_class->get_preferred_width = st_entry_get_preferred_width; actor_class->get_preferred_height = st_entry_get_preferred_height; actor_class->allocate = st_entry_allocate; actor_class->paint = st_entry_paint; actor_class->pick = st_entry_pick; actor_class->key_press_event = st_entry_key_press_event; actor_class->key_focus_in = st_entry_key_focus_in; widget_class->style_changed = st_entry_style_changed; widget_class->navigate_focus = st_entry_navigate_focus; widget_class->get_accessible_type = st_entry_accessible_get_type; pspec = g_param_spec_object ("clutter-text", "Clutter Text", "Internal ClutterText actor", CLUTTER_TYPE_TEXT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec); pspec = g_param_spec_string ("hint-text", "Hint Text", "Text to display when the entry is not focused " "and the text property is empty", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec); pspec = g_param_spec_string ("text", "Text", "Text of the entry", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); /* signals */ /** * StEntry::primary-icon-clicked: * * Emitted when the primary icon is clicked */ entry_signals[PRIMARY_ICON_CLICKED] = g_signal_new ("primary-icon-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StEntryClass, primary_icon_clicked), NULL, NULL, _st_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * StEntry::secondary-icon-clicked: * * Emitted when the secondary icon is clicked */ entry_signals[SECONDARY_ICON_CLICKED] = g_signal_new ("secondary-icon-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StEntryClass, secondary_icon_clicked), NULL, NULL, _st_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void st_entry_init (StEntry *entry) { StEntryPrivate *priv; priv = entry->priv = ST_ENTRY_GET_PRIVATE (entry); priv->entry = g_object_new (ST_TYPE_IM_TEXT, "line-alignment", PANGO_ALIGN_LEFT, "editable", TRUE, "reactive", TRUE, "single-line-mode", TRUE, NULL); g_signal_connect (priv->entry, "key-focus-in", G_CALLBACK (clutter_text_focus_in_cb), entry); g_signal_connect (priv->entry, "key-focus-out", G_CALLBACK (clutter_text_focus_out_cb), entry); g_signal_connect (priv->entry, "notify::password-char", G_CALLBACK (clutter_text_password_char_cb), entry); g_signal_connect (priv->entry, "notify::selection-bound", G_CALLBACK (clutter_text_selection_bound_cb), entry); g_signal_connect (priv->entry, "cursor-changed", G_CALLBACK (clutter_text_cursor_changed), entry); priv->spacing = 6.0f; clutter_actor_add_child (CLUTTER_ACTOR (entry), priv->entry); clutter_actor_set_reactive ((ClutterActor *) entry, TRUE); /* set cursor hidden until we receive focus */ priv->blink_timeout = 0; priv->blink_time = 0; priv->cursor_visible = FALSE; clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE); } /** * st_entry_new: * @text: text to set the entry to * * Create a new #StEntry with the specified entry * * Returns: a new #StEntry */ StWidget * st_entry_new (const gchar *text) { StWidget *entry; /* add the entry to the stage, but don't allow it to be visible */ entry = g_object_new (ST_TYPE_ENTRY, "text", text, NULL); return entry; } /** * st_entry_get_text: * @entry: a #StEntry * * Get the text displayed on the entry * * Returns: the text for the entry. This must not be freed by the application */ const gchar * st_entry_get_text (StEntry *entry) { g_return_val_if_fail (ST_IS_ENTRY (entry), NULL); if (entry->priv->hint_visible) return ""; else return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry)); } /** * st_entry_set_text: * @entry: a #StEntry * @text: (allow-none): text to set the entry to * * Sets the text displayed on the entry */ void st_entry_set_text (StEntry *entry, const gchar *text) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; /* set a hint if we are blanking the entry */ if (priv->hint && text && !strcmp ("", text) && !HAS_FOCUS (priv->entry)) { text = priv->hint; priv->hint_visible = TRUE; st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate"); } else { st_widget_remove_style_pseudo_class (ST_WIDGET (entry), "indeterminate"); priv->hint_visible = FALSE; } clutter_text_set_text (CLUTTER_TEXT (priv->entry), text); g_object_notify (G_OBJECT (entry), "text"); } /** * st_entry_get_clutter_text: * @entry: a #StEntry * * Retrieve the internal #ClutterText so that extra parameters can be set * * Returns: (transfer none): the #ClutterText used by #StEntry. The entry is * owned by the #StEntry and should not be unref'ed by the application. */ ClutterActor* st_entry_get_clutter_text (StEntry *entry) { g_return_val_if_fail (ST_ENTRY (entry), NULL); return entry->priv->entry; } /** * st_entry_set_hint_text: * @entry: a #StEntry * @text: (allow-none): text to set as the entry hint * * Sets the text to display when the entry is empty and unfocused. When the * entry is displaying the hint, it has a pseudo class of "indeterminate". * A value of NULL unsets the hint. */ void st_entry_set_hint_text (StEntry *entry, const gchar *text) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; g_free (priv->hint); priv->hint = g_strdup (text); if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "") && !HAS_FOCUS (priv->entry)) { priv->hint_visible = TRUE; clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint); st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate"); } } /** * st_entry_get_hint_text: * @entry: a #StEntry * * Gets the text that is displayed when the entry is empty and unfocused * * Returns: the current value of the hint property. This string is owned by the * #StEntry and should not be freed or modified. */ const gchar * st_entry_get_hint_text (StEntry *entry) { g_return_val_if_fail (ST_IS_ENTRY (entry), NULL); return entry->priv->hint; } static gboolean _st_entry_icon_press_cb (ClutterActor *actor, ClutterButtonEvent *event, StEntry *entry) { StEntryPrivate *priv = entry->priv; if (actor == priv->primary_icon) g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0); else g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0); return FALSE; } static void _st_entry_set_icon (StEntry *entry, ClutterActor **icon, ClutterActor *new_icon) { if (*icon) { g_signal_handlers_disconnect_by_func (*icon, _st_entry_icon_press_cb, entry); clutter_actor_unparent (*icon); *icon = NULL; } if (new_icon) { *icon = g_object_ref (new_icon); clutter_actor_set_reactive (*icon, TRUE); clutter_actor_add_child (CLUTTER_ACTOR (entry), *icon); g_signal_connect (*icon, "button-release-event", G_CALLBACK (_st_entry_icon_press_cb), entry); } clutter_actor_queue_relayout (CLUTTER_ACTOR (entry)); } static void _st_entry_set_icon_from_file (StEntry *entry, ClutterActor **icon, const gchar *filename) { ClutterActor *new_icon = NULL; if (filename) { StTextureCache *cache; GFile *file; gchar *uri; cache = st_texture_cache_get_default (); file = g_file_new_for_path (filename); uri = g_file_get_uri (file); g_object_unref (file); new_icon = (ClutterActor*) st_texture_cache_load_uri_async (cache, uri, -1, -1); g_free (uri); } _st_entry_set_icon (entry, icon, new_icon); } /** * st_entry_set_primary_icon: * @entry: a #StEntry * @icon: (allow-none): a #ClutterActor * * Set the primary icon of the entry to @icon */ void st_entry_set_primary_icon (StEntry *entry, ClutterActor *icon) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; _st_entry_set_icon (entry, &priv->primary_icon, icon); } /** * st_entry_set_secondary_icon: * @entry: a #StEntry * @icon: (allow-none): an #ClutterActor * * Set the secondary icon of the entry to @icon */ void st_entry_set_secondary_icon (StEntry *entry, ClutterActor *icon) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; _st_entry_set_icon (entry, &priv->secondary_icon, icon); } /** * st_entry_set_primary_icon_from_file: * @entry: a #StEntry * @filename: (allow-none): filename of an icon * * Set the primary icon of the entry to the given filename */ void st_entry_set_primary_icon_from_file (StEntry *entry, const gchar *filename) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; _st_entry_set_icon_from_file (entry, &priv->primary_icon, filename); } /** * st_entry_set_secondary_icon_from_file: * @entry: a #StEntry * @filename: (allow-none): filename of an icon * * Set the primary icon of the entry to the given filename */ void st_entry_set_secondary_icon_from_file (StEntry *entry, const gchar *filename) { StEntryPrivate *priv; g_return_if_fail (ST_IS_ENTRY (entry)); priv = entry->priv; _st_entry_set_icon_from_file (entry, &priv->secondary_icon, filename); } /******************************************************************************/ /*************************** ACCESSIBILITY SUPPORT ****************************/ /******************************************************************************/ #define ST_TYPE_ENTRY_ACCESSIBLE (st_entry_accessible_get_type ()) #define ST_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessible)) #define ST_IS_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_ENTRY_ACCESSIBLE)) #define ST_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass)) #define ST_IS_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), ST_TYPE_ENTRY_ACCESSIBLE)) #define ST_ENTRY_ACCESSIBLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass)) typedef struct _StEntryAccessible StEntryAccessible; typedef struct _StEntryAccessibleClass StEntryAccessibleClass; struct _StEntryAccessible { StWidgetAccessible parent; }; struct _StEntryAccessibleClass { StWidgetAccessibleClass parent_class; }; G_DEFINE_TYPE (StEntryAccessible, st_entry_accessible, ST_TYPE_WIDGET_ACCESSIBLE) static void st_entry_accessible_init (StEntryAccessible *self) { /* initialization done on AtkObject->initialize */ } static void st_entry_accessible_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (st_entry_accessible_parent_class)->initialize (obj, data); /* StEntry is behaving as a StImText container */ atk_object_set_role (obj, ATK_ROLE_PANEL); } static gint st_entry_accessible_get_n_children (AtkObject *obj) { StEntry *entry = NULL; g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), 0); entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (entry == NULL) return 0; if (entry->priv->entry == NULL) return 0; else return 1; } static AtkObject* st_entry_accessible_ref_child (AtkObject *obj, gint i) { StEntry *entry = NULL; AtkObject *result = NULL; g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), NULL); g_return_val_if_fail (i == 0, NULL); entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (entry == NULL) return NULL; if (entry->priv->entry == NULL) return NULL; result = clutter_actor_get_accessible (entry->priv->entry); g_object_ref (result); return result; } static void st_entry_accessible_class_init (StEntryAccessibleClass *klass) { AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = st_entry_accessible_initialize; atk_class->get_n_children = st_entry_accessible_get_n_children; atk_class->ref_child= st_entry_accessible_ref_child; } Cinnamon-2.8.6/src/st/st-overflow-box.c0000664000175000017500000003460012625666430016670 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-overflow-box.c: A vertical box which paints as many actors as it can fit * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-overflow-box * @short_description: A vertical box which paints as many actors as it can fit * * This is a "flexible" box which will paint as many actors as it can within * its given allocation; its minimum height request will be the sum of the * mimimum size for the #StOverflowBox:min-children property, which is * by default 0. * * Every child will be allocated the full width of the box, and always be * given its preferred height. Even if not actually painted, every child * is counted for overall preferred width/height. */ #include #include "st-overflow-box.h" #include "st-private.h" #include "st-box-layout-child.h" G_DEFINE_TYPE (StOverflowBox, st_overflow_box, ST_TYPE_CONTAINER) #define OVERFLOW_BOX_LAYOUT_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_OVERFLOW_BOX, StOverflowBoxPrivate)) enum { PROP_0, PROP_MIN_CHILDREN }; struct _StOverflowBoxPrivate { guint min_children; guint n_visible; guint spacing; }; static void st_overflow_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (object)->priv; switch (property_id) { case PROP_MIN_CHILDREN: g_value_set_uint (value, priv->min_children); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_overflow_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StOverflowBox *box = ST_OVERFLOW_BOX (object); switch (property_id) { case PROP_MIN_CHILDREN: st_overflow_box_set_min_children (box, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void get_content_preferred_width (StOverflowBox *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StOverflowBoxPrivate *priv = self->priv; gint n_children = 0; gint n_fixed = 0; gfloat min_width, natural_width; GList *l, *children; min_width = 0; natural_width = 0; children = st_container_get_children_list (ST_CONTAINER (self)); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = l->data; gfloat child_min = 0, child_nat = 0; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; n_children++; if (clutter_actor_get_fixed_position_set (child)) { n_fixed++; continue; } clutter_actor_get_preferred_width (child, -1, &child_min, &child_nat); min_width = MAX (child_min, min_width); natural_width = MAX (child_nat, natural_width); } if ((n_children - n_fixed) > 1) { min_width += priv->spacing * (n_children - n_fixed - 1); natural_width += priv->spacing * (n_children - n_fixed - 1); } if (min_width_p) *min_width_p = min_width; if (natural_width_p) *natural_width_p = natural_width; } static void st_overflow_box_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_height (theme_node, &for_height); get_content_preferred_width (ST_OVERFLOW_BOX (actor), for_height, min_width_p, natural_width_p); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void get_content_preferred_height (StOverflowBox *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StOverflowBoxPrivate *priv = self->priv; gint n_min_children = 0; gint n_children = 0; gint n_fixed = 0; gfloat min_height, natural_height; GList *l, *children; min_height = 0; natural_height = 0; children = st_container_get_children_list (ST_CONTAINER (self)); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = l->data; gfloat child_min = 0, child_nat = 0; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; n_children++; if (clutter_actor_get_fixed_position_set (child)) { n_fixed++; continue; } clutter_actor_get_preferred_height (child, for_width, &child_min, &child_nat); if (n_children < priv->min_children) { n_min_children++; min_height += child_min; } natural_height += child_nat; } min_height += priv->spacing * MAX(0, n_min_children - 1); natural_height += priv->spacing * MAX(0, n_children - n_fixed - 1); if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; } static void st_overflow_box_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_width (theme_node, &for_width); get_content_preferred_height (ST_OVERFLOW_BOX (actor), for_width, min_height_p, natural_height_p); st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_overflow_box_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorBox content_box; gfloat position; float avail_width; GList *l, *children; int i; gboolean done_non_fixed; CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->allocate (actor, box, flags); children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; st_theme_node_get_content_box (theme_node, box, &content_box); avail_width = content_box.x2 - content_box.x1; position = content_box.y1; priv->n_visible = 0; done_non_fixed = FALSE; for (l = children, i = 0; l; l = l->next, i++) { ClutterActor *child = (ClutterActor*) l->data; ClutterActorBox child_box; gfloat child_min, child_nat; gboolean fixed; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; fixed = clutter_actor_get_fixed_position_set (child); if (fixed) { clutter_actor_allocate_preferred_size (child, flags); continue; } else if (done_non_fixed) continue; clutter_actor_get_preferred_height (child, avail_width, &child_min, &child_nat); if (position + child_nat > content_box.y2) { done_non_fixed = TRUE; /* Continue iterating on non fixed */ continue; } priv->n_visible++; child_box.y1 = (int)(0.5 + position); child_box.y2 = child_box.y1 + (int)(0.5 + child_nat); child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; position += child_nat + priv->spacing; clutter_actor_allocate (child, &child_box, flags); } } static void visible_children_iter_init (StOverflowBox *box, GList **iter, int *n) { *iter = st_container_get_children_list (ST_CONTAINER (box)); *n = 0; } static ClutterActor * visible_children_iter (StOverflowBox *box, GList **iter, int *n) { StOverflowBoxPrivate *priv = box->priv; GList *l; for (l = *iter; *n < priv->n_visible && l; l = l->next) { ClutterActor *child = (ClutterActor*) l->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (!clutter_actor_get_fixed_position_set (child)) (*n)++; *iter = l->next; return child; } for (; l; l = l->next) { ClutterActor *child = (ClutterActor*) l->data; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; if (clutter_actor_get_fixed_position_set (child)) { *iter = l->next; return child; } } return NULL; } static void st_overflow_box_internal_paint (StOverflowBox *box) { ClutterActor *child; GList *children; int n; visible_children_iter_init (box, &children, &n); while ((child = visible_children_iter (box, &children, &n))) clutter_actor_paint (child); } static void st_overflow_box_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->paint (actor); st_overflow_box_internal_paint (ST_OVERFLOW_BOX (actor)); } static void st_overflow_box_pick (ClutterActor *actor, const ClutterColor *color) { CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->pick (actor, color); st_overflow_box_internal_paint (ST_OVERFLOW_BOX (actor)); } static void st_overflow_box_style_changed (StWidget *self) { StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (self); int old_spacing = priv->spacing; double spacing; spacing = st_theme_node_get_length (theme_node, "spacing"); priv->spacing = (int)(spacing + 0.5); if (priv->spacing != old_spacing) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); ST_WIDGET_CLASS (st_overflow_box_parent_class)->style_changed (self); } static GList * st_overflow_box_get_focus_chain (StContainer *container) { StOverflowBox *box = ST_OVERFLOW_BOX (container); ClutterActor *child; GList *children, *focus_chain; int n; focus_chain = NULL; visible_children_iter_init (box, &children, &n); while ((child = visible_children_iter (box, &children, &n))) focus_chain = g_list_prepend (focus_chain, child); return g_list_reverse (focus_chain); } static void st_overflow_box_class_init (StOverflowBoxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); StContainerClass *container_class = ST_CONTAINER_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StOverflowBoxPrivate)); object_class->get_property = st_overflow_box_get_property; object_class->set_property = st_overflow_box_set_property; actor_class->allocate = st_overflow_box_allocate; actor_class->get_preferred_width = st_overflow_box_get_preferred_width; actor_class->get_preferred_height = st_overflow_box_get_preferred_height; actor_class->paint = st_overflow_box_paint; actor_class->pick = st_overflow_box_pick; widget_class->style_changed = st_overflow_box_style_changed; container_class->get_focus_chain = st_overflow_box_get_focus_chain; pspec = g_param_spec_uint ("min-children", "Min Children", "The actor will request a minimum size large enough to include this many children", 0, G_MAXUINT, 0, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MIN_CHILDREN, pspec); } static void st_overflow_box_init (StOverflowBox *self) { self->priv = OVERFLOW_BOX_LAYOUT_PRIVATE (self); } /** * st_overflow_box_get_min_children: * @box: A #StOverflowBox * * Get the value of the #StOverflowBox::pack-start property. * * Returns: %TRUE if pack-start is enabled */ gboolean st_overflow_box_get_min_children (StOverflowBox *box) { g_return_val_if_fail (ST_IS_OVERFLOW_BOX (box), FALSE); return box->priv->min_children; } /** * st_box_layout_set_min_children: * @box: A #StOverflowBox * @min_children: Minimum children value * * Set the minimum number of children to be visible. */ void st_overflow_box_set_min_children (StOverflowBox *box, guint min_children) { g_return_if_fail (ST_IS_OVERFLOW_BOX (box)); if (box->priv->min_children != min_children) { box->priv->min_children = min_children; clutter_actor_queue_relayout ((ClutterActor*) box); g_object_notify (G_OBJECT (box), "min-children"); } } /** * st_overflow_box_get_n_children: * @box: a #StOverflowBox * * Returns the number of children in this box. */ guint st_overflow_box_get_n_children (StOverflowBox *self) { GList *children = st_container_get_children_list (ST_CONTAINER (self)); return g_list_length (children); } /** * st_overflow_box_get_n_visible: * @box: a #StOverflowBox * * Returns the number of children we will paint. Only valid * after the actor has been allocated. */ guint st_overflow_box_get_n_visible (StOverflowBox *self) { return self->priv->n_visible; } Cinnamon-2.8.6/src/st/st-theme-context.c0000664000175000017500000002761112625666430017027 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-context.c: holds global information about a tree of styled objects * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include "st-texture-cache.h" #include "st-theme.h" #include "st-theme-context.h" struct _StThemeContext { GObject parent; PangoFontDescription *font; StThemeNode *root_node; StTheme *theme; /* set of StThemeNode */ GHashTable *nodes; gint scale_factor; }; struct _StThemeContextClass { GObjectClass parent_class; }; #define DEFAULT_FONT "sans-serif 10" enum { PROP_0, PROP_SCALE_FACTOR }; enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT); static void on_icon_theme_changed (StTextureCache *cache, StThemeContext *context); static void st_theme_context_changed (StThemeContext *context); static void st_theme_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void st_theme_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void st_theme_context_finalize (GObject *object) { StThemeContext *context = ST_THEME_CONTEXT (object); g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (), (gpointer) on_icon_theme_changed, context); g_signal_handlers_disconnect_by_func (clutter_get_default_backend (), (gpointer) st_theme_context_changed, context); if (context->nodes) g_hash_table_unref (context->nodes); if (context->root_node) g_object_unref (context->root_node); if (context->theme) g_object_unref (context->theme); pango_font_description_free (context->font); G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object); } static void st_theme_context_class_init (StThemeContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = st_theme_context_set_property; object_class->get_property = st_theme_context_get_property; object_class->finalize = st_theme_context_finalize; /** * StThemeContext:scale-factor: * * The scaling factor used or high dpi scaling. */ g_object_class_install_property (object_class, PROP_SCALE_FACTOR, g_param_spec_int ("scale-factor", "Scale factor", "Integer scale factor used for high dpi scaling", 0, G_MAXINT, 1, G_PARAM_READABLE | G_PARAM_WRITABLE)); signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, /* no default handler slot */ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void st_theme_context_init (StThemeContext *context) { context->font = pango_font_description_from_string (DEFAULT_FONT); g_signal_connect (st_texture_cache_get_default (), "icon-theme-changed", G_CALLBACK (on_icon_theme_changed), context); g_signal_connect_swapped (clutter_get_default_backend (), "resolution-changed", G_CALLBACK (st_theme_context_changed), context); context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash, (GEqualFunc) st_theme_node_equal, g_object_unref, NULL); context->scale_factor = 1; } static void st_theme_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { StThemeContext *context = ST_THEME_CONTEXT (object); switch (prop_id) { case PROP_SCALE_FACTOR: { int scale_factor = g_value_get_int (value); if (scale_factor != context->scale_factor) { context->scale_factor = scale_factor; st_theme_context_changed (context); } break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_theme_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { StThemeContext *context = ST_THEME_CONTEXT (object); switch (prop_id) { case PROP_SCALE_FACTOR: g_value_set_int (value, context->scale_factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * st_theme_context_new: * * Create a new theme context not associated with any #ClutterStage. * This can be useful in testing scenarios, or if using StThemeContext * with something other than #ClutterActor objects, but you generally * should use st_theme_context_get_for_stage() instead. */ StThemeContext * st_theme_context_new (void) { StThemeContext *context; context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL); return context; } static void on_stage_destroy (ClutterStage *stage) { StThemeContext *context = st_theme_context_get_for_stage (stage); g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL); g_object_unref (context); } static void st_theme_context_changed (StThemeContext *context) { StThemeNode *old_root = context->root_node; context->root_node = NULL; g_hash_table_remove_all (context->nodes); g_signal_emit (context, signals[CHANGED], 0); if (old_root) g_object_unref (old_root); } static gboolean changed_idle (gpointer userdata) { st_theme_context_changed (userdata); return FALSE; } static void on_icon_theme_changed (StTextureCache *cache, StThemeContext *context) { /* Note that an icon theme change isn't really a change of the StThemeContext; * the style information has changed. But since the style factors into the * icon_name => icon lookup, faking a theme context change is a good way * to force users such as StIcon to look up icons again. Don't bother recreating * the root node, though. */ g_idle_add ((GSourceFunc) changed_idle, context); } /** * st_theme_context_get_for_stage: * @stage: a #ClutterStage * * Gets a singleton theme context associated with the stage. * * Return value: (transfer none): the singleton theme context for the stage */ StThemeContext * st_theme_context_get_for_stage (ClutterStage *stage) { StThemeContext *context; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); context = g_object_get_data (G_OBJECT (stage), "st-theme-context"); if (context) return context; context = st_theme_context_new (); g_object_set_data (G_OBJECT (stage), "st-theme-context", context); g_signal_connect (stage, "destroy", G_CALLBACK (on_stage_destroy), NULL); return context; } /** * st_theme_context_set_theme: * @context: a #StThemeContext * * Sets the default set of theme stylesheets for the context. This theme will * be used for the root node and for nodes descending from it, unless some other * style is explicitely specified. */ void st_theme_context_set_theme (StThemeContext *context, StTheme *theme) { g_return_if_fail (ST_IS_THEME_CONTEXT (context)); g_return_if_fail (theme == NULL || ST_IS_THEME (theme)); if (context->theme != theme) { if (context->theme) g_object_unref (context->theme); context->theme = theme; if (context->theme) g_object_ref (context->theme); st_theme_context_changed (context); } } /** * st_theme_context_get_theme: * @context: a #StThemeContext * * Gets the default theme for the context. See st_theme_context_set_theme() * * Return value: (transfer none): the default theme for the context */ StTheme * st_theme_context_get_theme (StThemeContext *context) { g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL); return context->theme; } /** * st_theme_context_set_font: * @context: a #StThemeContext * @font: the default font for theme context * * Sets the default font for the theme context. This is the font that * is inherited by the root node of the tree of theme nodes. If the * font is not overriden, then this font will be used. If the font is * partially modified (for example, with 'font-size: 110%', then that * modification is based on this font. */ void st_theme_context_set_font (StThemeContext *context, const PangoFontDescription *font) { g_return_if_fail (ST_IS_THEME_CONTEXT (context)); g_return_if_fail (font != NULL); if (context->font == font || pango_font_description_equal (context->font, font)) return; pango_font_description_free (context->font); context->font = pango_font_description_copy (font); st_theme_context_changed (context); } /** * st_theme_context_get_font: * @context: a #StThemeContext * * Gets the default font for the theme context. See st_theme_context_set_font(). * * Return value: the default font for the theme context. */ const PangoFontDescription * st_theme_context_get_font (StThemeContext *context) { g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL); return context->font; } /** * st_theme_context_get_root_node: * @context: a #StThemeContext * * Gets the root node of the tree of theme style nodes that associated with this * context. For the node tree associated with a stage, this node represents * styles applied to the stage itself. * * Return value: (transfer none): the root node of the context's style tree */ StThemeNode * st_theme_context_get_root_node (StThemeContext *context) { if (context->root_node == NULL) context->root_node = st_theme_node_new (context, NULL, context->theme, G_TYPE_NONE, NULL, NULL, NULL, NULL, FALSE); return context->root_node; } /** * st_theme_context_intern_node: * @context: a #StThemeContext * @node: a #StThemeNode * * Return an existing node matching @node, or if that isn't possible, * @node itself. * * Return value: (transfer none): a node with the same properties as @node */ StThemeNode * st_theme_context_intern_node (StThemeContext *context, StThemeNode *node) { StThemeNode *mine = g_hash_table_lookup (context->nodes, node); /* this might be node or not - it doesn't actually matter */ if (mine != NULL) return mine; g_hash_table_add (context->nodes, g_object_ref (node)); return node; }Cinnamon-2.8.6/src/st/st-enum-types.c.in0000664000175000017500000000135312625666430016751 0ustar fabiofabio/*** BEGIN file-header ***/ #include "st-enum-types.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type(void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); } return enum_type_id; } /*** END value-tail ***/ Cinnamon-2.8.6/src/st/st-table-child.c0000664000175000017500000005022012625666430016403 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-table-child.h: Table child implementation * * Copyright 2008, 2009 Intel Corporation. * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "st-private.h" #include "st-table-child.h" #include "st-table-private.h" #include "st-enum-types.h" #include #include /* * ClutterChildMeta Implementation */ /** * SECTION:st-table-child * @short_description: The child property store for #StTable * * The #ClutterChildMeta implementation for the #StTable container widget. * */ enum { CHILD_PROP_0, CHILD_PROP_COL, CHILD_PROP_ROW, CHILD_PROP_COL_SPAN, CHILD_PROP_ROW_SPAN, CHILD_PROP_X_EXPAND, CHILD_PROP_Y_EXPAND, CHILD_PROP_X_ALIGN, CHILD_PROP_Y_ALIGN, CHILD_PROP_X_FILL, CHILD_PROP_Y_FILL, CHILD_PROP_ALLOCATE_HIDDEN, }; G_DEFINE_TYPE (StTableChild, st_table_child, CLUTTER_TYPE_CHILD_META); static void table_child_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StTableChild *child = ST_TABLE_CHILD (gobject); StTable *table = ST_TABLE (CLUTTER_CHILD_META(gobject)->container); switch (prop_id) { case CHILD_PROP_COL: child->col = g_value_get_int (value); _st_table_update_row_col (table, -1, child->col); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW: child->row = g_value_get_int (value); _st_table_update_row_col (table, child->row, -1); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_COL_SPAN: child->col_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ROW_SPAN: child->row_span = g_value_get_int (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_EXPAND: child->x_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_EXPAND: child->y_expand = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_ALIGN: child->x_align = g_value_get_enum (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_X_FILL: child->x_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; case CHILD_PROP_ALLOCATE_HIDDEN: child->allocate_hidden = g_value_get_boolean (value); clutter_actor_queue_relayout (CLUTTER_ACTOR (table)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void table_child_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StTableChild *child = ST_TABLE_CHILD (gobject); switch (prop_id) { case CHILD_PROP_COL: g_value_set_int (value, child->col); break; case CHILD_PROP_ROW: g_value_set_int (value, child->row); break; case CHILD_PROP_COL_SPAN: g_value_set_int (value, child->col_span); break; case CHILD_PROP_ROW_SPAN: g_value_set_int (value, child->row_span); break; case CHILD_PROP_X_EXPAND: g_value_set_boolean (value, child->x_expand); break; case CHILD_PROP_Y_EXPAND: g_value_set_boolean (value, child->y_expand); break; case CHILD_PROP_X_ALIGN: g_value_set_enum (value, child->x_align); break; case CHILD_PROP_Y_ALIGN: g_value_set_enum (value, child->y_align); break; case CHILD_PROP_X_FILL: g_value_set_boolean (value, child->x_fill); break; case CHILD_PROP_Y_FILL: g_value_set_boolean (value, child->y_fill); break; case CHILD_PROP_ALLOCATE_HIDDEN: g_value_set_boolean (value, child->allocate_hidden); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_table_child_class_init (StTableChildClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = table_child_set_property; gobject_class->get_property = table_child_get_property; pspec = g_param_spec_int ("col", "Column Number", "The column the widget resides in", 0, G_MAXINT, 0, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_COL, pspec); pspec = g_param_spec_int ("row", "Row Number", "The row the widget resides in", 0, G_MAXINT, 0, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_ROW, pspec); pspec = g_param_spec_int ("row-span", "Row Span", "The number of rows the widget should span", 1, G_MAXINT, 1, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_ROW_SPAN, pspec); pspec = g_param_spec_int ("col-span", "Column Span", "The number of columns the widget should span", 1, G_MAXINT, 1, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_COL_SPAN, pspec); pspec = g_param_spec_boolean ("x-expand", "X Expand", "Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_EXPAND, pspec); pspec = g_param_spec_boolean ("y-expand", "Y Expand", "Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_EXPAND, pspec); pspec = g_param_spec_enum ("x-align", "X Alignment", "X alignment of the widget within the cell", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", "Y Alignment", "Y alignment of the widget within the cell", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_ALIGN, pspec); pspec = g_param_spec_boolean ("x-fill", "X Fill", "Whether the child should be allocated its " "entire available space, or whether it should " "be squashed and aligned.", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", "Y Fill", "Whether the child should be allocated its " "entire available space, or whether it should " "be squashed and aligned.", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_Y_FILL, pspec); pspec = g_param_spec_boolean ("allocate-hidden", "Allocate Hidden", "Whether the child should be allocate even " "if it is hidden", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, CHILD_PROP_ALLOCATE_HIDDEN, pspec); } static void st_table_child_init (StTableChild *self) { self->col_span = 1; self->row_span = 1; self->x_align = ST_ALIGN_MIDDLE; self->y_align = ST_ALIGN_MIDDLE; self->x_expand = TRUE; self->y_expand = TRUE; self->x_fill = TRUE; self->y_fill = TRUE; self->allocate_hidden = TRUE; } static StTableChild* get_child_meta (StTable *table, ClutterActor *child) { StTableChild *meta; meta = (StTableChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child); return meta; } /** * st_table_child_get_col_span: * @table: an #StTable * @child: a #ClutterActor * * Get the column span of the child. Defaults to 1. * * Returns: the column span of the child */ gint st_table_child_get_col_span (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->col_span; } /** * st_table_child_set_col_span: * @table: An #StTable * @child: An #ClutterActor * @span: The number of columns to span * * Set the column span of the child. * */ void st_table_child_set_col_span (StTable *table, ClutterActor *child, gint span) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (span > 1); meta = get_child_meta (table, child); meta->col_span = span; clutter_actor_queue_relayout (child); } /** * st_table_child_get_row_span: * @table: A #StTable * @child: A #ClutterActor * * Get the row span of the child. Defaults to 1. * * Returns: the row span of the child */ gint st_table_child_get_row_span (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->row_span; } /** * st_table_child_set_row_span: * @table: A #StTable * @child: A #ClutterActor * @span: the number of rows to span * * Set the row span of the child. * */ void st_table_child_set_row_span (StTable *table, ClutterActor *child, gint span) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (span > 1); meta = get_child_meta (table, child); meta->row_span = span; clutter_actor_queue_relayout (child); } /** * st_table_child_get_x_fill: * @table: A #StTable * @child: A #ClutterActor * * Get the x-fill state of the child * * Returns: %TRUE if the child is set to x-fill */ gboolean st_table_child_get_x_fill (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->x_fill; } /** * st_table_child_set_x_fill: * @table: A #StTable * @child: A #ClutterActor * @fill: the fill state * * Set the fill state of the child on the x-axis. This will cause the child to * be allocated the maximum available space. * */ void st_table_child_set_x_fill (StTable *table, ClutterActor *child, gboolean fill) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->x_fill = fill; clutter_actor_queue_relayout (child); } /** * st_table_child_get_y_fill: * @table: A #StTable * @child: A #ClutterActor * * Get the y-fill state of the child * * Returns: %TRUE if the child is set to y-fill */ gboolean st_table_child_get_y_fill (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->y_fill; } /** * st_table_child_set_y_fill: * @table: A #StTable * @child: A #ClutterActor * @fill: the fill state * * Set the fill state of the child on the y-axis. This will cause the child to * be allocated the maximum available space. * */ void st_table_child_set_y_fill (StTable *table, ClutterActor *child, gboolean fill) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->y_fill = fill; clutter_actor_queue_relayout (child); } /** * st_table_child_get_x_expand: * @table: A #StTable * @child: A #ClutterActor * * Get the x-expand property of the child * * Returns: %TRUE if the child is set to x-expand */ gboolean st_table_child_get_x_expand (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->x_expand; } /** * st_table_child_set_x_expand: * @table: A #StTable * @child: A #ClutterActor * @expand: the new value of the x expand child property * * Set x-expand on the child. This causes the column which the child * resides in to be allocated any extra space if the allocation of the table is * larger than the preferred size. * */ void st_table_child_set_x_expand (StTable *table, ClutterActor *child, gboolean expand) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->x_expand = expand; clutter_actor_queue_relayout (child); } /** * st_table_child_set_y_expand: * @table: A #StTable * @child: A #ClutterActor * @expand: the new value of the y-expand child property * * Set y-expand on the child. This causes the row which the child * resides in to be allocated any extra space if the allocation of the table is * larger than the preferred size. * */ void st_table_child_set_y_expand (StTable *table, ClutterActor *child, gboolean expand) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->y_expand = expand; clutter_actor_queue_relayout (child); } /** * st_table_child_get_y_expand: * @table: A #StTable * @child: A #ClutterActor * * Get the y-expand property of the child. * * Returns: %TRUE if the child is set to y-expand */ gboolean st_table_child_get_y_expand (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->y_expand; } /** * st_table_child_get_x_align: * @table: A #StTable * @child: A #ClutterActor * * Get the x-align value of the child * * Returns: An #StAlign value */ StAlign st_table_child_get_x_align (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->x_align; } /** * st_table_child_set_x_align: * @table: A #StTable * @child: A #ClutterActor * @align: A #StAlign value * * Set the alignment of the child within its cell. This will only have an effect * if the the x-fill property is FALSE. * */ void st_table_child_set_x_align (StTable *table, ClutterActor *child, StAlign align) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->x_align = align; clutter_actor_queue_relayout (child); } /** * st_table_child_get_y_align: * @table: A #StTable * @child: A #ClutterActor * * Get the y-align value of the child * * Returns: An #StAlign value */ StAlign st_table_child_get_y_align (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0); meta = get_child_meta (table, child); return meta->y_align; } /** * st_table_child_set_y_align: * @table: A #StTable * @child: A #ClutterActor * @align: A #StAlign value * * Set the value of the y-align property. This will only have an effect if * y-fill value is set to FALSE. * */ void st_table_child_set_y_align (StTable *table, ClutterActor *child, StAlign align) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); meta->y_align = align; clutter_actor_queue_relayout (child); } /** * st_table_child_set_allocate_hidden: * @table: A #StTable * @child: A #ClutterActor * @value: %TRUE if the actor should be allocated when hidden * * Set whether the child should be allocate even if it is hidden */ void st_table_child_set_allocate_hidden (StTable *table, ClutterActor *child, gboolean value) { StTableChild *meta; g_return_if_fail (ST_IS_TABLE (table)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); meta = get_child_meta (table, child); if (meta->allocate_hidden != value) { meta->allocate_hidden = value; clutter_actor_queue_relayout (child); g_object_notify (G_OBJECT (meta), "allocate-hidden"); } } /** * st_table_child_get_allocate_hidden: * @table: A #StTable * @child: A #ClutterActor * * Determine if the child is allocated even if it is hidden * * Returns: %TRUE if the actor is allocated when hidden */ gboolean st_table_child_get_allocate_hidden (StTable *table, ClutterActor *child) { StTableChild *meta; g_return_val_if_fail (ST_IS_TABLE (table), TRUE); g_return_val_if_fail (CLUTTER_IS_ACTOR (child), TRUE); meta = get_child_meta (table, child); return meta->allocate_hidden; } Cinnamon-2.8.6/src/st/st-bin.c0000664000175000017500000003727612625666430015023 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-bin.c: Basic container actor * * Copyright 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-bin * @short_description: a simple container with one actor * * #StBin is a simple container capable of having only one * #ClutterActor as a child. * * #StBin inherits from #StWidget, so it is fully themable. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-bin.h" #include "st-enum-types.h" #include "st-private.h" #define ST_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BIN, StBinPrivate)) struct _StBinPrivate { ClutterActor *child; StAlign x_align; StAlign y_align; guint x_fill : 1; guint y_fill : 1; }; enum { PROP_0, PROP_CHILD, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_X_FILL, PROP_Y_FILL }; static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)); static void st_bin_add (ClutterContainer *container, ClutterActor *actor) { st_bin_set_child (ST_BIN (container), actor); } static void st_bin_remove (ClutterContainer *container, ClutterActor *actor) { StBinPrivate *priv = ST_BIN (container)->priv; if (priv->child == actor) st_bin_set_child (ST_BIN (container), NULL); } static void st_bin_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { StBinPrivate *priv = ST_BIN (container)->priv; if (priv->child) callback (priv->child, user_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = st_bin_add; iface->remove = st_bin_remove; iface->foreach = st_bin_foreach; } static void st_bin_paint (ClutterActor *self) { StBinPrivate *priv = ST_BIN (self)->priv; /* allow StWidget to paint the background */ CLUTTER_ACTOR_CLASS (st_bin_parent_class)->paint (self); /* the pain our child */ if (priv->child) clutter_actor_paint (priv->child); } static void st_bin_pick (ClutterActor *self, const ClutterColor *pick_color) { StBinPrivate *priv = ST_BIN (self)->priv; /* get the default pick implementation */ CLUTTER_ACTOR_CLASS (st_bin_parent_class)->pick (self, pick_color); if (priv->child) clutter_actor_paint (priv->child); } static void st_bin_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBinPrivate *priv = ST_BIN (self)->priv; CLUTTER_ACTOR_CLASS (st_bin_parent_class)->allocate (self, box, flags); if (priv->child) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox childbox; st_theme_node_get_content_box (theme_node, box, &childbox); _st_allocate_fill (ST_WIDGET (self), priv->child, &childbox, priv->x_align, priv->y_align, priv->x_fill, priv->y_fill); clutter_actor_allocate (priv->child, &childbox, flags); } } static void st_bin_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StBinPrivate *priv = ST_BIN (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_height (theme_node, &for_height); if (priv->child == NULL) { if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; } else { _st_actor_get_preferred_width (priv->child, for_height, priv->y_fill, min_width_p, natural_width_p); } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_bin_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBinPrivate *priv = ST_BIN (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_width (theme_node, &for_width); if (priv->child == NULL) { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } else { _st_actor_get_preferred_height (priv->child, for_width, priv->x_fill, min_height_p, natural_height_p); } st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_bin_dispose (GObject *gobject) { StBinPrivate *priv = ST_BIN (gobject)->priv; if (priv->child) clutter_actor_destroy (priv->child); g_assert (priv->child == NULL); G_OBJECT_CLASS (st_bin_parent_class)->dispose (gobject); } static gboolean st_bin_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction) { StBinPrivate *priv = ST_BIN (widget)->priv; ClutterActor *bin_actor = CLUTTER_ACTOR (widget); if (st_widget_get_can_focus (widget)) { if (from && clutter_actor_contains (bin_actor, from)) return FALSE; clutter_actor_grab_key_focus (bin_actor); return TRUE; } else if (priv->child && ST_IS_WIDGET (priv->child)) return st_widget_navigate_focus (ST_WIDGET (priv->child), from, direction, FALSE); else return FALSE; } static void st_bin_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StBin *bin = ST_BIN (gobject); switch (prop_id) { case PROP_CHILD: st_bin_set_child (bin, g_value_get_object (value)); break; case PROP_X_ALIGN: st_bin_set_alignment (bin, g_value_get_enum (value), bin->priv->y_align); break; case PROP_Y_ALIGN: st_bin_set_alignment (bin, bin->priv->x_align, g_value_get_enum (value)); break; case PROP_X_FILL: st_bin_set_fill (bin, g_value_get_boolean (value), bin->priv->y_fill); break; case PROP_Y_FILL: st_bin_set_fill (bin, bin->priv->y_fill, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void st_bin_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StBinPrivate *priv = ST_BIN (gobject)->priv; switch (prop_id) { case PROP_CHILD: g_value_set_object (value, priv->child); break; case PROP_X_FILL: g_value_set_boolean (value, priv->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, priv->y_fill); break; case PROP_X_ALIGN: g_value_set_enum (value, priv->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, priv->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void st_bin_class_init (StBinClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StBinPrivate)); gobject_class->set_property = st_bin_set_property; gobject_class->get_property = st_bin_get_property; gobject_class->dispose = st_bin_dispose; actor_class->get_preferred_width = st_bin_get_preferred_width; actor_class->get_preferred_height = st_bin_get_preferred_height; actor_class->allocate = st_bin_allocate; actor_class->paint = st_bin_paint; actor_class->pick = st_bin_pick; widget_class->navigate_focus = st_bin_navigate_focus; /** * StBin:child: * * The child #ClutterActor of the #StBin container. */ pspec = g_param_spec_object ("child", "Child", "The child of the Bin", CLUTTER_TYPE_ACTOR, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD, pspec); /** * StBin:x-align: * * The horizontal alignment of the #StBin child. */ pspec = g_param_spec_enum ("x-align", "X Align", "The horizontal alignment", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); /** * StBin:y-align: * * The vertical alignment of the #StBin child. */ pspec = g_param_spec_enum ("y-align", "Y Align", "The vertical alignment", ST_TYPE_ALIGN, ST_ALIGN_MIDDLE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec); /** * StBin:x-fill: * * Whether the child should fill the horizontal allocation */ pspec = g_param_spec_boolean ("x-fill", "X Fill", "Whether the child should fill the " "horizontal allocation", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_X_FILL, pspec); /** * StBin:y-fill: * * Whether the child should fill the vertical allocation */ pspec = g_param_spec_boolean ("y-fill", "Y Fill", "Whether the child should fill the " "vertical allocation", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec); } static void st_bin_init (StBin *bin) { bin->priv = ST_BIN_GET_PRIVATE (bin); bin->priv->x_align = ST_ALIGN_MIDDLE; bin->priv->y_align = ST_ALIGN_MIDDLE; } /** * st_bin_new: * * Creates a new #StBin, a simple container for one child. * * Return value: the newly created #StBin actor */ StWidget * st_bin_new (void) { return g_object_new (ST_TYPE_BIN, NULL); } /** * st_bin_set_child: * @bin: a #StBin * @child: (allow-none): a #ClutterActor, or %NULL * * Sets @child as the child of @bin. * * If @bin already has a child, the previous child is removed. */ void st_bin_set_child (StBin *bin, ClutterActor *child) { StBinPrivate *priv; g_return_if_fail (ST_IS_BIN (bin)); g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child)); priv = bin->priv; if (priv->child == child) return; if (priv->child) { ClutterActor *old_child = priv->child; g_object_ref (old_child); priv->child = NULL; clutter_actor_unparent (old_child); g_signal_emit_by_name (bin, "actor-removed", old_child); g_object_unref (old_child); } if (child) { priv->child = child; clutter_actor_add_child (CLUTTER_ACTOR (bin), child); g_signal_emit_by_name (bin, "actor-added", priv->child); } clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_notify (G_OBJECT (bin), "child"); } /** * st_bin_get_child: * @bin: a #StBin * * Retrieves a pointer to the child of @bin. * * Return value: (transfer none): a #ClutterActor, or %NULL */ ClutterActor * st_bin_get_child (StBin *bin) { g_return_val_if_fail (ST_IS_BIN (bin), NULL); return bin->priv->child; } /** * st_bin_set_alignment: * @bin: a #StBin * @x_align: horizontal alignment * @y_align: vertical alignment * * Sets the horizontal and vertical alignment of the child * inside a #StBin. */ void st_bin_set_alignment (StBin *bin, StAlign x_align, StAlign y_align) { StBinPrivate *priv; gboolean changed = FALSE; g_return_if_fail (ST_IS_BIN (bin)); priv = bin->priv; g_object_freeze_notify (G_OBJECT (bin)); if (priv->x_align != x_align) { priv->x_align = x_align; g_object_notify (G_OBJECT (bin), "x-align"); changed = TRUE; } if (priv->y_align != y_align) { priv->y_align = y_align; g_object_notify (G_OBJECT (bin), "y-align"); changed = TRUE; } if (changed) clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_thaw_notify (G_OBJECT (bin)); } /** * st_bin_get_alignment: * @bin: a #StBin * @x_align: return location for the horizontal alignment, or %NULL * @y_align: return location for the vertical alignment, or %NULL * * Retrieves the horizontal and vertical alignment of the child * inside a #StBin, as set by st_bin_set_alignment(). */ void st_bin_get_alignment (StBin *bin, StAlign *x_align, StAlign *y_align) { StBinPrivate *priv; g_return_if_fail (ST_IS_BIN (bin)); priv = bin->priv; if (x_align) *x_align = priv->x_align; if (y_align) *y_align = priv->y_align; } /** * st_bin_set_fill: * @bin: a #StBin * @x_fill: %TRUE if the child should fill horizontally the @bin * @y_fill: %TRUE if the child should fill vertically the @bin * * Sets whether the child of @bin should fill out the horizontal * and/or vertical allocation of the parent */ void st_bin_set_fill (StBin *bin, gboolean x_fill, gboolean y_fill) { StBinPrivate *priv; gboolean changed = FALSE; g_return_if_fail (ST_IS_BIN (bin)); priv = bin->priv; g_object_freeze_notify (G_OBJECT (bin)); if (priv->x_fill != x_fill) { priv->x_fill = x_fill; changed = TRUE; g_object_notify (G_OBJECT (bin), "x-fill"); } if (priv->y_fill != y_fill) { priv->y_fill = y_fill; changed = TRUE; g_object_notify (G_OBJECT (bin), "y-fill"); } if (changed) clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_thaw_notify (G_OBJECT (bin)); } /** * st_bin_get_fill: * @bin: a #StBin * @x_fill: (out): return location for the horizontal fill, or %NULL * @y_fill: (out): return location for the vertical fill, or %NULL * * Retrieves the horizontal and vertical fill settings */ void st_bin_get_fill (StBin *bin, gboolean *x_fill, gboolean *y_fill) { g_return_if_fail (ST_IS_BIN (bin)); if (x_fill) *x_fill = bin->priv->x_fill; if (y_fill) *y_fill = bin->priv->y_fill; } Cinnamon-2.8.6/src/st/st-button.h0000664000175000017500000000670512625666430015564 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-button.h: Plain button actor * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_BUTTON_H__ #define __ST_BUTTON_H__ G_BEGIN_DECLS #include #define ST_TYPE_BUTTON (st_button_get_type ()) #define ST_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BUTTON, StButton)) #define ST_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BUTTON)) #define ST_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BUTTON, StButtonClass)) #define ST_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BUTTON)) #define ST_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BUTTON, StButtonClass)) typedef struct _StButton StButton; typedef struct _StButtonPrivate StButtonPrivate; typedef struct _StButtonClass StButtonClass; /** * StButton: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _StButton { /*< private >*/ StBin parent_instance; StButtonPrivate *priv; }; struct _StButtonClass { StBinClass parent_class; /* vfuncs, not signals */ void (* transition) (StButton *button); /* signals */ void (* clicked) (StButton *button); }; GType st_button_get_type (void) G_GNUC_CONST; StWidget *st_button_new (void); StWidget *st_button_new_with_label (const gchar *text); const gchar *st_button_get_label (StButton *button); void st_button_set_label (StButton *button, const gchar *text); void st_button_set_toggle_mode (StButton *button, gboolean toggle); gboolean st_button_get_toggle_mode (StButton *button); void st_button_set_checked (StButton *button, gboolean checked); gboolean st_button_get_checked (StButton *button); void st_button_fake_release (StButton *button); /** * StButtonMask: * @ST_BUTTON_ONE: button 1 (left) * @ST_BUTTON_TWO: button 2 (middle) * @ST_BUTTON_THREE: button 3 (right) * * A mask representing which mouse buttons an StButton responds to. */ typedef enum { ST_BUTTON_ONE = (1 << 0), ST_BUTTON_TWO = (1 << 1), ST_BUTTON_THREE = (1 << 2), } StButtonMask; #define ST_BUTTON_MASK_FROM_BUTTON(button) (1 << ((button) - 1)) void st_button_set_button_mask (StButton *button, StButtonMask mask); StButtonMask st_button_get_button_mask (StButton *button); G_END_DECLS #endif /* __ST_BUTTON_H__ */ Cinnamon-2.8.6/src/st/st-private.c0000664000175000017500000006376012625666430015722 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-private.h: Private declarations and functions * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010 Florian Müllner * Copyright 2010 Intel Corporation * Copyright 2010 Giovanni Campagna * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "st-private.h" #include "st-cogl-wrapper.h" /** * _st_actor_get_preferred_width: * @actor: a #ClutterActor * @for_height: as with clutter_actor_get_preferred_width() * @y_fill: %TRUE if @actor will fill its allocation vertically * @min_width_p: as with clutter_actor_get_preferred_width() * @natural_width_p: as with clutter_actor_get_preferred_width() * * Like clutter_actor_get_preferred_width(), but if @y_fill is %FALSE, * then it will compute a width request based on the assumption that * @actor will be given an allocation no taller than its natural * height. */ void _st_actor_get_preferred_width (ClutterActor *actor, gfloat for_height, gboolean y_fill, gfloat *min_width_p, gfloat *natural_width_p) { if (!y_fill && for_height != -1) { ClutterRequestMode mode; gfloat natural_height; mode = clutter_actor_get_request_mode (actor); if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { clutter_actor_get_preferred_height (actor, -1, NULL, &natural_height); if (for_height > natural_height) for_height = natural_height; } } clutter_actor_get_preferred_width (actor, for_height, min_width_p, natural_width_p); } /** * _st_actor_get_preferred_height: * @actor: a #ClutterActor * @for_width: as with clutter_actor_get_preferred_height() * @x_fill: %TRUE if @actor will fill its allocation horizontally * @min_height_p: as with clutter_actor_get_preferred_height() * @natural_height_p: as with clutter_actor_get_preferred_height() * * Like clutter_actor_get_preferred_height(), but if @x_fill is * %FALSE, then it will compute a height request based on the * assumption that @actor will be given an allocation no wider than * its natural width. */ void _st_actor_get_preferred_height (ClutterActor *actor, gfloat for_width, gboolean x_fill, gfloat *min_height_p, gfloat *natural_height_p) { if (!x_fill && for_width != -1) { ClutterRequestMode mode; gfloat natural_width; mode = clutter_actor_get_request_mode (actor); if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (actor, -1, NULL, &natural_width); if (for_width > natural_width) for_width = natural_width; } } clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p); } /** * _st_allocate_fill: * @parent: the parent #StWidget * @child: the child (not necessarily an #StWidget) * @childbox: total space that could be allocated to @child * @x_alignment: horizontal alignment within @childbox * @y_alignment: vertical alignment within @childbox * @x_fill: whether or not to fill @childbox horizontally * @y_fill: whether or not to fill @childbox vertically * * Given @childbox, containing the initial allocation of @child, this * adjusts the horizontal allocation if @x_fill is %FALSE, and the * vertical allocation if @y_fill is %FALSE, by: * * - reducing the allocation if it is larger than @child's natural * size. * * - adjusting the position of the child within the allocation * according to @x_alignment/@y_alignment (and flipping * @x_alignment if @parent has %ST_TEXT_DIRECTION_RTL) * * If @x_fill and @y_fill are both %TRUE, or if @child's natural size * is larger than the initial allocation in @childbox, then @childbox * will be unchanged. * * If you are allocating children with _st_allocate_fill(), you should * determine their preferred sizes using * _st_actor_get_preferred_width() and * _st_actor_get_preferred_height(), not with the corresponding * Clutter methods. */ void _st_allocate_fill (StWidget *parent, ClutterActor *child, ClutterActorBox *childbox, StAlign x_alignment, StAlign y_alignment, gboolean x_fill, gboolean y_fill) { gfloat natural_width, natural_height; gfloat min_width, min_height; gfloat child_width, child_height; gfloat available_width, available_height; ClutterRequestMode request; gdouble x_align, y_align; available_width = childbox->x2 - childbox->x1; available_height = childbox->y2 - childbox->y1; if (available_width < 0) { available_width = 0; childbox->x2 = childbox->x1; } if (available_height < 0) { available_height = 0; childbox->y2 = childbox->y1; } /* If we are filling both horizontally and vertically then we don't * need to do anything else. */ if (x_fill && y_fill) return; _st_get_align_factors (parent, x_alignment, y_alignment, &x_align, &y_align); /* The following is based on clutter_actor_get_preferred_size(), but * modified to cope with the fact that the available size may be * less than the preferred size. */ request = clutter_actor_get_request_mode (child); if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, -1, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, child_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, -1, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, child_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } if (!x_fill) { childbox->x1 += (int)((available_width - child_width) * x_align); childbox->x2 = childbox->x1 + (int) child_width; } if (!y_fill) { childbox->y1 += (int)((available_height - child_height) * y_align); childbox->y2 = childbox->y1 + (int) child_height; } } /** * _st_get_align_factors: * @widget: an #StWidget * @x_align: an #StAlign * @y_align: an #StAlign * @x_align_out: (out) (allow-none): @x_align as a #gdouble * @y_align_out: (out) (allow-none): @y_align as a #gdouble * * Converts @x_align and @y_align to #gdouble values. If @widget has * %ST_TEXT_DIRECTION_RTL, the @x_align_out value will be flipped * relative to @x_align. */ void _st_get_align_factors (StWidget *widget, StAlign x_align, StAlign y_align, gdouble *x_align_out, gdouble *y_align_out) { if (x_align_out) { switch (x_align) { case ST_ALIGN_START: *x_align_out = 0.0; break; case ST_ALIGN_MIDDLE: *x_align_out = 0.5; break; case ST_ALIGN_END: *x_align_out = 1.0; break; default: g_warn_if_reached (); break; } if (st_widget_get_direction (widget) == ST_TEXT_DIRECTION_RTL) *x_align_out = 1.0 - *x_align_out; } if (y_align_out) { switch (y_align) { case ST_ALIGN_START: *y_align_out = 0.0; break; case ST_ALIGN_MIDDLE: *y_align_out = 0.5; break; case ST_ALIGN_END: *y_align_out = 1.0; break; default: g_warn_if_reached (); break; } } } /** * _st_set_text_from_style: * @text: Target #ClutterText * @theme_node: Source #StThemeNode * * Set various GObject properties of the @text object using * CSS information from @theme_node. */ void _st_set_text_from_style (ClutterText *text, StThemeNode *theme_node) { ClutterColor color; StTextDecoration decoration; PangoAttrList *attribs; const PangoFontDescription *font; gchar *font_string; StTextAlign align; st_theme_node_get_foreground_color (theme_node, &color); clutter_text_set_color (text, &color); font = st_theme_node_get_font (theme_node); font_string = pango_font_description_to_string (font); clutter_text_set_font_name (text, font_string); g_free (font_string); attribs = pango_attr_list_new (); decoration = st_theme_node_get_text_decoration (theme_node); if (decoration & ST_TEXT_DECORATION_UNDERLINE) { PangoAttribute *underline = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); pango_attr_list_insert (attribs, underline); } if (decoration & ST_TEXT_DECORATION_LINE_THROUGH) { PangoAttribute *strikethrough = pango_attr_strikethrough_new (TRUE); pango_attr_list_insert (attribs, strikethrough); } /* Pango doesn't have an equivalent attribute for _OVERLINE, and we deliberately * skip BLINK (for now...) */ clutter_text_set_attributes (text, attribs); pango_attr_list_unref (attribs); align = st_theme_node_get_text_align (theme_node); if(align == ST_TEXT_ALIGN_JUSTIFY) { clutter_text_set_justify (text, TRUE); clutter_text_set_line_alignment (text, PANGO_ALIGN_LEFT); } else { clutter_text_set_justify (text, FALSE); clutter_text_set_line_alignment (text, (PangoAlignment) align); } } /** * _st_create_texture_material: * @src_texture: The CoglTexture for the material * * Creates a simple material which contains the given texture as a * single layer. */ CoglHandle _st_create_texture_material (CoglHandle src_texture) { static CoglHandle texture_material_template = COGL_INVALID_HANDLE; CoglHandle material; g_return_val_if_fail (src_texture != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); /* We use a material that has a dummy texture as a base for all texture materials. The idea is that only the Cogl texture object would be different in the children so it is likely that Cogl will be able to share GL programs between all the textures. */ if (G_UNLIKELY (texture_material_template == COGL_INVALID_HANDLE)) { static const guint8 white_pixel[] = { 0xff, 0xff, 0xff, 0xff }; CoglHandle dummy_texture; dummy_texture = st_cogl_texture_new_from_data_wrapper (1, 1, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_ANY, 4, white_pixel); texture_material_template = cogl_material_new (); cogl_material_set_layer (texture_material_template, 0, dummy_texture); cogl_handle_unref (dummy_texture); } material = cogl_material_copy (texture_material_template); cogl_material_set_layer (material, 0, src_texture); return material; } /***** * Shadows *****/ static gdouble * calculate_gaussian_kernel (gdouble sigma, guint n_values) { gdouble *ret, sum; gdouble exp_divisor; gint half, i; g_return_val_if_fail (sigma > 0, NULL); half = n_values / 2; ret = g_malloc (n_values * sizeof (gdouble)); sum = 0.0; exp_divisor = 2 * sigma * sigma; /* n_values of 1D Gauss function */ for (i = 0; i < n_values; i++) { ret[i] = exp (-(i - half) * (i - half) / exp_divisor); sum += ret[i]; } /* normalize */ for (i = 0; i < n_values; i++) ret[i] /= sum; return ret; } static guchar * blur_pixels (guchar *pixels_in, gint width_in, gint height_in, gint rowstride_in, gdouble blur, gint *width_out, gint *height_out, gint *rowstride_out) { guchar *pixels_out; float sigma; /* The CSS specification defines (or will define) the blur radius as twice * the Gaussian standard deviation. See: * * http://lists.w3.org/Archives/Public/www-style/2010Sep/0002.html */ sigma = blur / 2.; if ((guint) blur == 0) { *width_out = width_in; *height_out = height_in; *rowstride_out = rowstride_in; pixels_out = g_memdup (pixels_in, *rowstride_out * *height_out); } else { gdouble *kernel; guchar *line; gint n_values, half; gint x_in, y_in, x_out, y_out, i; n_values = (gint) 5 * sigma; half = n_values / 2; *width_out = width_in + 2 * half; *height_out = height_in + 2 * half; *rowstride_out = (*width_out + 3) & ~3; pixels_out = g_malloc0 (*rowstride_out * *height_out); line = g_malloc0 (*rowstride_out); kernel = calculate_gaussian_kernel (sigma, n_values); /* vertical blur */ for (x_in = 0; x_in < width_in; x_in++) for (y_out = 0; y_out < *height_out; y_out++) { guchar *pixel_in, *pixel_out; gint i0, i1; y_in = y_out - half; /* We read from the source at 'y = y_in + i - half'; clamp the * full i range [0, n_values) so that y is in [0, height_in). */ i0 = MAX (half - y_in, 0); i1 = MIN (height_in + half - y_in, n_values); pixel_in = pixels_in + (y_in + i0 - half) * rowstride_in + x_in; pixel_out = pixels_out + y_out * *rowstride_out + (x_in + half); for (i = i0; i < i1; i++) { *pixel_out += *pixel_in * kernel[i]; pixel_in += rowstride_in; } } /* horizontal blur */ for (y_out = 0; y_out < *height_out; y_out++) { memcpy (line, pixels_out + y_out * *rowstride_out, *rowstride_out); for (x_out = 0; x_out < *width_out; x_out++) { gint i0, i1; guchar *pixel_out, *pixel_in; /* We read from the source at 'x = x_out + i - half'; clamp the * full i range [0, n_values) so that x is in [0, width_out). */ i0 = MAX (half - x_out, 0); i1 = MIN (*width_out + half - x_out, n_values); pixel_in = line + x_out + i0 - half; pixel_out = pixels_out + *rowstride_out * y_out + x_out; *pixel_out = 0; for (i = i0; i < i1; i++) { *pixel_out += *pixel_in * kernel[i]; pixel_in++; } } } g_free (kernel); g_free (line); } return pixels_out; } CoglHandle _st_create_shadow_material (StShadow *shadow_spec, CoglHandle src_texture) { static CoglHandle shadow_material_template = COGL_INVALID_HANDLE; CoglHandle material; CoglHandle texture; guchar *pixels_in, *pixels_out; gint width_in, height_in, rowstride_in; gint width_out, height_out, rowstride_out; g_return_val_if_fail (shadow_spec != NULL, COGL_INVALID_HANDLE); g_return_val_if_fail (src_texture != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); width_in = cogl_texture_get_width (src_texture); height_in = cogl_texture_get_height (src_texture); rowstride_in = (width_in + 3) & ~3; pixels_in = g_malloc0 (rowstride_in * height_in); cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8, rowstride_in, pixels_in); pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in, shadow_spec->blur, &width_out, &height_out, &rowstride_out); g_free (pixels_in); texture = st_cogl_texture_new_from_data_wrapper (width_out, height_out, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_A_8, COGL_PIXEL_FORMAT_A_8, rowstride_out, pixels_out); g_free (pixels_out); if (G_UNLIKELY (shadow_material_template == COGL_INVALID_HANDLE)) { shadow_material_template = cogl_material_new (); /* We set up the material to blend the shadow texture with the combine * constant, but defer setting the latter until painting, so that we can * take the actor's overall opacity into account. */ cogl_material_set_layer_combine (shadow_material_template, 0, "RGBA = MODULATE (CONSTANT, TEXTURE[A])", NULL); } material = cogl_material_copy (shadow_material_template); cogl_material_set_layer (material, 0, texture); cogl_handle_unref (texture); return material; } CoglHandle _st_create_shadow_material_from_actor (StShadow *shadow_spec, ClutterActor *actor) { CoglHandle shadow_material = COGL_INVALID_HANDLE; if (CLUTTER_IS_TEXTURE (actor)) { CoglHandle texture; texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (actor)); shadow_material = _st_create_shadow_material (shadow_spec, texture); } else { CoglHandle buffer, offscreen; ClutterActorBox box; CoglColor clear_color; float width, height; clutter_actor_get_allocation_box (actor, &box); clutter_actor_box_get_size (&box, &width, &height); if (width == 0 || height == 0) return COGL_INVALID_HANDLE; buffer = st_cogl_texture_new_with_size_wrapper (width, height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); if (buffer == COGL_INVALID_HANDLE) return COGL_INVALID_HANDLE; offscreen = cogl_offscreen_new_to_texture (buffer); if (offscreen == COGL_INVALID_HANDLE) { cogl_handle_unref (buffer); return COGL_INVALID_HANDLE; } cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0); cogl_push_framebuffer (offscreen); cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); cogl_ortho (0, width, height, 0, 0, 1.0); clutter_actor_paint (actor); cogl_pop_framebuffer (); cogl_handle_unref (offscreen); shadow_material = _st_create_shadow_material (shadow_spec, buffer); cogl_handle_unref (buffer); } return shadow_material; } /** * _st_create_shadow_cairo_pattern: * @shadow_spec: the definition of the shadow * @src_pattern: surface pattern for which we create the shadow * (must be a surface pattern) * * This is a utility function for creating shadows used by * st-theme-node.c; it's in this file to share the gaussian * blur implementation. The usage of this function is quite different * depending on whether shadow_spec->inset is %TRUE or not. If * shadow_spec->inset is %TRUE, the caller should pass in a @src_pattern * which is the inverse of what they want shadowed, and must take * care of the spread and offset from the shadow spec themselves. If * shadow_spec->inset is %FALSE then the caller should pass in what they * want shadowed directly, and this function takes care of the spread and * the offset. */ cairo_pattern_t * _st_create_shadow_cairo_pattern (StShadow *shadow_spec, cairo_pattern_t *src_pattern) { static cairo_user_data_key_t shadow_pattern_user_data; cairo_t *cr; cairo_surface_t *src_surface; cairo_surface_t *surface_in; cairo_surface_t *surface_out; cairo_pattern_t *dst_pattern; guchar *pixels_in, *pixels_out; gint width_in, height_in, rowstride_in; gint width_out, height_out, rowstride_out; cairo_matrix_t shadow_matrix; int i, j; g_return_val_if_fail (shadow_spec != NULL, NULL); g_return_val_if_fail (src_pattern != NULL, NULL); cairo_pattern_get_surface (src_pattern, &src_surface); width_in = cairo_image_surface_get_width (src_surface); height_in = cairo_image_surface_get_height (src_surface); /* We want the output to be a color agnostic alpha mask, * so we need to strip the color channels from the input */ if (cairo_image_surface_get_format (src_surface) != CAIRO_FORMAT_A8) { surface_in = cairo_image_surface_create (CAIRO_FORMAT_A8, width_in, height_in); cr = cairo_create (surface_in); cairo_set_source_surface (cr, src_surface, 0, 0); cairo_paint (cr); cairo_destroy (cr); } else { surface_in = cairo_surface_reference (src_surface); } pixels_in = cairo_image_surface_get_data (surface_in); rowstride_in = cairo_image_surface_get_stride (surface_in); pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in, shadow_spec->blur, &width_out, &height_out, &rowstride_out); cairo_surface_destroy (surface_in); /* Invert pixels for inset shadows */ if (shadow_spec->inset) { for (j = 0; j < height_out; j++) { guchar *p = pixels_out + rowstride_out * j; for (i = 0; i < width_out; i++, p++) *p = ~*p; } } surface_out = cairo_image_surface_create_for_data (pixels_out, CAIRO_FORMAT_A8, width_out, height_out, rowstride_out); cairo_surface_set_user_data (surface_out, &shadow_pattern_user_data, pixels_out, (cairo_destroy_func_t) g_free); dst_pattern = cairo_pattern_create_for_surface (surface_out); cairo_surface_destroy (surface_out); cairo_pattern_get_matrix (src_pattern, &shadow_matrix); if (shadow_spec->inset) { /* For inset shadows, offsets and spread radius have already been * applied to the original pattern, so all left to do is shift the * blurred image left, so that it aligns centered under the * unblurred one */ cairo_matrix_translate (&shadow_matrix, (width_out - width_in) / 2.0, (height_out - height_in) / 2.0); cairo_pattern_set_matrix (dst_pattern, &shadow_matrix); return dst_pattern; } /* Read all the code from the cairo_pattern_set_matrix call * at the end of this function to here from bottom to top, * because each new affine transformation is applied in * front of all the previous ones */ /* 6. Invert the matrix back */ cairo_matrix_invert (&shadow_matrix); /* 5. Adjust based on specified offsets */ cairo_matrix_translate (&shadow_matrix, shadow_spec->xoffset, shadow_spec->yoffset); /* 4. Recenter the newly scaled image */ cairo_matrix_translate (&shadow_matrix, - shadow_spec->spread, - shadow_spec->spread); /* 3. Scale up the blurred image to fill the spread */ cairo_matrix_scale (&shadow_matrix, (width_in + 2.0 * shadow_spec->spread) / width_in, (height_in + 2.0 * shadow_spec->spread) / height_in); /* 2. Shift the blurred image left, so that it aligns centered * under the unblurred one */ cairo_matrix_translate (&shadow_matrix, - (width_out - width_in) / 2.0, - (height_out - height_in) / 2.0); /* 1. Invert the matrix so we can work with it in pattern space */ cairo_matrix_invert (&shadow_matrix); cairo_pattern_set_matrix (dst_pattern, &shadow_matrix); return dst_pattern; } void _st_paint_shadow_with_opacity (StShadow *shadow_spec, CoglHandle shadow_material, ClutterActorBox *box, guint8 paint_opacity) { ClutterActorBox shadow_box; CoglColor color; g_return_if_fail (shadow_spec != NULL); g_return_if_fail (shadow_material != COGL_INVALID_HANDLE); st_shadow_get_box (shadow_spec, box, &shadow_box); cogl_color_set_from_4ub (&color, shadow_spec->color.red * paint_opacity / 255, shadow_spec->color.green * paint_opacity / 255, shadow_spec->color.blue * paint_opacity / 255, shadow_spec->color.alpha * paint_opacity / 255); cogl_color_premultiply (&color); cogl_material_set_layer_combine_constant (shadow_material, 0, &color); cogl_set_source (shadow_material); cogl_rectangle_with_texture_coords (shadow_box.x1, shadow_box.y1, shadow_box.x2, shadow_box.y2, 0, 0, 1, 1); } Cinnamon-2.8.6/src/st/test-theme.c0000664000175000017500000003664612625666430015706 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * test-theme.c: test program for CSS styling code * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include "st-theme.h" #include "st-theme-context.h" #include "st-label.h" #include #include static ClutterActor *stage; static StThemeNode *root; static StThemeNode *group1; static StThemeNode *text1; static StThemeNode *text2; static StThemeNode *group2; static StThemeNode *text3; static StThemeNode *text4; static StThemeNode *group3; static StThemeNode *cairo_texture; static gboolean fail; static const char *test; static void assert_font (StThemeNode *node, const char *node_description, const char *expected) { char *value = pango_font_description_to_string (st_theme_node_get_font (node)); if (strcmp (expected, value) != 0) { g_print ("%s: %s.font: expected: %s, got: %s\n", test, node_description, expected, value); fail = TRUE; } g_free (value); } static char * text_decoration_to_string (StTextDecoration decoration) { GString *result = g_string_new (NULL); if (decoration & ST_TEXT_DECORATION_UNDERLINE) g_string_append(result, " underline"); if (decoration & ST_TEXT_DECORATION_OVERLINE) g_string_append(result, " overline"); if (decoration & ST_TEXT_DECORATION_LINE_THROUGH) g_string_append(result, " line_through"); if (decoration & ST_TEXT_DECORATION_BLINK) g_string_append(result, " blink"); if (result->len > 0) g_string_erase (result, 0, 1); else g_string_append(result, "none"); return g_string_free (result, FALSE); } static void assert_text_decoration (StThemeNode *node, const char *node_description, StTextDecoration expected) { StTextDecoration value = st_theme_node_get_text_decoration (node); if (expected != value) { char *es = text_decoration_to_string (expected); char *vs = text_decoration_to_string (value); g_print ("%s: %s.text-decoration: expected: %s, got: %s\n", test, node_description, es, vs); fail = TRUE; g_free (es); g_free (vs); } } static void assert_foreground_color (StThemeNode *node, const char *node_description, guint32 expected) { ClutterColor color; st_theme_node_get_foreground_color (node, &color); guint32 value = clutter_color_to_pixel (&color); if (expected != value) { g_print ("%s: %s.color: expected: #%08x, got: #%08x\n", test, node_description, expected, value); fail = TRUE; } } static void assert_background_color (StThemeNode *node, const char *node_description, guint32 expected) { ClutterColor color; st_theme_node_get_background_color (node, &color); guint32 value = clutter_color_to_pixel (&color); if (expected != value) { g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n", test, node_description, expected, value); fail = TRUE; } } static const char * side_to_string (StSide side) { switch (side) { case ST_SIDE_TOP: return "top"; case ST_SIDE_RIGHT: return "right"; case ST_SIDE_BOTTOM: return "bottom"; case ST_SIDE_LEFT: return "left"; } return ""; } static void assert_border_color (StThemeNode *node, const char *node_description, StSide side, guint32 expected) { ClutterColor color; st_theme_node_get_border_color (node, side, &color); guint32 value = clutter_color_to_pixel (&color); if (expected != value) { g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n", test, node_description, side_to_string (side), expected, value); fail = TRUE; } } static void assert_background_image (StThemeNode *node, const char *node_description, const char *expected) { const char *value = st_theme_node_get_background_image (node); if (expected == NULL) expected = "(null)"; if (value == NULL) value = "(null)"; if (strcmp (expected, value) != 0) { g_print ("%s: %s.background-image: expected: %s, got: %s\n", test, node_description, expected, value); fail = TRUE; } } #define LENGTH_EPSILON 0.001 static void assert_length (const char *node_description, const char *property_description, double expected, double value) { if (fabs (expected - value) > LENGTH_EPSILON) { g_print ("%s %s.%s: expected: %3f, got: %3f\n", test, node_description, property_description, expected, value); fail = TRUE; } } static void test_defaults (void) { test = "defaults"; /* font comes from context */ assert_font (root, "stage", "sans-serif 12"); /* black is the default foreground color */ assert_foreground_color (root, "stage", 0x00000ff); } static void test_lengths (void) { test = "lengths"; /* 12pt == 16px at 96dpi */ assert_length ("group1", "padding-top", 16., st_theme_node_get_padding (group1, ST_SIDE_TOP)); /* 12px == 12px */ assert_length ("group1", "padding-right", 12., st_theme_node_get_padding (group1, ST_SIDE_RIGHT)); /* 2em == 32px (with a 12pt font) */ assert_length ("group1", "padding-bottom", 32., st_theme_node_get_padding (group1, ST_SIDE_BOTTOM)); /* 1in == 72pt == 96px, at 96dpi */ assert_length ("group1", "padding-left", 96., st_theme_node_get_padding (group1, ST_SIDE_LEFT)); } static void test_classes (void) { test = "classes"; /* .special-text class overrides size and style; * the ClutterTexture.special-text selector doesn't match */ assert_font (text1, "text1", "sans-serif Italic 32px"); } static void test_type_inheritance (void) { test = "type_inheritance"; /* From ClutterTexture element selector */ assert_length ("cairoTexture", "padding-top", 10., st_theme_node_get_padding (cairo_texture, ST_SIDE_TOP)); /* From ClutterCairoTexture element selector */ assert_length ("cairoTexture", "padding-right", 20., st_theme_node_get_padding (cairo_texture, ST_SIDE_RIGHT)); } static void test_adjacent_selector (void) { test = "adjacent_selector"; /* #group1 > #text1 matches text1 */ assert_foreground_color (text1, "text1", 0x00ff00ff); /* stage > #text2 doesn't match text2 */ assert_foreground_color (text2, "text2", 0x000000ff); } static void test_padding (void) { test = "padding"; /* Test that a 4-sided padding property assigns the right paddings to * all sides */ assert_length ("group2", "padding-top", 1., st_theme_node_get_padding (group2, ST_SIDE_TOP)); assert_length ("group2", "padding-right", 2., st_theme_node_get_padding (group2, ST_SIDE_RIGHT)); assert_length ("group2", "padding-bottom", 3., st_theme_node_get_padding (group2, ST_SIDE_BOTTOM)); assert_length ("group2", "padding-left", 4., st_theme_node_get_padding (group2, ST_SIDE_LEFT)); } static void test_border (void) { test = "border"; /* group2 is defined as having a thin black border along the top three * sides with rounded joins, then a square-joined green border at the * botttom */ assert_length ("group2", "border-top-width", 2., st_theme_node_get_border_width (group2, ST_SIDE_TOP)); assert_length ("group2", "border-right-width", 2., st_theme_node_get_border_width (group2, ST_SIDE_RIGHT)); assert_length ("group2", "border-bottom-width", 5., st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM)); assert_length ("group2", "border-left-width", 2., st_theme_node_get_border_width (group2, ST_SIDE_LEFT)); assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff); assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff); assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff); assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff); assert_length ("group2", "border-radius-topleft", 10., st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT)); assert_length ("group2", "border-radius-topright", 10., st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT)); assert_length ("group2", "border-radius-bottomright", 0., st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT)); assert_length ("group2", "border-radius-bottomleft", 0., st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT)); } static void test_background (void) { test = "background"; /* group1 has a background: shortcut property setting color and image */ assert_background_color (group1, "group1", 0xff0000ff); assert_background_image (group1, "group1", "st/some-background.png"); /* text1 inherits the background image but not the color */ assert_background_color (text1, "text1", 0x00000000); assert_background_image (text1, "text1", "st/some-background.png"); /* text1 inherits inherits both, but then background: none overrides both */ assert_background_color (text2, "text2", 0x00000000); assert_background_image (text2, "text2", NULL); /* background-image property */ assert_background_image (group2, "group2", "st/other-background.png"); } static void test_font (void) { test = "font"; /* font specified with font: */ assert_font (group2, "group2", "serif Italic 12px"); /* text3 inherits and overrides individually properties */ assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px"); } static void test_pseudo_class (void) { StWidget *label; StThemeNode *labelNode; test = "pseudo_class"; /* text4 has :visited and :hover pseudo-classes, so should pick up both of these */ assert_foreground_color (text4, "text4", 0x888888ff); assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE); /* :hover pseudo-class matches, but class doesn't match */ assert_text_decoration (group3, "group3", 0); /* Test the StWidget add/remove pseudo_class interfaces */ label = st_label_new ("foo"); clutter_actor_add_child (stage, CLUTTER_ACTOR (label)); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff); assert_text_decoration (labelNode, "label", 0); assert_length ("label", "border-width", 0., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_add_style_pseudo_class (label, "visited"); g_assert (st_widget_has_style_pseudo_class (label, "visited")); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x888888ff); assert_text_decoration (labelNode, "label", 0); assert_length ("label", "border-width", 0., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_add_style_pseudo_class (label, "hover"); g_assert (st_widget_has_style_pseudo_class (label, "hover")); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x888888ff); assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE); assert_length ("label", "border-width", 0., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_remove_style_pseudo_class (label, "visited"); g_assert (!st_widget_has_style_pseudo_class (label, "visited")); g_assert (st_widget_has_style_pseudo_class (label, "hover")); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff); assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE); assert_length ("label", "border-width", 0., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_add_style_pseudo_class (label, "boxed"); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff); assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE); assert_length ("label", "border-width", 1., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_remove_style_pseudo_class (label, "hover"); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff); assert_text_decoration (labelNode, "label", 0); assert_length ("label", "border-width", 1., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); st_widget_remove_style_pseudo_class (label, "boxed"); g_assert (st_widget_get_style_pseudo_class (label) == NULL); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff); assert_text_decoration (labelNode, "label", 0); assert_length ("label", "border-width", 0., st_theme_node_get_border_width (labelNode, ST_SIDE_TOP)); } static void test_inline_style (void) { test = "inline_style"; /* These properties come from the inline-style specified when creating the node */ assert_foreground_color (text3, "text3", 0x00000ffff); assert_length ("text3", "padding-bottom", 12., st_theme_node_get_padding (text3, ST_SIDE_BOTTOM)); } int main (int argc, char **argv) { StTheme *theme; StThemeContext *context; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; theme = st_theme_new ("st/test-theme.css", NULL, NULL); stage = clutter_stage_new (); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); st_theme_context_set_theme (context, theme); st_theme_context_set_font (context, pango_font_description_from_string ("sans-serif 12")); root = st_theme_context_get_root_node (context); group1 = st_theme_node_new (context, root, NULL, CLUTTER_TYPE_GROUP, "group1", NULL, NULL, NULL, FALSE); text1 = st_theme_node_new (context, group1, NULL, CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL, FALSE); text2 = st_theme_node_new (context, group1, NULL, CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL, FALSE); group2 = st_theme_node_new (context, root, NULL, CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL, FALSE); text3 = st_theme_node_new (context, group2, NULL, CLUTTER_TYPE_TEXT, "text3", NULL, NULL, "color: #0000ff; padding-bottom: 12px;", FALSE); text4 = st_theme_node_new (context, group2, NULL, CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL, FALSE); group3 = st_theme_node_new (context, group2, NULL, CLUTTER_TYPE_GROUP, "group3", NULL, "hover", NULL, FALSE); cairo_texture = st_theme_node_new (context, root, NULL, CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL, FALSE); test_defaults (); test_lengths (); test_classes (); test_type_inheritance (); test_adjacent_selector (); test_padding (); test_border (); test_background (); test_font (); test_pseudo_class (); test_inline_style (); return fail ? 1 : 0; } Cinnamon-2.8.6/src/st/st-theme-node-private.h0000664000175000017500000000654412625666430017747 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node-private.h: private structures and functions for StThemeNode * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_NODE_PRIVATE_H__ #define __ST_THEME_NODE_PRIVATE_H__ #include #include "st-theme-node.h" #include "st-types.h" G_BEGIN_DECLS struct _StThemeNode { GObject parent; StThemeContext *context; StThemeNode *parent_node; StTheme *theme; PangoFontDescription *font_desc; ClutterColor background_color; /* If gradient is set, then background_color is the gradient start */ StGradientType background_gradient_type; ClutterColor background_gradient_end; int background_position_x; int background_position_y; StBackgroundSize background_size; gint background_size_w; gint background_size_h; ClutterColor foreground_color; ClutterColor border_color[4]; ClutterColor outline_color; int border_width[4]; int border_radius[4]; int outline_width; guint padding[4]; int width; int height; int min_width; int min_height; int max_width; int max_height; int transition_duration; char *background_image; char *background_bumpmap; StBorderImage *border_image; StShadow *box_shadow; StShadow *background_image_shadow; StShadow *text_shadow; StIconColors *icon_colors; GType element_type; char *element_id; GStrv element_classes; GStrv pseudo_classes; char *inline_style; gboolean important; CRDeclaration **properties; int n_properties; /* We hold onto these separately so we can destroy them on finalize */ CRDeclaration *inline_properties; guint background_position_set : 1; guint background_repeat : 1; guint properties_computed : 1; guint geometry_computed : 1; guint background_computed : 1; guint foreground_computed : 1; guint border_image_computed : 1; guint box_shadow_computed : 1; guint background_image_shadow_computed : 1; guint text_shadow_computed : 1; guint link_type : 2; /* Graphics state */ float alloc_width; float alloc_height; CoglHandle background_shadow_material; CoglHandle box_shadow_material; CoglHandle background_texture; CoglHandle background_material; CoglHandle border_slices_texture; CoglHandle border_slices_material; CoglHandle prerendered_texture; CoglHandle prerendered_material; CoglHandle corner_material[4]; }; struct _StThemeNodeClass { GObjectClass parent_class; }; void _st_theme_node_ensure_background (StThemeNode *node); void _st_theme_node_ensure_geometry (StThemeNode *node); void _st_theme_node_init_drawing_state (StThemeNode *node); void _st_theme_node_free_drawing_state (StThemeNode *node); G_END_DECLS #endif /* __ST_THEME_NODE_PRIVATE_H__ */ Cinnamon-2.8.6/src/st/st-drawing-area.c0000664000175000017500000002051012625666430016573 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-drawing-area.c: A dynamically-sized Cairo drawing area * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-drawing-area * @short_description: A dynamically-sized Cairo drawing area * * #StDrawingArea is similar to #ClutterCairoTexture in that * it allows drawing via Cairo; the primary difference is that * it is dynamically sized. To use, connect to the #StDrawingArea::repaint * signal, and inside the signal handler, call * st_drawing_area_get_context() to get the Cairo context to draw to. The * #StDrawingArea::repaint signal will be emitted by default when the area is * resized or the CSS style changes; you can use the * st_drawing_area_queue_repaint() as well. */ #include "st-drawing-area.h" #include "st-cogl-wrapper.h" #include G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_WIDGET); struct _StDrawingAreaPrivate { CoglHandle texture; CoglHandle material; cairo_t *context; guint needs_repaint : 1; guint in_repaint : 1; }; /* Signals */ enum { REPAINT, LAST_SIGNAL }; static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 }; static void st_drawing_area_dispose (GObject *object) { StDrawingArea *area = ST_DRAWING_AREA (object); StDrawingAreaPrivate *priv = area->priv; if (priv->material != COGL_INVALID_HANDLE) { cogl_handle_unref (priv->material); priv->material = COGL_INVALID_HANDLE; } if (priv->texture != COGL_INVALID_HANDLE) { cogl_handle_unref (priv->texture); priv->texture = COGL_INVALID_HANDLE; } G_OBJECT_CLASS (st_drawing_area_parent_class)->dispose (object); } static void st_drawing_area_paint (ClutterActor *self) { StDrawingArea *area = ST_DRAWING_AREA (self); StDrawingAreaPrivate *priv = area->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox allocation_box; ClutterActorBox content_box; int width, height; CoglColor color; guint8 paint_opacity; (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->paint (self); clutter_actor_get_allocation_box (self, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); width = (int)(0.5 + content_box.x2 - content_box.x1); height = (int)(0.5 + content_box.y2 - content_box.y1); if (priv->material == COGL_INVALID_HANDLE) priv->material = cogl_material_new (); if (priv->texture != COGL_INVALID_HANDLE && (width != cogl_texture_get_width (priv->texture) || height != cogl_texture_get_height (priv->texture))) { cogl_handle_unref (priv->texture); priv->texture = COGL_INVALID_HANDLE; } if (width > 0 && height > 0) { if (priv->texture == COGL_INVALID_HANDLE) { priv->texture = st_cogl_texture_new_with_size_wrapper (width, height, COGL_TEXTURE_NONE, CLUTTER_CAIRO_FORMAT_ARGB32); priv->needs_repaint = TRUE; } if (priv->needs_repaint) { cairo_surface_t *surface; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); priv->context = cairo_create (surface); priv->in_repaint = TRUE; priv->needs_repaint = FALSE; g_signal_emit ((GObject*)area, st_drawing_area_signals[REPAINT], 0); priv->in_repaint = FALSE; cairo_destroy (priv->context); priv->context = NULL; cogl_texture_set_region (priv->texture, 0, 0, 0, 0, width, height, width, height, CLUTTER_CAIRO_FORMAT_ARGB32, cairo_image_surface_get_stride (surface), cairo_image_surface_get_data (surface)); cairo_surface_destroy (surface); } } cogl_material_set_layer (priv->material, 0, priv->texture); if (priv->texture) { paint_opacity = clutter_actor_get_paint_opacity (self); cogl_color_set_from_4ub (&color, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_material_set_color (priv->material, &color); cogl_set_source (priv->material); cogl_rectangle_with_texture_coords (content_box.x1, content_box.y1, width, height, 0.0f, 0.0f, 1.0f, 1.0f); } } static void st_drawing_area_style_changed (StWidget *self) { StDrawingArea *area = ST_DRAWING_AREA (self); StDrawingAreaPrivate *priv = area->priv; (ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self); priv->needs_repaint = TRUE; } static void st_drawing_area_class_init (StDrawingAreaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); gobject_class->dispose = st_drawing_area_dispose; actor_class->paint = st_drawing_area_paint; widget_class->style_changed = st_drawing_area_style_changed; st_drawing_area_signals[REPAINT] = g_signal_new ("repaint", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StDrawingAreaClass, repaint), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 0); g_type_class_add_private (gobject_class, sizeof (StDrawingAreaPrivate)); } static void st_drawing_area_init (StDrawingArea *area) { area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, ST_TYPE_DRAWING_AREA, StDrawingAreaPrivate); area->priv->texture = COGL_INVALID_HANDLE; } /** * st_drawing_area_queue_repaint: * @area: the #StDrawingArea * * Will cause the actor to emit a ::repaint signal before it is next * drawn to the scene. Useful if some parameters for the area being * drawn other than the size or style have changed. Note that * clutter_actor_queue_redraw() will simply result in the same * contents being drawn to the scene again. */ void st_drawing_area_queue_repaint (StDrawingArea *area) { StDrawingAreaPrivate *priv; g_return_if_fail (ST_IS_DRAWING_AREA (area)); priv = area->priv; priv->needs_repaint = TRUE; clutter_actor_queue_redraw (CLUTTER_ACTOR (area)); } /** * st_drawing_area_get_context: * @area: the #StDrawingArea * * Gets the Cairo context to paint to. This function must only be called * from a signal hander for the ::repaint signal. * * Return Value: (transfer none): the Cairo context for the paint operation */ cairo_t * st_drawing_area_get_context (StDrawingArea *area) { g_return_val_if_fail (ST_IS_DRAWING_AREA (area), NULL); g_return_val_if_fail (area->priv->in_repaint, NULL); return area->priv->context; } /** * st_drawing_area_get_surface_size: * @area: the #StDrawingArea * @width: (out): location to store the width of the painted area * @height: (out): location to store the height of the painted area * * Gets the size of the cairo surface being painted to, which is equal * to the size of the content area of the widget. This function must * only be called from a signal hander for the ::repaint signal. */ void st_drawing_area_get_surface_size (StDrawingArea *area, guint *width, guint *height) { StDrawingAreaPrivate *priv; g_return_if_fail (ST_IS_DRAWING_AREA (area)); g_return_if_fail (area->priv->in_repaint); priv = area->priv; if (width) *width = cogl_texture_get_width (priv->texture); if (height) *height = cogl_texture_get_height (priv->texture); } Cinnamon-2.8.6/src/st/st-shadow.c0000664000175000017500000001247412625666430015531 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-shadow.c: Boxed type holding for -st-shadow attributes * * Copyright 2009, 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "config.h" #include "st-shadow.h" /** * SECTION: st-shadow * @short_description: Boxed type for -st-shadow attributes * * #StShadow is a boxed type for storing attributes of the -st-shadow * property, modelled liberally after the CSS3 box-shadow property. * See http://www.css3.info/preview/box-shadow/ * */ /** * st_shadow_new: * @color: shadow's color * @xoffset: horizontal offset * @yoffset: vertical offset * @blur: blur radius * @spread: spread radius * @inset: whether the shadow should be inset * * Creates a new #StShadow * * Returns: the newly allocated shadow. Use st_shadow_free() when done */ StShadow * st_shadow_new (ClutterColor *color, gdouble xoffset, gdouble yoffset, gdouble blur, gdouble spread, gboolean inset) { StShadow *shadow; shadow = g_slice_new (StShadow); shadow->color = *color; shadow->xoffset = xoffset; shadow->yoffset = yoffset; shadow->blur = blur; shadow->spread = spread; shadow->inset = inset; shadow->ref_count = 1; return shadow; } /** * st_shadow_ref: * @shadow: a #StShadow * * Atomically increments the reference count of @shadow by one. * * Returns: the passed in #StShadow. */ StShadow * st_shadow_ref (StShadow *shadow) { g_return_val_if_fail (shadow != NULL, NULL); g_return_val_if_fail (shadow->ref_count > 0, shadow); g_atomic_int_inc (&shadow->ref_count); return shadow; } /** * st_shadow_unref: * @shadow: a #StShadow * * Atomically decrements the reference count of @shadow by one. * If the reference count drops to 0, all memory allocated by the * #StShadow is released. */ void st_shadow_unref (StShadow *shadow) { g_return_if_fail (shadow != NULL); g_return_if_fail (shadow->ref_count > 0); if (g_atomic_int_dec_and_test (&shadow->ref_count)) g_slice_free (StShadow, shadow); } /** * st_shadow_equal: * @shadow: a #StShadow * @other: a different #StShadow * * Check if two shadow objects are identical. Note that two shadows may * compare non-identically if they differ only by floating point rounding * errors. * * Return value: %TRUE if the two shadows are identical */ gboolean st_shadow_equal (StShadow *shadow, StShadow *other) { g_return_val_if_fail (shadow != NULL, FALSE); g_return_val_if_fail (other != NULL, FALSE); /* We use strict equality to compare double quantities; this means * that, for example, a shadow offset of 0.25in does not necessarily * compare equal to a shadow offset of 18pt in this test. Assume * that a few false negatives are mostly harmless. */ return (clutter_color_equal (&shadow->color, &other->color) && shadow->xoffset == other->xoffset && shadow->yoffset == other->yoffset && shadow->blur == other->blur && shadow->spread == other->spread && shadow->inset == other->inset); } /** * st_shadow_get_box: * @shadow: a #StShadow * @actor_box: the box allocated to a #ClutterAlctor * @shadow_box: computed box occupied by @shadow * * Gets the box used to paint @shadow, which will be partly * outside of @actor_box */ void st_shadow_get_box (StShadow *shadow, const ClutterActorBox *actor_box, ClutterActorBox *shadow_box) { g_return_if_fail (shadow != NULL); g_return_if_fail (actor_box != NULL); g_return_if_fail (shadow_box != NULL); /* Inset shadows are drawn below the border, so returning * the original box is not actually correct; still, it's * good enough for the purpose of determing additional space * required outside the actor box. */ if (shadow->inset) { *shadow_box = *actor_box; return; } shadow_box->x1 = actor_box->x1 + shadow->xoffset - shadow->blur - shadow->spread; shadow_box->x2 = actor_box->x2 + shadow->xoffset + shadow->blur + shadow->spread; shadow_box->y1 = actor_box->y1 + shadow->yoffset - shadow->blur - shadow->spread; shadow_box->y2 = actor_box->y2 + shadow->yoffset + shadow->blur + shadow->spread; } GType st_shadow_get_type (void) { static GType _st_shadow_type = 0; if (G_UNLIKELY (_st_shadow_type == 0)) _st_shadow_type = g_boxed_type_register_static ("StShadow", (GBoxedCopyFunc) st_shadow_ref, (GBoxedFreeFunc) st_shadow_unref); return _st_shadow_type; } Cinnamon-2.8.6/src/st/st-label.h0000664000175000017500000000444212625666430015324 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-label.h: Plain label actor * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_LABEL_H__ #define __ST_LABEL_H__ G_BEGIN_DECLS #include #define ST_TYPE_LABEL (st_label_get_type ()) #define ST_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_LABEL, StLabel)) #define ST_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_LABEL)) #define ST_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_LABEL, StLabelClass)) #define ST_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_LABEL)) #define ST_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_LABEL, StLabelClass)) typedef struct _StLabel StLabel; typedef struct _StLabelPrivate StLabelPrivate; typedef struct _StLabelClass StLabelClass; /** * StLabel: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _StLabel { /*< private >*/ StWidget parent_instance; StLabelPrivate *priv; }; struct _StLabelClass { StWidgetClass parent_class; }; GType st_label_get_type (void) G_GNUC_CONST; StWidget * st_label_new (const gchar *text); const gchar * st_label_get_text (StLabel *label); void st_label_set_text (StLabel *label, const gchar *text); ClutterActor * st_label_get_clutter_text (StLabel *label); G_END_DECLS #endif /* __ST_LABEL_H__ */ Cinnamon-2.8.6/src/st/test-theme.css0000664000175000017500000000213312625666430016234 0ustar fabiofabiostage { } #group1 { padding: 12pt; padding-right: 12px; padding-bottom: 2em; padding-left: 1in; background: #ff0000 url('some-background.png'); } #text1 { background-image: inherit; } .special-text { font-size: 24pt; font-style: italic; } ClutterTexture.special-text { font-weight: bold; } #text2 { background: inherit; background: none; /* also overrides the color */ } #group2 { font: italic 12px serif; } #text3 { font-variant: small-caps; font-weight: bold; font-style: oblique; font-size: 200%; } ClutterTexture { padding: 10px; } ClutterCairoTexture { padding-right: 20px; } #group1 > #text1 { color: #00ff00; } stage > #text2 { color: #ff0000; } #group2 { background-image: url('other-background.png'); padding: 1px 2px 3px 4px; border: 2px solid #000000; border-bottom: 5px solid #0000ff; border-radius: 10px 10px 0px 0px; } ClutterText:hover, StLabel:hover { text-decoration: underline; } ClutterText:visited, StLabel:visited { color: #888888; } StLabel:boxed { border: 1px; } Cinnamon-2.8.6/src/st/st-adjustment.h0000664000175000017500000001006412625666430016420 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-adjustment.h: Adjustment object * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_ADJUSTMENT_H__ #define __ST_ADJUSTMENT_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_ADJUSTMENT (st_adjustment_get_type()) #define ST_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ADJUSTMENT, StAdjustment)) #define ST_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ADJUSTMENT)) #define ST_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ADJUSTMENT, StAdjustmentClass)) #define ST_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ADJUSTMENT)) #define ST_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ADJUSTMENT, StAdjustmentClass)) typedef struct _StAdjustment StAdjustment; typedef struct _StAdjustmentPrivate StAdjustmentPrivate; typedef struct _StAdjustmentClass StAdjustmentClass; /** * StAdjustment: * * Class for handling an interval between to values. The contents of * the #StAdjustment are private and should be accessed using the * public API. */ struct _StAdjustment { /*< private >*/ GObject parent_instance; StAdjustmentPrivate *priv; }; /** * StAdjustmentClass: * @changed: Class handler for the ::changed signal. * * Base class for #StAdjustment. */ struct _StAdjustmentClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ void (* changed) (StAdjustment *adjustment); }; GType st_adjustment_get_type (void) G_GNUC_CONST; StAdjustment *st_adjustment_new (gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size); gdouble st_adjustment_get_value (StAdjustment *adjustment); void st_adjustment_set_value (StAdjustment *adjustment, gdouble value); void st_adjustment_clamp_page (StAdjustment *adjustment, gdouble lower, gdouble upper); void st_adjustment_set_values (StAdjustment *adjustment, gdouble value, gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size); void st_adjustment_get_values (StAdjustment *adjustment, gdouble *value, gdouble *lower, gdouble *upper, gdouble *step_increment, gdouble *page_increment, gdouble *page_size); G_END_DECLS #endif /* __ST_ADJUSTMENT_H__ */ Cinnamon-2.8.6/src/st/st-clipboard.c0000664000175000017500000002442612625666430016203 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-clipboard.c: clipboard object * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-clipboard * @short_description: a simple representation of the X clipboard * * #StCliboard is a very simple object representation of the clipboard * available to applications. Text is always assumed to be UTF-8 and non-text * items are not handled. */ #include "st-clipboard.h" #include #include #include #include G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT) #define CLIPBOARD_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_CLIPBOARD, StClipboardPrivate)) struct _StClipboardPrivate { Window clipboard_window; gchar *clipboard_text; Atom *supported_targets; gint n_targets; }; typedef struct _EventFilterData EventFilterData; struct _EventFilterData { StClipboard *clipboard; StClipboardCallbackFunc callback; gpointer user_data; }; static Atom __atom_clip = None; static Atom __utf8_string = None; static Atom __atom_targets = None; static void st_clipboard_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_clipboard_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_clipboard_dispose (GObject *object) { G_OBJECT_CLASS (st_clipboard_parent_class)->dispose (object); } static void st_clipboard_finalize (GObject *object) { StClipboardPrivate *priv = ((StClipboard *) object)->priv; g_free (priv->clipboard_text); priv->clipboard_text = NULL; g_free (priv->supported_targets); priv->supported_targets = NULL; priv->n_targets = 0; G_OBJECT_CLASS (st_clipboard_parent_class)->finalize (object); } static ClutterX11FilterReturn st_clipboard_provider (XEvent *xev, ClutterEvent *cev, StClipboard *clipboard) { XSelectionEvent notify_event; XSelectionRequestEvent *req_event; if (xev->type != SelectionRequest) return CLUTTER_X11_FILTER_CONTINUE; req_event = &xev->xselectionrequest; clutter_x11_trap_x_errors (); if (req_event->target == __atom_targets) { XChangeProperty (req_event->display, req_event->requestor, req_event->property, XA_ATOM, 32, PropModeReplace, (guchar*) clipboard->priv->supported_targets, clipboard->priv->n_targets); } else { XChangeProperty (req_event->display, req_event->requestor, req_event->property, req_event->target, 8, PropModeReplace, (guchar*) clipboard->priv->clipboard_text, strlen (clipboard->priv->clipboard_text)); } notify_event.type = SelectionNotify; notify_event.display = req_event->display; notify_event.requestor = req_event->requestor; notify_event.selection = req_event->selection; notify_event.target = req_event->target; notify_event.time = req_event->time; if (req_event->property == None) notify_event.property = req_event->target; else notify_event.property = req_event->property; /* notify the requestor that they have a copy of the selection */ XSendEvent (req_event->display, req_event->requestor, False, 0, (XEvent *) ¬ify_event); /* Make it happen non async */ XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */ return CLUTTER_X11_FILTER_REMOVE; } static void st_clipboard_class_init (StClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (StClipboardPrivate)); object_class->get_property = st_clipboard_get_property; object_class->set_property = st_clipboard_set_property; object_class->dispose = st_clipboard_dispose; object_class->finalize = st_clipboard_finalize; } static void st_clipboard_init (StClipboard *self) { Display *dpy; StClipboardPrivate *priv; priv = self->priv = CLIPBOARD_PRIVATE (self); priv->clipboard_window = XCreateSimpleWindow (clutter_x11_get_default_display (), clutter_x11_get_root_window (), -1, -1, 1, 1, 0, 0, 0); dpy = clutter_x11_get_default_display (); /* Only create once */ if (__atom_clip == None) __atom_clip = XInternAtom (dpy, "CLIPBOARD", 0); if (__utf8_string == None) __utf8_string = XInternAtom (dpy, "UTF8_STRING", 0); if (__atom_targets == None) __atom_targets = XInternAtom (dpy, "TARGETS", 0); priv->n_targets = 2; priv->supported_targets = g_new (Atom, priv->n_targets); priv->supported_targets[0] = __utf8_string; priv->supported_targets[1] = __atom_targets; clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_provider, self); } static ClutterX11FilterReturn st_clipboard_x11_event_filter (XEvent *xev, ClutterEvent *cev, EventFilterData *filter_data) { Atom actual_type; int actual_format, result; unsigned long nitems, bytes_after; unsigned char *data = NULL; if(xev->type != SelectionNotify) return CLUTTER_X11_FILTER_CONTINUE; if (xev->xselection.property == None) { /* clipboard empty */ filter_data->callback (filter_data->clipboard, NULL, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, filter_data); g_free (filter_data); return CLUTTER_X11_FILTER_REMOVE; } clutter_x11_trap_x_errors (); result = XGetWindowProperty (xev->xselection.display, xev->xselection.requestor, xev->xselection.property, 0L, G_MAXINT, True, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (clutter_x11_untrap_x_errors () || result != Success) { /* FIXME: handle failure better */ g_warning ("Clipboard: prop retrival failed"); } filter_data->callback (filter_data->clipboard, (char*) data, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, filter_data); g_free (filter_data); if (data) XFree (data); return CLUTTER_X11_FILTER_REMOVE; } /** * st_clipboard_get_default: * * Get the global #StClipboard object that represents the clipboard. * * Returns: (transfer none): a #StClipboard owned by St and must not be * unrefferenced or freed. */ StClipboard* st_clipboard_get_default (void) { static StClipboard *default_clipboard = NULL; if (!default_clipboard) { default_clipboard = g_object_new (ST_TYPE_CLIPBOARD, NULL); } return default_clipboard; } /** * st_clipboard_get_text: * @clipboard: A #StCliboard * @callback: (scope async): function to be called when the text is retreived * @user_data: data to be passed to the callback * * Request the data from the clipboard in text form. @callback is executed * when the data is retreived. * */ void st_clipboard_get_text (StClipboard *clipboard, StClipboardCallbackFunc callback, gpointer user_data) { EventFilterData *data; Display *dpy; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); g_return_if_fail (callback != NULL); data = g_new0 (EventFilterData, 1); data->clipboard = clipboard; data->callback = callback; data->user_data = user_data; clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, data); dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); /* safety on */ XConvertSelection (dpy, __atom_clip, __utf8_string, __utf8_string, clipboard->priv->clipboard_window, CurrentTime); clutter_x11_untrap_x_errors (); } /** * st_clipboard_set_text: * @clipboard: A #StClipboard * @text: text to copy to the clipboard * * Sets text as the current contents of the clipboard. * */ void st_clipboard_set_text (StClipboard *clipboard, const gchar *text) { StClipboardPrivate *priv; Display *dpy; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); g_return_if_fail (text != NULL); priv = clipboard->priv; /* make a copy of the text */ g_free (priv->clipboard_text); priv->clipboard_text = g_strdup (text); /* tell X we own the clipboard selection */ dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); } Cinnamon-2.8.6/src/st/st-box-layout-child.h0000664000175000017500000000435412625666430017433 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-box-layout-child.h: box layout child actor * * Copyright 2009 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef _ST_BOX_LAYOUT_CHILD_H #define _ST_BOX_LAYOUT_CHILD_H #include #include "st-enum-types.h" #include "st-box-layout.h" G_BEGIN_DECLS #define ST_TYPE_BOX_LAYOUT_CHILD st_box_layout_child_get_type() #define ST_BOX_LAYOUT_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChild)) #define ST_BOX_LAYOUT_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass)) #define ST_IS_BOX_LAYOUT_CHILD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_BOX_LAYOUT_CHILD)) #define ST_IS_BOX_LAYOUT_CHILD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_BOX_LAYOUT_CHILD)) #define ST_BOX_LAYOUT_CHILD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass)) typedef struct _StBoxLayoutChild StBoxLayoutChild; typedef struct _StBoxLayoutChildClass StBoxLayoutChildClass; typedef struct _StBoxLayoutChildPrivate StBoxLayoutChildPrivate; /** * StBoxLayoutChild: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _StBoxLayoutChild { /*< private >*/ ClutterChildMeta parent; gboolean expand; gboolean x_fill : 1; gboolean y_fill : 1; StAlign x_align; StAlign y_align; }; struct _StBoxLayoutChildClass { ClutterChildMetaClass parent_class; }; GType st_box_layout_child_get_type (void); G_END_DECLS #endif /* _ST_BOX_LAYOUT_CHILD_H */ Cinnamon-2.8.6/src/st/st-theme-context.h0000664000175000017500000000636312625666430017035 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-context.c: holds global information about a tree of styled objects * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_CONTEXT_H__ #define __ST_THEME_CONTEXT_H__ #include #include #include "st-theme-node.h" G_BEGIN_DECLS /** * SECTION:StThemeContext * @short_description: holds global information about a tree of styled objects * * #StThemeContext is responsible for managing information global to a tree of styled objects, * such as the set of stylesheets or the default font. In normal usage, a #StThemeContext * is bound to a #ClutterStage; a singleton #StThemeContext can be obtained for a #ClutterStage * by using st_theme_context_get_for_stage(). */ typedef struct _StThemeContextClass StThemeContextClass; #define ST_TYPE_THEME_CONTEXT (st_theme_context_get_type ()) #define ST_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_CONTEXT, StThemeContext)) #define ST_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_CONTEXT, StThemeContextClass)) #define ST_IS_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_CONTEXT)) #define ST_IS_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_CONTEXT)) #define ST_THEME_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_CONTEXT, StThemeContextClass)) GType st_theme_context_get_type (void) G_GNUC_CONST; StThemeContext *st_theme_context_new (void); StThemeContext *st_theme_context_get_for_stage (ClutterStage *stage); void st_theme_context_set_theme (StThemeContext *context, StTheme *theme); StTheme * st_theme_context_get_theme (StThemeContext *context); void st_theme_context_set_font (StThemeContext *context, const PangoFontDescription *font); const PangoFontDescription *st_theme_context_get_font (StThemeContext *context); StThemeNode * st_theme_context_get_root_node (StThemeContext *context); StThemeNode * st_theme_context_intern_node (StThemeContext *context, StThemeNode *node); G_END_DECLS #endif /* __ST_THEME_CONTEXT_H__ */ Cinnamon-2.8.6/src/st/st-widget-accessible.h0000664000175000017500000000435212625666430017623 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-widget-accessible.h: Accessible object for StWidget * * Copyright 2010 Igalia, S.L. * Author: Alejandro Piñeiro Iglesias * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_WIDGET_ACCESSIBLE_H__ #define __ST_WIDGET_ACCESSIBLE_H__ G_BEGIN_DECLS #include #include #define ST_TYPE_WIDGET_ACCESSIBLE st_widget_accessible_get_type () #define ST_WIDGET_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessible)) #define ST_IS_WIDGET_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_WIDGET_ACCESSIBLE)) #define ST_WIDGET_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass)) #define ST_IS_WIDGET_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_WIDGET_ACCESSIBLE)) #define ST_WIDGET_ACCESSIBLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass)) typedef struct _StWidgetAccessible StWidgetAccessible; typedef struct _StWidgetAccessibleClass StWidgetAccessibleClass; typedef struct _StWidgetAccessiblePrivate StWidgetAccessiblePrivate; struct _StWidgetAccessible { CallyActor parent; /*< private >*/ StWidgetAccessiblePrivate *priv; }; struct _StWidgetAccessibleClass { CallyActorClass parent_class; }; GType st_widget_accessible_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __ST_WIDGET_ACCESSIBLE_H__ */ Cinnamon-2.8.6/src/st/st-polygon.c0000664000175000017500000002675112625666430015736 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-polygon.c: A dynamically-sized Cairo drawing area * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-polygon * @short_description: A dynamically-sized Cairo drawing area * * #StPolygon is similar to #ClutterCairoTexture in that * it allows drawing via Cairo; the primary difference is that * it is dynamically sized. To use, connect to the #StPolygon::repaint * signal, and inside the signal handler, call * st_polygon_get_context() to get the Cairo context to draw to. The * #StPolygon::repaint signal will be emitted by default when the area is * resized or the CSS style changes; you can use the * st_polygon_queue_repaint() as well. */ #include "st-polygon.h" #include G_DEFINE_TYPE(StPolygon, st_polygon, CLUTTER_TYPE_ACTOR); struct _StPolygonPrivate { CoglHandle texture; CoglHandle material; cairo_t *context; guint needs_repaint : 1; guint in_repaint : 1; gfloat ulc_x; gfloat ulc_y; gfloat llc_x; gfloat llc_y; gfloat urc_x; gfloat urc_y; gfloat lrc_x; gfloat lrc_y; gboolean debug; }; /* Signals */ enum { REPAINT, LAST_SIGNAL }; static guint st_polygon_signals [LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_ULC_X, PROP_ULC_Y, PROP_LLC_X, PROP_LLC_Y, PROP_URC_X, PROP_URC_Y, PROP_LRC_X, PROP_LRC_Y, PROP_DEBUG, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static void st_polygon_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { StPolygon *actor = ST_POLYGON (object); StPolygonPrivate *priv = actor->priv; switch (prop_id) { case PROP_ULC_X: priv->ulc_x = g_value_get_float (value); break; case PROP_ULC_Y: priv->ulc_y = g_value_get_float (value); break; case PROP_LLC_X: priv->llc_x = g_value_get_float (value); break; case PROP_LLC_Y: priv->llc_y = g_value_get_float (value); break; case PROP_URC_X: priv->urc_x = g_value_get_float (value); break; case PROP_URC_Y: priv->urc_y = g_value_get_float (value); break; case PROP_LRC_X: priv->lrc_x = g_value_get_float (value); break; case PROP_LRC_Y: priv->lrc_y = g_value_get_float (value); break; case PROP_DEBUG: priv->debug = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_polygon_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { StPolygon *actor = ST_POLYGON (object); StPolygonPrivate *priv = actor->priv; switch (prop_id) { case PROP_ULC_X: g_value_set_float (value, priv->ulc_x); break; case PROP_ULC_Y: g_value_set_float (value, priv->ulc_y); break; case PROP_LLC_X: g_value_set_float (value, priv->llc_x); break; case PROP_LLC_Y: g_value_set_float (value, priv->llc_y); break; case PROP_URC_X: g_value_set_float (value, priv->urc_x); break; case PROP_URC_Y: g_value_set_float (value, priv->urc_y); break; case PROP_LRC_X: g_value_set_float (value, priv->lrc_x); break; case PROP_LRC_Y: g_value_set_float (value, priv->lrc_y); break; case PROP_DEBUG: g_value_set_boolean (value, priv->debug); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void st_polygon_paint (ClutterActor *self) { StPolygon *area = ST_POLYGON (self); StPolygonPrivate *priv = area->priv; if (priv->debug) { cogl_set_source_color4f (.50, .50, .50, .50); gfloat coords[8]; coords[0] = priv->ulc_x; coords[1] = priv->ulc_y; coords[2] = priv->llc_x; coords[3] = priv->llc_y; coords[4] = priv->lrc_x; coords[5] = priv->lrc_y; coords[6] = priv->urc_x; coords[7] = priv->urc_y; cogl_path_polygon ((float *)coords, 4); cogl_path_fill (); } } static void st_polygon_pick (ClutterActor *self, const ClutterColor *pick_color) { StPolygon *area = ST_POLYGON (self); StPolygonPrivate *priv = area->priv; if (!clutter_actor_should_pick_paint (self)) return; gfloat coords[8]; coords[0] = priv->ulc_x; coords[1] = priv->ulc_y; coords[2] = priv->llc_x; coords[3] = priv->llc_y; coords[4] = priv->lrc_x; coords[5] = priv->lrc_y; coords[6] = priv->urc_x; coords[7] = priv->urc_y; cogl_set_source_color4ub (pick_color->red, pick_color->green, pick_color->blue, pick_color->alpha); cogl_path_polygon ((float *)coords, 4); cogl_path_fill (); } static void st_polygon_class_init (StPolygonClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->set_property = st_polygon_set_property; gobject_class->get_property = st_polygon_get_property; actor_class->paint = st_polygon_paint; actor_class->pick = st_polygon_pick; st_polygon_signals[REPAINT] = g_signal_new ("repaint", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StPolygonClass, repaint), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 0); obj_props[PROP_ULC_X] = g_param_spec_float ("ulc-x", "Upper Left X coordinate", "Upper Left X coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_ULC_X, obj_props[PROP_ULC_X]); obj_props[PROP_ULC_Y] = g_param_spec_float ("ulc-y", "Upper Left Y coordinate", "Upper Left Y coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_ULC_Y, obj_props[PROP_ULC_Y]); obj_props[PROP_LLC_X] = g_param_spec_float ("llc-x", "Lower Left X coordinate", "Lower Left X coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LLC_X, obj_props[PROP_LLC_X]); obj_props[PROP_LLC_Y] = g_param_spec_float ("llc-y", "Lower Left Y coordinate", "Lower Left Y coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LLC_Y, obj_props[PROP_LLC_Y]); obj_props[PROP_URC_X] = g_param_spec_float ("urc-x", "Upper Right X coordinate", "Upper Right X coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_URC_X, obj_props[PROP_URC_X]); obj_props[PROP_URC_Y] = g_param_spec_float ("urc-y", "Upper Right Y coordinate", "Upper Right Y coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_URC_Y, obj_props[PROP_URC_Y]); obj_props[PROP_LRC_X] = g_param_spec_float ("lrc-x", "Lower Right X coordinate", "Lower Right X coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LRC_X, obj_props[PROP_LRC_X]); obj_props[PROP_LRC_Y] = g_param_spec_float ("lrc-y", "Lower Right Y coordinate", "Lower Right Y coordinate of the polygon", -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_LRC_Y, obj_props[PROP_LRC_Y]); obj_props[PROP_DEBUG] = g_param_spec_boolean ("debug", "Make polygon visible", "Make polygon visible to assist in debugging", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_DEBUG, obj_props[PROP_DEBUG]); g_type_class_add_private (gobject_class, sizeof (StPolygonPrivate)); } static void st_polygon_init (StPolygon *area) { area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, ST_TYPE_POLYGON, StPolygonPrivate); area->priv->texture = COGL_INVALID_HANDLE; area->priv->debug = FALSE; } /** * st_polygon_queue_repaint: * @area: the #StPolygon * * Will cause the actor to emit a ::repaint signal before it is next * drawn to the scene. Useful if some parameters for the area being * drawn other than the size or style have changed. Note that * clutter_actor_queue_redraw() will simply result in the same * contents being drawn to the scene again. */ void st_polygon_queue_repaint (StPolygon *area) { StPolygonPrivate *priv; g_return_if_fail (ST_IS_POLYGON (area)); priv = area->priv; priv->needs_repaint = TRUE; clutter_actor_queue_redraw (CLUTTER_ACTOR (area)); } Cinnamon-2.8.6/src/st/st-theme-node-drawing.c0000664000175000017500000022571612625666430017727 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node-drawing.c: Code to draw themed elements * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009, 2010 Florian Müllner * Copyright 2010 Intel Corporation. * * Contains code derived from: * rectangle.c: Rounded rectangle. * Copyright 2008 litl, LLC. * st-texture-frame.h: Expandible texture actor * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "st-shadow.h" #include "st-private.h" #include "st-theme-private.h" #include "st-theme-context.h" #include "st-texture-cache.h" #include "st-theme-node-private.h" #include "st-cogl-wrapper.h" /**** * Rounded corners ****/ typedef struct { ClutterColor color; ClutterColor border_color_1; ClutterColor border_color_2; guint radius; guint border_width_1; guint border_width_2; } StCornerSpec; static void elliptical_arc (cairo_t *cr, double x_center, double y_center, double x_radius, double y_radius, double angle1, double angle2) { cairo_save (cr); cairo_translate (cr, x_center, y_center); cairo_scale (cr, x_radius, y_radius); cairo_arc (cr, 0, 0, 1.0, angle1, angle2); cairo_restore (cr); } static CoglHandle create_corner_material (StCornerSpec *corner) { CoglHandle texture; cairo_t *cr; cairo_surface_t *surface; guint rowstride; guint8 *data; guint size; guint max_border_width; max_border_width = MAX(corner->border_width_2, corner->border_width_1); size = 2 * MAX(max_border_width, corner->radius); rowstride = size * 4; data = g_new0 (guint8, size * rowstride); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, size, size, rowstride); cr = cairo_create (surface); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_scale (cr, size, size); if (max_border_width <= corner->radius) { double x_radius, y_radius; if (max_border_width != 0) { cairo_set_source_rgba (cr, corner->border_color_1.red / 255., corner->border_color_1.green / 255., corner->border_color_1.blue / 255., corner->border_color_1.alpha / 255.); cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI); cairo_fill (cr); } cairo_set_source_rgba (cr, corner->color.red / 255., corner->color.green / 255., corner->color.blue / 255., corner->color.alpha / 255.); x_radius = 0.5 * (1.0 - (double) corner->border_width_2 / corner->radius); y_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius); /* TOPRIGHT */ elliptical_arc (cr, 0.5, 0.5, x_radius, y_radius, 3 * M_PI / 2, 2 * M_PI); /* BOTTOMRIGHT */ elliptical_arc (cr, 0.5, 0.5, x_radius, y_radius, 0, M_PI / 2); /* TOPLEFT */ elliptical_arc (cr, 0.5, 0.5, x_radius, y_radius, M_PI, 3 * M_PI / 2); /* BOTTOMLEFT */ elliptical_arc (cr, 0.5, 0.5, x_radius, y_radius, M_PI / 2, M_PI); cairo_fill (cr); } else { double radius; radius = (gdouble)corner->radius / max_border_width; cairo_set_source_rgba (cr, corner->border_color_1.red / 255., corner->border_color_1.green / 255., corner->border_color_1.blue / 255., corner->border_color_1.alpha / 255.); cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2); cairo_line_to (cr, 1.0 - radius, 0.0); cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI); cairo_line_to (cr, 1.0, 1.0 - radius); cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2); cairo_line_to (cr, radius, 1.0); cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI); cairo_fill (cr); } cairo_destroy (cr); cairo_surface_destroy (surface); texture = st_cogl_texture_new_from_data_wrapper (size, size, COGL_TEXTURE_NONE, CLUTTER_CAIRO_FORMAT_ARGB32, COGL_PIXEL_FORMAT_ANY, rowstride, data); g_free (data); g_assert (texture != COGL_INVALID_HANDLE); return texture; } static char * corner_to_string (StCornerSpec *corner) { return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u", corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha, corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha, corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha, corner->radius, corner->border_width_1, corner->border_width_2); } static CoglHandle load_corner (StTextureCache *cache, const char *key, void *datap, GError **error) { return create_corner_material ((StCornerSpec *) datap); } /* To match the CSS specification, we want the border to look like it was * drawn over the background. But actually drawing the border over the * background will produce slightly bad antialiasing at the edges, so * compute the effective border color instead. */ #define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8) #define MULT(c,a) NORM(c*a) static void premultiply (ClutterColor *color) { guint t; color->red = MULT (color->red, color->alpha); color->green = MULT (color->green, color->alpha); color->blue = MULT (color->blue, color->alpha); } static void unpremultiply (ClutterColor *color) { if (color->alpha != 0) { color->red = (color->red * 255 + 127) / color->alpha; color->green = (color->green * 255 + 127) / color->alpha; color->blue = (color->blue * 255 + 127) / color->alpha; } } static void over (const ClutterColor *source, const ClutterColor *destination, ClutterColor *result) { guint t; ClutterColor src = *source; ClutterColor dst = *destination; premultiply (&src); premultiply (&dst); result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha); result->red = src.red + NORM ((255 - src.alpha) * dst.red); result->green = src.green + NORM ((255 - src.alpha) * dst.green); result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue); unpremultiply (result); } /* * st_theme_node_reduce_border_radius: * @node: a #StThemeNode * @corners: (array length=4) (out): reduced corners * * Implements the corner overlap algorithm mentioned at * http://www.w3.org/TR/css3-background/#corner-overlap */ static void st_theme_node_reduce_border_radius (StThemeNode *node, guint *corners) { gfloat scale; guint sum; scale = 1.0; /* top */ sum = node->border_radius[ST_CORNER_TOPLEFT] + node->border_radius[ST_CORNER_TOPRIGHT]; if (sum > 0) scale = MIN (node->alloc_width / sum, scale); /* right */ sum = node->border_radius[ST_CORNER_TOPRIGHT] + node->border_radius[ST_CORNER_BOTTOMRIGHT]; if (sum > 0) scale = MIN (node->alloc_height / sum, scale); /* bottom */ sum = node->border_radius[ST_CORNER_BOTTOMLEFT] + node->border_radius[ST_CORNER_BOTTOMRIGHT]; if (sum > 0) scale = MIN (node->alloc_width / sum, scale); /* left */ sum = node->border_radius[ST_CORNER_BOTTOMLEFT] + node->border_radius[ST_CORNER_TOPLEFT]; if (sum > 0) scale = MIN (node->alloc_height / sum, scale); corners[ST_CORNER_TOPLEFT] = node->border_radius[ST_CORNER_TOPLEFT] * scale; corners[ST_CORNER_TOPRIGHT] = node->border_radius[ST_CORNER_TOPRIGHT] * scale; corners[ST_CORNER_BOTTOMLEFT] = node->border_radius[ST_CORNER_BOTTOMLEFT] * scale; corners[ST_CORNER_BOTTOMRIGHT] = node->border_radius[ST_CORNER_BOTTOMRIGHT] * scale; } static void st_theme_node_get_corner_border_widths (StThemeNode *node, StCorner corner_id, guint *border_width_1, guint *border_width_2) { switch (corner_id) { case ST_CORNER_TOPLEFT: if (border_width_1) *border_width_1 = node->border_width[ST_SIDE_TOP]; if (border_width_2) *border_width_2 = node->border_width[ST_SIDE_LEFT]; break; case ST_CORNER_TOPRIGHT: if (border_width_1) *border_width_1 = node->border_width[ST_SIDE_TOP]; if (border_width_2) *border_width_2 = node->border_width[ST_SIDE_RIGHT]; break; case ST_CORNER_BOTTOMRIGHT: if (border_width_1) *border_width_1 = node->border_width[ST_SIDE_BOTTOM]; if (border_width_2) *border_width_2 = node->border_width[ST_SIDE_RIGHT]; break; case ST_CORNER_BOTTOMLEFT: if (border_width_1) *border_width_1 = node->border_width[ST_SIDE_BOTTOM]; if (border_width_2) *border_width_2 = node->border_width[ST_SIDE_LEFT]; break; } } static CoglHandle st_theme_node_lookup_corner (StThemeNode *node, StCorner corner_id) { CoglHandle texture, material; char *key; StTextureCache *cache; StCornerSpec corner; guint radius[4]; cache = st_texture_cache_get_default (); st_theme_node_reduce_border_radius (node, radius); if (radius[corner_id] == 0) return COGL_INVALID_HANDLE; corner.radius = radius[corner_id]; corner.color = node->background_color; st_theme_node_get_corner_border_widths (node, corner_id, &corner.border_width_1, &corner.border_width_2); switch (corner_id) { case ST_CORNER_TOPLEFT: over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1); over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2); break; case ST_CORNER_TOPRIGHT: over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1); over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2); break; case ST_CORNER_BOTTOMRIGHT: over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1); over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2); break; case ST_CORNER_BOTTOMLEFT: over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1); over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2); break; } if (corner.color.alpha == 0 && corner.border_color_1.alpha == 0 && corner.border_color_2.alpha == 0) return COGL_INVALID_HANDLE; key = corner_to_string (&corner); texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_NONE, load_corner, &corner, NULL); material = _st_create_texture_material (texture); cogl_handle_unref (texture); g_free (key); return material; } static void get_background_scale (StThemeNode *node, gdouble painting_area_width, gdouble painting_area_height, gdouble background_image_width, gdouble background_image_height, gdouble *scale_w, gdouble *scale_h) { *scale_w = -1.0; *scale_h = -1.0; switch (node->background_size) { case ST_BACKGROUND_SIZE_AUTO: *scale_w = 1.0; break; case ST_BACKGROUND_SIZE_CONTAIN: if (background_image_width > background_image_height) *scale_w = painting_area_width / background_image_width; else *scale_w = painting_area_height / background_image_height; break; case ST_BACKGROUND_SIZE_COVER: if (background_image_width < background_image_height) *scale_w = painting_area_width / background_image_width; else *scale_w = painting_area_height / background_image_height; break; case ST_BACKGROUND_SIZE_FIXED: if (node->background_size_w > -1) { *scale_w = node->background_size_w / background_image_width; if (node->background_size_h > -1) *scale_h = node->background_size_h / background_image_height; } else if (node->background_size_h > -1) *scale_w = node->background_size_h / background_image_height; break; } if (*scale_h < 0.0) *scale_h = *scale_w; } static void get_background_coordinates (StThemeNode *node, gdouble painting_area_width, gdouble painting_area_height, gdouble background_image_width, gdouble background_image_height, gdouble *x, gdouble *y) { /* honor the specified position if any */ if (node->background_position_set) { *x = node->background_position_x; *y = node->background_position_y; } else { /* center the background on the widget */ *x = (painting_area_width / 2.0) - (background_image_width / 2.0); *y = (painting_area_height / 2.0) - (background_image_height / 2.0); } } static void get_background_position (StThemeNode *self, const ClutterActorBox *allocation, ClutterActorBox *result, ClutterActorBox *texture_coords) { gdouble painting_area_width, painting_area_height; gdouble background_image_width, background_image_height; gdouble x1, y1; gdouble scale_w, scale_h; /* get the background image size */ background_image_width = cogl_texture_get_width (self->background_texture); background_image_height = cogl_texture_get_height (self->background_texture); /* get the painting area size */ painting_area_width = allocation->x2 - allocation->x1; painting_area_height = allocation->y2 - allocation->y1; /* scale if requested */ get_background_scale (self, painting_area_width, painting_area_height, background_image_width, background_image_height, &scale_w, &scale_h); background_image_width *= scale_w; background_image_height *= scale_h; /* get coordinates */ get_background_coordinates (self, painting_area_width, painting_area_height, background_image_width, background_image_height, &x1, &y1); if (self->background_repeat) { gdouble width = allocation->x2 - allocation->x1 + x1; gdouble height = allocation->y2 - allocation->y1 + y1; *result = *allocation; /* reference image is at x1, y1 */ texture_coords->x1 = x1 / background_image_width; texture_coords->y1 = y1 / background_image_height; texture_coords->x2 = width / background_image_width; texture_coords->y2 = height / background_image_height; } else { result->x1 = x1; result->y1 = y1; result->x2 = x1 + background_image_width; result->y2 = y1 + background_image_height; texture_coords->x1 = texture_coords->y1 = 0; texture_coords->x2 = texture_coords->y2 = 1; } } /* Use of this function marks code which doesn't support * non-uniform colors. */ static void get_arbitrary_border_color (StThemeNode *node, ClutterColor *color) { if (color) st_theme_node_get_border_color (node, ST_SIDE_TOP, color); } static gboolean st_theme_node_has_visible_outline (StThemeNode *node) { if (node->background_color.alpha > 0) return TRUE; if (node->background_gradient_end.alpha > 0) return TRUE; if (node->border_radius[ST_CORNER_TOPLEFT] > 0 || node->border_radius[ST_CORNER_TOPRIGHT] > 0 || node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 || node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0) return TRUE; if (node->border_width[ST_SIDE_TOP] > 0 || node->border_width[ST_SIDE_LEFT] > 0 || node->border_width[ST_SIDE_RIGHT] > 0 || node->border_width[ST_SIDE_BOTTOM] > 0) return TRUE; return FALSE; } static cairo_pattern_t * create_cairo_pattern_of_background_gradient (StThemeNode *node) { cairo_pattern_t *pattern; g_return_val_if_fail (node->background_gradient_type != ST_GRADIENT_NONE, NULL); if (node->background_gradient_type == ST_GRADIENT_VERTICAL) pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height); else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL) pattern = cairo_pattern_create_linear (0, 0, node->alloc_width, 0); else { gdouble cx, cy; cx = node->alloc_width / 2.; cy = node->alloc_height / 2.; pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy)); } cairo_pattern_add_color_stop_rgba (pattern, 0, node->background_color.red / 255., node->background_color.green / 255., node->background_color.blue / 255., node->background_color.alpha / 255.); cairo_pattern_add_color_stop_rgba (pattern, 1, node->background_gradient_end.red / 255., node->background_gradient_end.green / 255., node->background_gradient_end.blue / 255., node->background_gradient_end.alpha / 255.); return pattern; } static cairo_pattern_t * create_cairo_pattern_of_background_image (StThemeNode *node, gboolean *needs_background_fill) { cairo_surface_t *surface; cairo_pattern_t *pattern; cairo_content_t content; cairo_matrix_t matrix; const char *file; StTextureCache *texture_cache; gdouble background_image_width, background_image_height; gdouble x, y; gdouble scale_w, scale_h; file = st_theme_node_get_background_image (node); texture_cache = st_texture_cache_get_default (); surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file); if (surface == NULL) return NULL; g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE); content = cairo_surface_get_content (surface); pattern = cairo_pattern_create_for_surface (surface); background_image_width = cairo_image_surface_get_width (surface); background_image_height = cairo_image_surface_get_height (surface); *needs_background_fill = TRUE; cairo_matrix_init_identity (&matrix); get_background_scale (node, node->alloc_width, node->alloc_height, background_image_width, background_image_height, &scale_w, &scale_h); if ((scale_w != 1) || (scale_h != 1)) cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h); background_image_width *= scale_w; background_image_height *= scale_h; get_background_coordinates (node, node->alloc_width, node->alloc_height, background_image_width, background_image_height, &x, &y); cairo_matrix_translate (&matrix, -x, -y); if (node->background_repeat) cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); /* If it's opaque, fills up the entire allocated * area, then don't bother doing a background fill first */ if (content != CAIRO_CONTENT_COLOR_ALPHA) { if (node->background_repeat || (x >= 0 && y >= 0 && background_image_width - x >= node->alloc_width && background_image_height -y >= node->alloc_height)) *needs_background_fill = FALSE; } cairo_pattern_set_matrix (pattern, &matrix); return pattern; } /* fill_exterior = TRUE means that pattern is a surface pattern and * we should extend the pattern with a solid fill from its edges. * This is a bit of a hack; the alternative would be to make the * surface of the surface pattern 1 pixel bigger and use CAIRO_EXTEND_PAD. */ static void paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec, cairo_pattern_t *pattern, gboolean fill_exterior, cairo_t *cr, cairo_path_t *interior_path, cairo_path_t *outline_path) { /* If there are borders, clip the shadow to the interior * of the borders; if there is a visible outline, clip the shadow to * that outline */ cairo_path_t *path = (interior_path != NULL) ? interior_path : outline_path; double x1, x2, y1, y2; /* fill_exterior only makes sense if we're clipping the shadow - filling * to the edges of the surface would be silly */ g_assert (!(fill_exterior && path == NULL)); cairo_save (cr); if (path != NULL) { cairo_append_path (cr, path); /* There's no way to invert a path in cairo, so we need bounds for * the area we are drawing in order to create the "exterior" region. * Pixel align to hit fast paths. */ if (fill_exterior) { cairo_path_extents (cr, &x1, &y1, &x2, &y2); x1 = floor (x1); y1 = floor (y1); x2 = ceil (x2); y2 = ceil (y2); } cairo_clip (cr); } cairo_set_source_rgba (cr, shadow_spec->color.red / 255.0, shadow_spec->color.green / 255.0, shadow_spec->color.blue / 255.0, shadow_spec->color.alpha / 255.0); if (fill_exterior) { cairo_surface_t *surface; int width, height; cairo_matrix_t matrix; cairo_save (cr); /* Start with a rectangle enclosing the bounds of the clipped * region */ cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1); /* Then subtract out the bounds of the surface in the surface * pattern; we transform the context by the inverse of the * pattern matrix to get to surface coordinates */ cairo_pattern_get_surface (pattern, &surface); width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); cairo_pattern_get_matrix (pattern, &matrix); cairo_matrix_invert (&matrix); cairo_transform (cr, &matrix); cairo_rectangle (cr, 0, height, width, - height); cairo_fill (cr); cairo_restore (cr); } cairo_mask (cr, pattern); cairo_restore (cr); } static void paint_background_image_shadow_to_cairo_context (StThemeNode *node, StShadow *shadow_spec, cairo_pattern_t *pattern, cairo_t *cr, cairo_path_t *interior_path, cairo_path_t *outline_path, int x, int y, int width, int height) { cairo_pattern_t *shadow_pattern; g_assert (shadow_spec != NULL); g_assert (pattern != NULL); if (outline_path != NULL) { cairo_surface_t *clipped_surface; cairo_pattern_t *clipped_pattern; cairo_t *temp_cr; /* Prerender the pattern to a temporary surface, * so it's properly clipped before we create a shadow from it */ clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); temp_cr = cairo_create (clipped_surface); cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR); cairo_paint (temp_cr); cairo_set_operator (temp_cr, CAIRO_OPERATOR_SOURCE); if (interior_path != NULL) { cairo_append_path (temp_cr, interior_path); cairo_clip (temp_cr); } cairo_append_path (temp_cr, outline_path); cairo_translate (temp_cr, x, y); cairo_set_source (temp_cr, pattern); cairo_clip (temp_cr); cairo_paint (temp_cr); cairo_destroy (temp_cr); clipped_pattern = cairo_pattern_create_for_surface (clipped_surface); cairo_surface_destroy (clipped_surface); shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec, clipped_pattern); cairo_pattern_destroy (clipped_pattern); } else { shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec, pattern); } paint_shadow_pattern_to_cairo_context (shadow_spec, shadow_pattern, FALSE, cr, interior_path, outline_path); cairo_pattern_destroy (shadow_pattern); } /* gets the extents of a cairo_path_t; slightly inefficient, but much simpler than * computing from the raw path data */ static void path_extents (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_surface_t *dummy = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1); cairo_t *cr = cairo_create (dummy); cairo_append_path (cr, path); cairo_path_extents (cr, x1, y1, x2, y2); cairo_destroy (cr); cairo_surface_destroy (dummy); } static void paint_inset_box_shadow_to_cairo_context (StThemeNode *node, StShadow *shadow_spec, cairo_t *cr, cairo_path_t *shadow_outline) { cairo_surface_t *shadow_surface; cairo_pattern_t *shadow_pattern; double extents_x1, extents_y1, extents_x2, extents_y2; double shrunk_extents_x1, shrunk_extents_y1, shrunk_extents_x2, shrunk_extents_y2; gboolean fill_exterior; g_assert (shadow_spec != NULL); g_assert (shadow_outline != NULL); /* Create the pattern used to create the inset shadow; as the shadow * should be drawn as if everything outside the outline was opaque, * we use a temporary surface to draw the background as a solid shape, * which is inverted when creating the shadow pattern. */ /* First we need to find the size of the temporary surface */ path_extents (shadow_outline, &extents_x1, &extents_y1, &extents_x2, &extents_y2); /* Shrink the extents by the spread, and offset */ shrunk_extents_x1 = extents_x1 + shadow_spec->xoffset + shadow_spec->spread; shrunk_extents_y1 = extents_y1 + shadow_spec->yoffset + shadow_spec->spread; shrunk_extents_x2 = extents_x2 + shadow_spec->xoffset - shadow_spec->spread; shrunk_extents_y2 = extents_y2 + shadow_spec->yoffset - shadow_spec->spread; if (shrunk_extents_x1 >= shrunk_extents_x2 || shrunk_extents_y1 >= shrunk_extents_x2) { /* Shadow occupies entire area within border */ shadow_pattern = cairo_pattern_create_rgb (0., 0., 0.); fill_exterior = FALSE; } else { /* Bounds of temporary surface */ int surface_x = floor (shrunk_extents_x1); int surface_y = floor (shrunk_extents_y1); int surface_width = ceil (shrunk_extents_x2) - surface_x; int surface_height = ceil (shrunk_extents_y2) - surface_y; /* Center of the original path */ double x_center = (extents_x1 + extents_x2) / 2; double y_center = (extents_y1 + extents_y2) / 2; cairo_pattern_t *pattern; cairo_t *temp_cr; cairo_matrix_t matrix; shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height); temp_cr = cairo_create (shadow_surface); /* Match the coordinates in the temporary context to the parent context */ cairo_translate (temp_cr, - surface_x, - surface_y); /* Shadow offset */ cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset); /* Scale the path around the center to match the shrunk bounds */ cairo_translate (temp_cr, x_center, y_center); cairo_scale (temp_cr, (shrunk_extents_x2 - shrunk_extents_x1) / (extents_x2 - extents_x1), (shrunk_extents_y2 - shrunk_extents_y1) / (extents_y2 - extents_y1)); cairo_translate (temp_cr, - x_center, - y_center); cairo_append_path (temp_cr, shadow_outline); cairo_fill (temp_cr); cairo_destroy (temp_cr); pattern = cairo_pattern_create_for_surface (shadow_surface); cairo_surface_destroy (shadow_surface); /* The pattern needs to be offset back to coordinates in the parent context */ cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); cairo_pattern_set_matrix (pattern, &matrix); shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec, pattern); fill_exterior = TRUE; cairo_pattern_destroy (pattern); } paint_shadow_pattern_to_cairo_context (shadow_spec, shadow_pattern, fill_exterior, cr, shadow_outline, NULL); cairo_pattern_destroy (shadow_pattern); } /* In order for borders to be smoothly blended with non-solid backgrounds, * we need to use cairo. This function is a slow fallback path for those * cases (gradients, background images, etc). */ static CoglHandle st_theme_node_prerender_background (StThemeNode *node) { StBorderImage *border_image; CoglHandle texture; guint radius[4]; int i; cairo_t *cr; cairo_surface_t *surface; StShadow *shadow_spec; StShadow *box_shadow_spec; cairo_pattern_t *pattern = NULL; cairo_path_t *outline_path = NULL; gboolean draw_solid_background = TRUE; gboolean background_is_translucent; gboolean interior_dirty; gboolean draw_background_image_shadow = FALSE; gboolean has_visible_outline; ClutterColor border_color; int border_width[4]; guint rowstride; guchar *data; ClutterActorBox actor_box; ClutterActorBox paint_box; cairo_path_t *interior_path = NULL; border_image = st_theme_node_get_border_image (node); shadow_spec = st_theme_node_get_background_image_shadow (node); box_shadow_spec = st_theme_node_get_box_shadow (node); actor_box.x1 = 0; actor_box.x2 = node->alloc_width; actor_box.y1 = 0; actor_box.y2 = node->alloc_height; /* If there's a background image shadow, we * may need to create an image bigger than the nodes * allocation */ st_theme_node_get_background_paint_box (node, &actor_box, &paint_box); /* translate the boxes so the paint box is at 0,0 */ actor_box.x1 += - paint_box.x1; actor_box.x2 += - paint_box.x1; actor_box.y1 += - paint_box.y1; actor_box.y2 += - paint_box.y1; paint_box.x2 += - paint_box.x1; paint_box.x1 += - paint_box.x1; paint_box.y2 += - paint_box.y1; paint_box.y1 += - paint_box.y1; rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, paint_box.x2 - paint_box.x1); data = g_new0 (guchar, (paint_box.y2 - paint_box.y1) * rowstride); /* We zero initialize the destination memory, so it's fully transparent * by default. */ interior_dirty = FALSE; surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, paint_box.x2 - paint_box.x1, paint_box.y2 - paint_box.y1, rowstride); cr = cairo_create (surface); /* TODO - support non-uniform border colors */ get_arbitrary_border_color (node, &border_color); st_theme_node_reduce_border_radius (node, radius); for (i = 0; i < 4; i++) border_width[i] = st_theme_node_get_border_width (node, i); /* Note we don't support translucent background images on top * of gradients. It's strictly either/or. */ if (node->background_gradient_type != ST_GRADIENT_NONE) { pattern = create_cairo_pattern_of_background_gradient (node); draw_solid_background = FALSE; /* If the gradient has any translucent areas, we need to * erase the interior region before drawing, so that we show * what's actually under the gradient and not whatever is * left over from filling the border, etc. */ if (node->background_color.alpha < 255 || node->background_gradient_end.alpha < 255) background_is_translucent = TRUE; else background_is_translucent = FALSE; } else { const char *background_image; background_image = st_theme_node_get_background_image (node); if (background_image != NULL) { pattern = create_cairo_pattern_of_background_image (node, &draw_solid_background); if (shadow_spec && pattern != NULL) draw_background_image_shadow = TRUE; } /* We never need to clear the interior region before drawing the * background image, because it either always fills the entire area * opaquely, or we draw the solid background behind it. */ background_is_translucent = FALSE; } if (pattern == NULL) draw_solid_background = TRUE; /* drawing the solid background implicitly clears the interior * region, so if we're going to draw a solid background before drawing * the background pattern, then we don't need to bother also clearing the * background region. */ if (draw_solid_background) background_is_translucent = FALSE; has_visible_outline = st_theme_node_has_visible_outline (node); /* Create a path for the background's outline first */ if (radius[ST_CORNER_TOPLEFT] > 0) cairo_arc (cr, actor_box.x1 + radius[ST_CORNER_TOPLEFT], actor_box.y1 + radius[ST_CORNER_TOPLEFT], radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2); else cairo_move_to (cr, actor_box.x1, actor_box.y1); cairo_line_to (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1); if (radius[ST_CORNER_TOPRIGHT] > 0) cairo_arc (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1 + radius[ST_CORNER_TOPRIGHT], radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI); cairo_line_to (cr, actor_box.x2, actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT]); if (radius[ST_CORNER_BOTTOMRIGHT] > 0) cairo_arc (cr, actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT], actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT], radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2); cairo_line_to (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2); if (radius[ST_CORNER_BOTTOMLEFT] > 0) cairo_arc (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT], radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI); cairo_close_path (cr); outline_path = cairo_copy_path (cr); /* If we have a solid border, we fill the outline shape with the border * color and create the inline shape for the background; * otherwise the outline shape is filled with the background * directly */ if (border_image == NULL && (border_width[ST_SIDE_TOP] > 0 || border_width[ST_SIDE_RIGHT] > 0 || border_width[ST_SIDE_BOTTOM] > 0 || border_width[ST_SIDE_LEFT] > 0)) { cairo_set_source_rgba (cr, border_color.red / 255., border_color.green / 255., border_color.blue / 255., border_color.alpha / 255.); cairo_fill (cr); /* We were sloppy when filling in the border, and now the interior * is filled with the border color, too. */ interior_dirty = TRUE; if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP], border_width[ST_SIDE_LEFT])) elliptical_arc (cr, actor_box.x1 + radius[ST_CORNER_TOPLEFT], actor_box.y1 + radius[ST_CORNER_TOPLEFT], radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT], radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP], M_PI, 3 * M_PI / 2); else cairo_move_to (cr, actor_box.x1 + border_width[ST_SIDE_LEFT], actor_box.y1 + border_width[ST_SIDE_TOP]); cairo_line_to (cr, actor_box.x2 - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]), actor_box.y1 + border_width[ST_SIDE_TOP]); if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP], border_width[ST_SIDE_RIGHT])) elliptical_arc (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.y1 + radius[ST_CORNER_TOPRIGHT], radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT], radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP], 3 * M_PI / 2, 2 * M_PI); else cairo_line_to (cr, actor_box.x2 - border_width[ST_SIDE_RIGHT], actor_box.y1 + border_width[ST_SIDE_TOP]); cairo_line_to (cr, actor_box.x2 - border_width[ST_SIDE_RIGHT], actor_box.y2 - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM])); if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM], border_width[ST_SIDE_RIGHT])) elliptical_arc (cr, actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT], actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT], radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT], radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM], 0, M_PI / 2); else cairo_line_to (cr, actor_box.x2 - border_width[ST_SIDE_RIGHT], actor_box.y2 - border_width[ST_SIDE_BOTTOM]); cairo_line_to (cr, MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]), actor_box.y2 - border_width[ST_SIDE_BOTTOM]); if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM], border_width[ST_SIDE_LEFT])) elliptical_arc (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT], radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT], radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM], M_PI / 2, M_PI); else cairo_line_to (cr, actor_box.x1 + border_width[ST_SIDE_LEFT], actor_box.y2 - border_width[ST_SIDE_BOTTOM]); cairo_close_path (cr); interior_path = cairo_copy_path (cr); /* clip drawing to the region inside of the borders */ cairo_clip (cr); /* But fill the pattern as if it started at the edge of outline, * behind the borders. This is similar to * background-clip: border-box; semantics. */ cairo_append_path (cr, outline_path); } if (interior_dirty && background_is_translucent) { cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_fill_preserve (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); } if (draw_solid_background) { cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba (cr, node->background_color.red / 255., node->background_color.green / 255., node->background_color.blue / 255., node->background_color.alpha / 255.); cairo_fill_preserve (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); } if (draw_background_image_shadow) { paint_background_image_shadow_to_cairo_context (node, shadow_spec, pattern, cr, interior_path, has_visible_outline? outline_path : NULL, actor_box.x1, actor_box.y1, paint_box.x2 - paint_box.x1, paint_box.y2 - paint_box.y1); cairo_append_path (cr, outline_path); } cairo_translate (cr, actor_box.x1, actor_box.y1); if (pattern != NULL) { cairo_set_source (cr, pattern); cairo_fill (cr); cairo_pattern_destroy (pattern); } if (box_shadow_spec && box_shadow_spec->inset) { paint_inset_box_shadow_to_cairo_context (node, box_shadow_spec, cr, interior_path ? interior_path : outline_path); } if (outline_path != NULL) cairo_path_destroy (outline_path); if (interior_path != NULL) cairo_path_destroy (interior_path); texture = st_cogl_texture_new_from_data_wrapper (paint_box.x2 - paint_box.x1, paint_box.y2 - paint_box.y1, COGL_TEXTURE_NONE, CLUTTER_CAIRO_FORMAT_ARGB32, COGL_PIXEL_FORMAT_ANY, rowstride, data); cairo_destroy (cr); cairo_surface_destroy (surface); g_free (data); return texture; } void _st_theme_node_free_drawing_state (StThemeNode *node) { int corner_id; if (node->background_texture != COGL_INVALID_HANDLE) cogl_handle_unref (node->background_texture); if (node->background_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->background_material); if (node->background_shadow_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->background_shadow_material); if (node->border_slices_texture != COGL_INVALID_HANDLE) cogl_handle_unref (node->border_slices_texture); if (node->border_slices_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->border_slices_material); if (node->prerendered_texture != COGL_INVALID_HANDLE) cogl_handle_unref (node->prerendered_texture); if (node->prerendered_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->prerendered_material); if (node->box_shadow_material != COGL_INVALID_HANDLE) cogl_handle_unref (node->box_shadow_material); for (corner_id = 0; corner_id < 4; corner_id++) if (node->corner_material[corner_id] != COGL_INVALID_HANDLE) cogl_handle_unref (node->corner_material[corner_id]); _st_theme_node_init_drawing_state (node); } void _st_theme_node_init_drawing_state (StThemeNode *node) { int corner_id; node->background_texture = COGL_INVALID_HANDLE; node->background_material = COGL_INVALID_HANDLE; node->background_shadow_material = COGL_INVALID_HANDLE; node->box_shadow_material = COGL_INVALID_HANDLE; node->border_slices_texture = COGL_INVALID_HANDLE; node->border_slices_material = COGL_INVALID_HANDLE; node->prerendered_texture = COGL_INVALID_HANDLE; node->prerendered_material = COGL_INVALID_HANDLE; for (corner_id = 0; corner_id < 4; corner_id++) node->corner_material[corner_id] = COGL_INVALID_HANDLE; } static void st_theme_node_paint_borders (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity); static void st_theme_node_render_resources (StThemeNode *node, float width, float height) { StTextureCache *texture_cache; StBorderImage *border_image; gboolean has_border; gboolean has_border_radius; gboolean has_inset_box_shadow; gboolean has_large_corners; StShadow *box_shadow_spec; StShadow *background_image_shadow_spec; const char *background_image; g_return_if_fail (width > 0 && height > 0); texture_cache = st_texture_cache_get_default (); /* FIXME - need to separate this into things that need to be recomputed on * geometry change versus things that can be cached regardless, such as * a background image. */ _st_theme_node_free_drawing_state (node); node->alloc_width = width; node->alloc_height = height; _st_theme_node_ensure_background (node); _st_theme_node_ensure_geometry (node); box_shadow_spec = st_theme_node_get_box_shadow (node); has_inset_box_shadow = box_shadow_spec && box_shadow_spec->inset; if (node->border_width[ST_SIDE_TOP] > 0 || node->border_width[ST_SIDE_LEFT] > 0 || node->border_width[ST_SIDE_RIGHT] > 0 || node->border_width[ST_SIDE_BOTTOM] > 0) has_border = TRUE; else has_border = FALSE; if (node->border_radius[ST_CORNER_TOPLEFT] > 0 || node->border_radius[ST_CORNER_TOPRIGHT] > 0 || node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 || node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0) has_border_radius = TRUE; else has_border_radius = FALSE; /* The cogl code pads each corner to the maximum border radius, * which results in overlapping corner areas if the radius * exceeds the actor's halfsize, causing rendering errors. * Fall back to cairo in these cases. */ has_large_corners = FALSE; if (has_border_radius) { guint border_radius[4]; int corner; st_theme_node_reduce_border_radius (node, border_radius); for (corner = 0; corner < 4; corner ++) { if (border_radius[corner] * 2 > height || border_radius[corner] * 2 > width) { has_large_corners = TRUE; break; } } } /* Load referenced images from disk and draw anything we need with cairo now */ background_image = st_theme_node_get_background_image (node); border_image = st_theme_node_get_border_image (node); if (border_image) { const char *filename; filename = st_border_image_get_filename (border_image); node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename); } if (node->border_slices_texture) node->border_slices_material = _st_create_texture_material (node->border_slices_texture); else node->border_slices_material = COGL_INVALID_HANDLE; /* Use cairo to prerender the node if there is a gradient, or * background image with borders and/or rounded corners, * or large corners, since we can't do those things * easily with cogl. * * FIXME: if we could figure out ahead of time that a * background image won't overlap with the node borders, * then we could use cogl for that case. */ if ((node->background_gradient_type != ST_GRADIENT_NONE) || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0)) || (background_image && (has_border || has_border_radius)) || has_large_corners) node->prerendered_texture = st_theme_node_prerender_background (node); if (node->prerendered_texture) node->prerendered_material = _st_create_texture_material (node->prerendered_texture); else node->prerendered_material = COGL_INVALID_HANDLE; if (box_shadow_spec && !has_inset_box_shadow) { if (node->border_slices_texture != COGL_INVALID_HANDLE) node->box_shadow_material = _st_create_shadow_material (box_shadow_spec, node->border_slices_texture); else if (node->prerendered_texture != COGL_INVALID_HANDLE) node->box_shadow_material = _st_create_shadow_material (box_shadow_spec, node->prerendered_texture); else if (node->background_color.alpha > 0 || has_border) { CoglHandle buffer, offscreen; int texture_width = ceil (width); int texture_height = ceil (height); buffer = st_cogl_texture_new_with_size_wrapper (texture_width, texture_height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); offscreen = cogl_offscreen_new_to_texture (buffer); if (offscreen != COGL_INVALID_HANDLE) { ClutterActorBox box = { 0, 0, width, height }; CoglColor clear_color; cogl_push_framebuffer (offscreen); cogl_ortho (0, width, height, 0, 0, 1.0); cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0); cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); st_theme_node_paint_borders (node, &box, 0xFF); cogl_pop_framebuffer (); cogl_handle_unref (offscreen); node->box_shadow_material = _st_create_shadow_material (box_shadow_spec, buffer); } cogl_handle_unref (buffer); } } background_image_shadow_spec = st_theme_node_get_background_image_shadow (node); if (background_image != NULL && !has_border && !has_border_radius) { node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image); node->background_material = _st_create_texture_material (node->background_texture); if (node->background_repeat) cogl_material_set_layer_wrap_mode (node->background_material, 0, COGL_MATERIAL_WRAP_MODE_REPEAT); if (background_image_shadow_spec) { node->background_shadow_material = _st_create_shadow_material (background_image_shadow_spec, node->background_texture); } } node->corner_material[ST_CORNER_TOPLEFT] = st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT); node->corner_material[ST_CORNER_TOPRIGHT] = st_theme_node_lookup_corner (node, ST_CORNER_TOPRIGHT); node->corner_material[ST_CORNER_BOTTOMRIGHT] = st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMRIGHT); node->corner_material[ST_CORNER_BOTTOMLEFT] = st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMLEFT); } static void paint_material_with_opacity (CoglHandle material, ClutterActorBox *box, ClutterActorBox *coords, guint8 paint_opacity) { cogl_material_set_color4ub (material, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_set_source (material); if (coords) cogl_rectangle_with_texture_coords (box->x1, box->y1, box->x2, box->y2, coords->x1, coords->y1, coords->x2, coords->y2); else cogl_rectangle (box->x1, box->y1, box->x2, box->y2); } static void st_theme_node_paint_borders (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity) { float width, height; int border_width[4]; guint border_radius[4]; int max_border_radius = 0; int max_width_radius[4]; int corner_id, side_id; ClutterColor border_color; guint8 alpha; width = box->x2 - box->x1; height = box->y2 - box->y1; /* TODO - support non-uniform border colors */ get_arbitrary_border_color (node, &border_color); for (side_id = 0; side_id < 4; side_id++) border_width[side_id] = st_theme_node_get_border_width(node, side_id); st_theme_node_reduce_border_radius (node, border_radius); for (corner_id = 0; corner_id < 4; corner_id++) { guint border_width_1, border_width_2; st_theme_node_get_corner_border_widths (node, corner_id, &border_width_1, &border_width_2); if (border_radius[corner_id] > max_border_radius) max_border_radius = border_radius[corner_id]; max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2), border_radius[corner_id]); } /* borders */ if (border_width[ST_SIDE_TOP] > 0 || border_width[ST_SIDE_RIGHT] > 0 || border_width[ST_SIDE_BOTTOM] > 0 || border_width[ST_SIDE_LEFT] > 0) { ClutterColor effective_border; gboolean skip_corner_1, skip_corner_2; float x1, y1, x2, y2; over (&border_color, &node->background_color, &effective_border); alpha = paint_opacity * effective_border.alpha / 255; if (alpha > 0) { cogl_set_source_color4ub (effective_border.red, effective_border.green, effective_border.blue, alpha); /* NORTH */ skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0; skip_corner_2 = border_radius[ST_CORNER_TOPRIGHT] > 0; x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0; y1 = 0; x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width; y2 = border_width[ST_SIDE_TOP]; cogl_rectangle (x1, y1, x2, y2); /* EAST */ skip_corner_1 = border_radius[ST_CORNER_TOPRIGHT] > 0; skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0; x1 = width - border_width[ST_SIDE_RIGHT]; y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT] : border_width[ST_SIDE_TOP]; x2 = width; y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT] : height - border_width[ST_SIDE_BOTTOM]; cogl_rectangle (x1, y1, x2, y2); /* SOUTH */ skip_corner_1 = border_radius[ST_CORNER_BOTTOMLEFT] > 0; skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0; x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0; y1 = height - border_width[ST_SIDE_BOTTOM]; x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT] : width; y2 = height; cogl_rectangle (x1, y1, x2, y2); /* WEST */ skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0; skip_corner_2 = border_radius[ST_CORNER_BOTTOMLEFT] > 0; x1 = 0; y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : border_width[ST_SIDE_TOP]; x2 = border_width[ST_SIDE_LEFT]; y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT] : height - border_width[ST_SIDE_BOTTOM]; cogl_rectangle (x1, y1, x2, y2); } } /* corners */ if (max_border_radius > 0 && paint_opacity > 0) { for (corner_id = 0; corner_id < 4; corner_id++) { if (node->corner_material[corner_id] == COGL_INVALID_HANDLE) continue; cogl_material_set_color4ub (node->corner_material[corner_id], paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_set_source (node->corner_material[corner_id]); switch (corner_id) { case ST_CORNER_TOPLEFT: cogl_rectangle_with_texture_coords (0, 0, max_width_radius[ST_CORNER_TOPLEFT], max_width_radius[ST_CORNER_TOPLEFT], 0, 0, 0.5, 0.5); break; case ST_CORNER_TOPRIGHT: cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_TOPRIGHT], 0, width, max_width_radius[ST_CORNER_TOPRIGHT], 0.5, 0, 1, 0.5); break; case ST_CORNER_BOTTOMRIGHT: cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_BOTTOMRIGHT], height - max_width_radius[ST_CORNER_BOTTOMRIGHT], width, height, 0.5, 0.5, 1, 1); break; case ST_CORNER_BOTTOMLEFT: cogl_rectangle_with_texture_coords (0, height - max_width_radius[ST_CORNER_BOTTOMLEFT], max_width_radius[ST_CORNER_BOTTOMLEFT], height, 0, 0.5, 0.5, 1); break; } } } /* background color */ alpha = paint_opacity * node->background_color.alpha / 255; if (alpha > 0) { cogl_set_source_color4ub (node->background_color.red, node->background_color.green, node->background_color.blue, alpha); /* We add padding to each corner, so that all corners end up as if they * had a border-radius of max_border_radius, which allows us to treat * corners as uniform further on. */ for (corner_id = 0; corner_id < 4; corner_id++) { float verts[8]; int n_rects; /* corner texture does not need padding */ if (max_border_radius == border_radius[corner_id]) continue; n_rects = border_radius[corner_id] == 0 ? 1 : 2; switch (corner_id) { case ST_CORNER_TOPLEFT: verts[0] = border_width[ST_SIDE_LEFT]; verts[1] = MAX(border_radius[corner_id], border_width[ST_SIDE_TOP]); verts[2] = max_border_radius; verts[3] = max_border_radius; if (n_rects == 2) { verts[4] = MAX(border_radius[corner_id], border_width[ST_SIDE_LEFT]); verts[5] = border_width[ST_SIDE_TOP]; verts[6] = max_border_radius; verts[7] = MAX(border_radius[corner_id], border_width[ST_SIDE_TOP]); } break; case ST_CORNER_TOPRIGHT: verts[0] = width - max_border_radius; verts[1] = MAX(border_radius[corner_id], border_width[ST_SIDE_TOP]); verts[2] = width - border_width[ST_SIDE_RIGHT]; verts[3] = max_border_radius; if (n_rects == 2) { verts[4] = width - max_border_radius; verts[5] = border_width[ST_SIDE_TOP]; verts[6] = width - MAX(border_radius[corner_id], border_width[ST_SIDE_RIGHT]); verts[7] = MAX(border_radius[corner_id], border_width[ST_SIDE_TOP]); } break; case ST_CORNER_BOTTOMRIGHT: verts[0] = width - max_border_radius; verts[1] = height - max_border_radius; verts[2] = width - border_width[ST_SIDE_RIGHT]; verts[3] = height - MAX(border_radius[corner_id], border_width[ST_SIDE_BOTTOM]); if (n_rects == 2) { verts[4] = width - max_border_radius; verts[5] = height - MAX(border_radius[corner_id], border_width[ST_SIDE_BOTTOM]); verts[6] = width - MAX(border_radius[corner_id], border_width[ST_SIDE_RIGHT]); verts[7] = height - border_width[ST_SIDE_BOTTOM]; } break; case ST_CORNER_BOTTOMLEFT: verts[0] = border_width[ST_SIDE_LEFT]; verts[1] = height - max_border_radius; verts[2] = max_border_radius; verts[3] = height - MAX(border_radius[corner_id], border_width[ST_SIDE_BOTTOM]); if (n_rects == 2) { verts[4] = MAX(border_radius[corner_id], border_width[ST_SIDE_LEFT]); verts[5] = height - MAX(border_radius[corner_id], border_width[ST_SIDE_BOTTOM]); verts[6] = max_border_radius; verts[7] = height - border_width[ST_SIDE_BOTTOM]; } break; } cogl_rectangles (verts, n_rects); } /* Once we've drawn the borders and corners, if the corners are bigger * then the border width, the remaining area is shaped like * * ######## * ########## * ########## * ######## * * We draw it in at most 3 pieces - first the top and bottom if * necessary, then the main rectangle */ if (max_border_radius > border_width[ST_SIDE_TOP]) cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]), border_width[ST_SIDE_TOP], width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]), max_border_radius); if (max_border_radius > border_width[ST_SIDE_BOTTOM]) cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]), height - max_border_radius, width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]), height - border_width[ST_SIDE_BOTTOM]); cogl_rectangle (border_width[ST_SIDE_LEFT], MAX(border_width[ST_SIDE_TOP], max_border_radius), width - border_width[ST_SIDE_RIGHT], height - MAX(border_width[ST_SIDE_BOTTOM], max_border_radius)); } } static void st_theme_node_paint_sliced_border_image (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity) { gfloat ex, ey; gfloat tx1, ty1, tx2, ty2; gint border_left, border_right, border_top, border_bottom; float img_width, img_height; StBorderImage *border_image; CoglHandle material; border_image = st_theme_node_get_border_image (node); g_assert (border_image != NULL); st_border_image_get_borders (border_image, &border_left, &border_right, &border_top, &border_bottom); img_width = cogl_texture_get_width (node->border_slices_texture); img_height = cogl_texture_get_height (node->border_slices_texture); tx1 = border_left / img_width; tx2 = (img_width - border_right) / img_width; ty1 = border_top / img_height; ty2 = (img_height - border_bottom) / img_height; ex = node->alloc_width - border_right; if (ex < 0) ex = border_right; /* FIXME ? */ ey = node->alloc_height - border_bottom; if (ey < 0) ey = border_bottom; /* FIXME ? */ material = node->border_slices_material; cogl_material_set_color4ub (material, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_set_source (material); { float rectangles[] = { /* top left corner */ 0, 0, border_left, border_top, 0.0, 0.0, tx1, ty1, /* top middle */ border_left, 0, ex, border_top, tx1, 0.0, tx2, ty1, /* top right */ ex, 0, node->alloc_width, border_top, tx2, 0.0, 1.0, ty1, /* mid left */ 0, border_top, border_left, ey, 0.0, ty1, tx1, ty2, /* center */ border_left, border_top, ex, ey, tx1, ty1, tx2, ty2, /* mid right */ ex, border_top, node->alloc_width, ey, tx2, ty1, 1.0, ty2, /* bottom left */ 0, ey, border_left, node->alloc_height, 0.0, ty2, tx1, 1.0, /* bottom center */ border_left, ey, ex, node->alloc_height, tx1, ty2, tx2, 1.0, /* bottom right */ ex, ey, node->alloc_width, node->alloc_height, tx2, ty2, 1.0, 1.0 }; cogl_rectangles_with_texture_coords (rectangles, 9); } } static void st_theme_node_paint_outline (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity) { float width, height; int outline_width; ClutterColor outline_color, effective_outline; width = box->x2 - box->x1; height = box->y2 - box->y1; outline_width = st_theme_node_get_outline_width (node); if (outline_width == 0) return; st_theme_node_get_outline_color (node, &outline_color); over (&outline_color, &node->background_color, &effective_outline); cogl_set_source_color4ub (effective_outline.red, effective_outline.green, effective_outline.blue, paint_opacity * effective_outline.alpha / 255); /* The outline is drawn just outside the border, which means just * outside the allocation box. This means that in some situations * involving clip_to_allocation or the screen edges, you won't be * able to see the outline. In practice, it works well enough. */ /* NORTH */ cogl_rectangle (-outline_width, -outline_width, width + outline_width, 0); /* EAST */ cogl_rectangle (width, 0, width + outline_width, height); /* SOUTH */ cogl_rectangle (-outline_width, height, width + outline_width, height + outline_width); /* WEST */ cogl_rectangle (-outline_width, 0, 0, height); } void st_theme_node_paint (StThemeNode *node, const ClutterActorBox *box, guint8 paint_opacity) { float width, height; ClutterActorBox allocation; /* Some things take an ActorBox, some things just width/height */ width = box->x2 - box->x1; height = box->y2 - box->y1; allocation.x1 = allocation.y1 = 0; allocation.x2 = width; allocation.y2 = height; if (width <= 0 || height <= 0) return; if (node->alloc_width != width || node->alloc_height != height) st_theme_node_render_resources (node, width, height); /* Rough notes about the relationship of borders and backgrounds in CSS3; * see http://www.w3.org/TR/css3-background/ for more accurate details. * * - Things are drawn in 4 layers, from the bottom: * Background color * Background image * Border color or border image * Content * - The background color, gradient and image extend to and are clipped by * the edge of the border area, so will be rounded if the border is * rounded. (CSS3 background-clip property modifies this) * - The border image replaces what would normally be drawn by the border * - The border image is not clipped by a rounded border-radius * - The border radius rounds the background even if the border is * zero width or a border image is being used. * * Deviations from the above as implemented here: * - The combination of border image and a non-zero border radius is * not supported; the background color will be drawn with square * corners. * - The background image is drawn above the border color, not below it. * - We clip the background image to the inside edges of the border * instead of the outside edges of the border (but position the image * such that it's aligned to the outside edges) */ if (node->box_shadow_material) _st_paint_shadow_with_opacity (node->box_shadow, node->box_shadow_material, &allocation, paint_opacity); if (node->prerendered_material != COGL_INVALID_HANDLE || node->border_slices_material != COGL_INVALID_HANDLE) { if (node->prerendered_material != COGL_INVALID_HANDLE) { ClutterActorBox paint_box; st_theme_node_get_background_paint_box (node, &allocation, &paint_box); paint_material_with_opacity (node->prerendered_material, &paint_box, NULL, paint_opacity); } if (node->border_slices_material != COGL_INVALID_HANDLE) st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity); } else { st_theme_node_paint_borders (node, box, paint_opacity); } st_theme_node_paint_outline (node, box, paint_opacity); if (node->background_texture != COGL_INVALID_HANDLE) { ClutterActorBox background_box; ClutterActorBox texture_coords; gboolean has_visible_outline; /* If the node doesn't have an opaque or repeating background or * a border then we let its background image shadows leak out, * but otherwise we clip it. */ has_visible_outline = st_theme_node_has_visible_outline (node); get_background_position (node, &allocation, &background_box, &texture_coords); if (has_visible_outline || node->background_repeat) cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2); /* CSS based drop shadows * * Drop shadows in ST are modelled after the CSS3 box-shadow property; * see http://www.css3.info/preview/box-shadow/ for a detailed description. * * While the syntax of the property is mostly identical - we do not support * multiple shadows and allow for a more liberal placement of the color * parameter - its interpretation defers significantly in that the shadow's * shape is not determined by the bounding box, but by the CSS background * image. The drop shadows are allowed to escape the nodes allocation if * there is nothing (like a border, or the edge of the background color) * to logically confine it. */ if (node->background_shadow_material != COGL_INVALID_HANDLE) _st_paint_shadow_with_opacity (node->background_image_shadow, node->background_shadow_material, &background_box, paint_opacity); paint_material_with_opacity (node->background_material, &background_box, &texture_coords, paint_opacity); if (has_visible_outline || node->background_repeat) cogl_clip_pop (); } } /** * st_theme_node_copy_cached_paint_state: * @node: a #StThemeNode * @other: a different #StThemeNode * * Copy cached painting state from @other to @node. This function can be used to * optimize redrawing cached background images when the style on an element changess * in a way that doesn't affect background drawing. This function must only be called * if st_theme_node_paint_equal (node, other) returns %TRUE. */ void st_theme_node_copy_cached_paint_state (StThemeNode *node, StThemeNode *other) { int corner_id; g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (ST_IS_THEME_NODE (other)); /* Check omitted for speed: */ /* g_return_if_fail (st_theme_node_paint_equal (node, other)); */ _st_theme_node_free_drawing_state (node); node->alloc_width = other->alloc_width; node->alloc_height = other->alloc_height; if (other->background_shadow_material) node->background_shadow_material = cogl_handle_ref (other->background_shadow_material); if (other->box_shadow_material) node->box_shadow_material = cogl_handle_ref (other->box_shadow_material); if (other->background_texture) node->background_texture = cogl_handle_ref (other->background_texture); if (other->background_material) node->background_material = cogl_handle_ref (other->background_material); if (other->border_slices_texture) node->border_slices_texture = cogl_handle_ref (other->border_slices_texture); if (other->border_slices_material) node->border_slices_material = cogl_handle_ref (other->border_slices_material); if (other->prerendered_texture) node->prerendered_texture = cogl_handle_ref (other->prerendered_texture); if (other->prerendered_material) node->prerendered_material = cogl_handle_ref (other->prerendered_material); for (corner_id = 0; corner_id < 4; corner_id++) if (other->corner_material[corner_id]) node->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]); } Cinnamon-2.8.6/src/st/st-border-image.h0000664000175000017500000000525212625666430016602 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-border-image.h: store information about an image with borders * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_BORDER_IMAGE_H__ #define __ST_BORDER_IMAGE_H__ #include G_BEGIN_DECLS /* A StBorderImage encapsulates an image with specified unscaled borders on each edge. */ typedef struct _StBorderImage StBorderImage; typedef struct _StBorderImageClass StBorderImageClass; #define ST_TYPE_BORDER_IMAGE (st_border_image_get_type ()) #define ST_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_BORDER_IMAGE, StBorderImage)) #define ST_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BORDER_IMAGE, StBorderImageClass)) #define ST_IS_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_BORDER_IMAGE)) #define ST_IS_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BORDER_IMAGE)) #define ST_BORDER_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BORDER_IMAGE, StBorderImageClass)) GType st_border_image_get_type (void) G_GNUC_CONST; StBorderImage *st_border_image_new (const char *filename, int border_top, int border_right, int border_bottom, int border_left); const char *st_border_image_get_filename (StBorderImage *image); void st_border_image_get_borders (StBorderImage *image, int *border_top, int *border_right, int *border_bottom, int *border_left); gboolean st_border_image_equal (StBorderImage *image, StBorderImage *other); G_END_DECLS #endif /* __ST_BORDER_IMAGE_H__ */ Cinnamon-2.8.6/src/st/st-group.h0000664000175000017500000000422412625666430015377 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-group.h: A fixed layout container based on ClutterGroup * * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_GROUP_H__ #define __ST_GROUP_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_GROUP (st_group_get_type ()) #define ST_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_GROUP, StGroup)) #define ST_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_GROUP)) #define ST_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_GROUP, StGroupClass)) #define ST_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_GROUP)) #define ST_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_GROUP, StGroupClass)) typedef struct _StGroup StGroup; typedef struct _StGroupPrivate StGroupPrivate; typedef struct _StGroupClass StGroupClass; /** * StGroup: * * The #StGroup struct contains only private data */ struct _StGroup { /*< private >*/ StContainer parent_instance; }; /** * StGroupClass: * * The #StGroupClass struct contains only private data */ struct _StGroupClass { /*< private >*/ StContainerClass parent_class; }; GType st_group_get_type (void) G_GNUC_CONST; StWidget *st_group_new (void); G_END_DECLS #endif /* __ST_GROUP_H__ */ Cinnamon-2.8.6/src/st/st-texture-cache.h0000664000175000017500000001441212625666430017004 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-texture-cache.h: Object for loading and caching images as textures * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010, Maxim Ermilov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_TEXTURE_CACHE_H__ #define __ST_TEXTURE_CACHE_H__ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #include #include #include #include #include #include #define ST_TYPE_TEXTURE_CACHE (st_texture_cache_get_type ()) #define ST_TEXTURE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_CACHE, StTextureCache)) #define ST_TEXTURE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_CACHE, StTextureCacheClass)) #define ST_IS_TEXTURE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_CACHE)) #define ST_IS_TEXTURE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_CACHE)) #define ST_TEXTURE_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_CACHE, StTextureCacheClass)) typedef struct _StTextureCache StTextureCache; typedef struct _StTextureCacheClass StTextureCacheClass; typedef struct _StTextureCachePrivate StTextureCachePrivate; struct _StTextureCache { GObject parent; StTextureCachePrivate *priv; }; struct _StTextureCacheClass { GObjectClass parent_class; }; typedef enum { ST_TEXTURE_CACHE_POLICY_NONE, ST_TEXTURE_CACHE_POLICY_FOREVER } StTextureCachePolicy; GType st_texture_cache_get_type (void) G_GNUC_CONST; StTextureCache* st_texture_cache_get_default (void); ClutterActor * st_texture_cache_load_sliced_image (StTextureCache *cache, const gchar *path, gint grid_width, gint grid_height, GFunc load_callback, gpointer user_data); ClutterActor *st_texture_cache_bind_pixbuf_property (StTextureCache *cache, GObject *object, const char *property_name); ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size); ClutterActor *st_texture_cache_load_icon_name (StTextureCache *cache, StThemeNode *theme_node, const char *name, StIconType icon_type, gint size); ClutterActor *st_texture_cache_load_uri_async (StTextureCache *cache, const gchar *uri, int available_width, int available_height); ClutterActor *st_texture_cache_load_uri_sync (StTextureCache *cache, StTextureCachePolicy policy, const gchar *uri, int available_width, int available_height, GError **error); CoglTexture *st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, const gchar *file_path); cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, const gchar *file_path); ClutterActor *st_texture_cache_load_from_raw (StTextureCache *cache, const guchar *data, gsize len, gboolean has_alpha, int width, int height, int rowstride, int size, GError **error); ClutterActor *st_texture_cache_load_file_simple (StTextureCache *cache, const gchar *file_path); /** * StTextureCacheLoader: (skip) * @cache: a #StTextureCache * @key: Unique identifier for this texture * @data: Callback user data * @error: A #GError * * See st_texture_cache_load(). Implementations should return a * texture handle for the given key, or set @error. * */ typedef CoglTexture * (*StTextureCacheLoader) (StTextureCache *cache, const char *key, void *data, GError **error); CoglTexture * st_texture_cache_load (StTextureCache *cache, const char *key, StTextureCachePolicy policy, StTextureCacheLoader load, void *data, GError **error); #endif /* __ST_TEXTURE_CACHE_H__ */ Cinnamon-2.8.6/src/st/st-theme-node.c0000664000175000017500000033220712625666430016270 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node.c: style information for one node in a tree of themed objects * * Copyright 2008-2010 Red Hat, Inc. * Copyright 2009 Steve Frécinaux * Copyright 2009, 2010 Florian Müllner * Copyright 2010 Adel Gadllah * Copyright 2010 Giovanni Campagna * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "st-theme-private.h" #include "st-theme-context.h" #include "st-theme-node-private.h" static void st_theme_node_init (StThemeNode *node); static void st_theme_node_class_init (StThemeNodeClass *klass); static void st_theme_node_dispose (GObject *object); static void st_theme_node_finalize (GObject *object); static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff }; static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 }; static const ClutterColor DEFAULT_SUCCESS_COLOR = { 0x4e, 0x9a, 0x06, 0xff }; static const ClutterColor DEFAULT_WARNING_COLOR = { 0xf5, 0x79, 0x3e, 0xff }; static const ClutterColor DEFAULT_ERROR_COLOR = { 0xcc, 0x00, 0x00, 0xff }; extern gfloat st_slow_down_factor; G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT) static void st_theme_node_init (StThemeNode *node) { node->transition_duration = -1; _st_theme_node_init_drawing_state (node); } static void st_theme_node_class_init (StThemeNodeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = st_theme_node_dispose; object_class->finalize = st_theme_node_finalize; } static void on_custom_stylesheets_changed (StTheme *theme, gpointer data) { StThemeNode *node = data; node->properties_computed = FALSE; } static void st_theme_node_dispose (GObject *gobject) { StThemeNode *node = ST_THEME_NODE (gobject); if (node->context) { g_object_unref (node->context); node->context = NULL; } if (node->theme) { g_signal_handlers_disconnect_by_func (node->theme, on_custom_stylesheets_changed, node); g_object_unref (node->theme); node->theme = NULL; } if (node->parent_node) { g_object_unref (node->parent_node); node->parent_node = NULL; } if (node->border_image) { g_object_unref (node->border_image); node->border_image = NULL; } if (node->icon_colors) { st_icon_colors_unref (node->icon_colors); node->icon_colors = NULL; } G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject); } static void st_theme_node_finalize (GObject *object) { StThemeNode *node = ST_THEME_NODE (object); g_free (node->element_id); g_strfreev (node->element_classes); g_strfreev (node->pseudo_classes); g_free (node->inline_style); if (node->properties) { g_free (node->properties); node->properties = NULL; node->n_properties = 0; } if (node->inline_properties) { /* This destroys the list, not just the head of the list */ cr_declaration_destroy (node->inline_properties); } if (node->font_desc) { pango_font_description_free (node->font_desc); node->font_desc = NULL; } if (node->box_shadow) { st_shadow_unref (node->box_shadow); node->box_shadow = NULL; } if (node->background_image_shadow) { st_shadow_unref (node->background_image_shadow); node->background_image_shadow = NULL; } if (node->text_shadow) { st_shadow_unref (node->text_shadow); node->text_shadow = NULL; } if (node->background_image) g_free (node->background_image); _st_theme_node_free_drawing_state (node); G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object); } static GStrv split_on_whitespace (const gchar *s) { gchar *cur; gchar *l; gchar *temp; GPtrArray *arr; if (s == NULL) return NULL; arr = g_ptr_array_new (); l = g_strdup (s); cur = strtok_r (l, " \t\f\r\n", &temp); while (cur != NULL) { g_ptr_array_add (arr, g_strdup (cur)); cur = strtok_r (NULL, " \t\f\r\n", &temp); } g_free (l); g_ptr_array_add (arr, NULL); return (GStrv) g_ptr_array_free (arr, FALSE); } /** * st_theme_node_new: * @context: the context representing global state for this themed tree * @parent_node: (allow-none): the parent node of this node * @theme: (allow-none): a theme (stylesheet set) that overrides the * theme inherited from the parent node * @element_type: the type of the GObject represented by this node * in the tree (corresponding to an element if we were theming an XML * document. %G_TYPE_NONE means this style was created for the stage * actor and matches a selector element name of 'stage'. * @element_id: (allow-none): the ID to match CSS rules against * @element_class: (allow-none): a whitespace-separated list of classes * to match CSS rules against * @pseudo_class: (allow-none): a whitespace-separated list of pseudo-classes * (like 'hover' or 'visited') to match CSS rules against * * Creates a new #StThemeNode. Once created, a node is immutable. Of any * of the attributes of the node (like the @element_class) change the node * and its child nodes must be destroyed and recreated. * * Return value: (transfer full): the theme node */ StThemeNode * st_theme_node_new (StThemeContext *context, StThemeNode *parent_node, StTheme *theme, GType element_type, const char *element_id, const char *element_class, const char *pseudo_class, const char *inline_style, gboolean important) { StThemeNode *node; g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL); g_return_val_if_fail (parent_node == NULL || ST_IS_THEME_NODE (parent_node), NULL); node = g_object_new (ST_TYPE_THEME_NODE, NULL); node->context = g_object_ref (context); if (parent_node != NULL) node->parent_node = g_object_ref (parent_node); else node->parent_node = NULL; if (theme == NULL && parent_node != NULL) theme = parent_node->theme; if (theme != NULL) { node->theme = g_object_ref (theme); g_signal_connect (node->theme, "custom-stylesheets-changed", G_CALLBACK (on_custom_stylesheets_changed), node); } node->important = (parent_node ? parent_node->important : FALSE) || important; node->element_type = element_type; node->element_id = g_strdup (element_id); node->element_classes = split_on_whitespace (element_class); node->pseudo_classes = split_on_whitespace (pseudo_class); node->inline_style = g_strdup (inline_style); return node; } /** * st_theme_node_get_parent: * @node: a #StThemeNode * * Gets the parent themed element node. * * Return value: (transfer none): the parent #StThemeNode, or %NULL if this * is the root node of the tree of theme elements. */ StThemeNode * st_theme_node_get_parent (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); return node->parent_node; } /** * st_theme_node_get_theme: * @node: a #StThemeNode * * Gets the theme stylesheet set that styles this node * * Return value: (transfer none): the theme stylesheet set */ StTheme * st_theme_node_get_theme (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); return node->theme; } GType st_theme_node_get_element_type (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), G_TYPE_NONE); return node->element_type; } const char * st_theme_node_get_element_id (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); return node->element_id; } /** * st_theme_node_get_element_classes: * * Returns: (transfer none): the element's classes */ GStrv st_theme_node_get_element_classes (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); return node->element_classes; } /** * st_theme_node_get_pseudo_classes: * * Returns: (transfer none): the element's pseudo-classes */ GStrv st_theme_node_get_pseudo_classes (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); return node->pseudo_classes; } /** * st_theme_node_equal: * @node_a: first #StThemeNode * @node_b: second #StThemeNode * * Compare two #StThemeNodes. Two nodes which compare equal will match * the same CSS rules and have the same style properties. However, two * nodes that have ended up with identical style properties do not * necessarily compare equal. * In detail, @node_a and @node_b are considered equal iff * * * they share the same #StTheme and #StThemeContext * * * they have the same parent * * * they have the same element type * * * their id, class, pseudo-class and inline-style match * * * * Returns: %TRUE if @node_a equals @node_b */ gboolean st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b) { g_return_val_if_fail (ST_IS_THEME_NODE (node_a), FALSE); if (node_a == node_b) return TRUE; g_return_val_if_fail (ST_IS_THEME_NODE (node_b), FALSE); if (node_a->parent_node != node_b->parent_node || node_a->context != node_b->context || node_a->theme != node_b->theme || node_a->element_type != node_b->element_type || node_a->important != node_b->important || g_strcmp0 (node_a->element_id, node_b->element_id) || g_strcmp0 (node_a->inline_style, node_b->inline_style)) return FALSE; if ((node_a->element_classes == NULL) != (node_b->element_classes == NULL)) return FALSE; if ((node_a->pseudo_classes == NULL) != (node_b->pseudo_classes == NULL)) return FALSE; if (node_a->element_classes != NULL) { int i; for (i = 0; ; i++) { if (g_strcmp0 (node_a->element_classes[i], node_b->element_classes[i])) return FALSE; if (node_a->element_classes[i] == NULL) break; } } if (node_a->pseudo_classes != NULL) { int i; for (i = 0; ; i++) { if (g_strcmp0 (node_a->pseudo_classes[i], node_b->pseudo_classes[i])) return FALSE; if (node_a->pseudo_classes[i] == NULL) break; } } return TRUE; } guint st_theme_node_hash (StThemeNode *node) { guint hash = GPOINTER_TO_UINT (node->parent_node); hash = hash * 33 + GPOINTER_TO_UINT (node->context); hash = hash * 33 + GPOINTER_TO_UINT (node->theme); hash = hash * 33 + ((guint) node->element_type); if (node->element_id != NULL) hash = hash * 33 + g_str_hash (node->element_id); if (node->inline_style != NULL) hash = hash * 33 + g_str_hash (node->inline_style); if (node->element_classes != NULL) { gchar **it; for (it = node->element_classes; *it != NULL; it++) hash = hash * 33 + g_str_hash (*it) + 1; } if (node->pseudo_classes != NULL) { gchar **it; for (it = node->pseudo_classes; *it != NULL; it++) hash = hash * 33 + g_str_hash (*it) + 1; } return hash; } static void ensure_properties (StThemeNode *node) { if (!node->properties_computed) { GPtrArray *properties = NULL; node->properties_computed = TRUE; if (node->theme) { properties = _st_theme_get_matched_properties (node->theme, node); if ((!properties || properties->len == 0) && node->important) properties = _st_theme_get_matched_properties_fallback (node->theme, node); } if (node->inline_style) { CRDeclaration *cur_decl; if (!properties) properties = g_ptr_array_new (); node->inline_properties = _st_theme_parse_declaration_list (node->inline_style); for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next) g_ptr_array_add (properties, cur_decl); } if (properties) { node->n_properties = properties->len; node->properties = (CRDeclaration **)g_ptr_array_free (properties, FALSE); } } } typedef enum { VALUE_FOUND, VALUE_NOT_FOUND, VALUE_INHERIT } GetFromTermResult; static gboolean term_is_inherit (CRTerm *term) { return (term->type == TERM_IDENT && strcmp (term->content.str->stryng->str, "inherit") == 0); } static gboolean term_is_none (CRTerm *term) { return (term->type == TERM_IDENT && strcmp (term->content.str->stryng->str, "none") == 0); } static gboolean term_is_transparent (CRTerm *term) { return (term->type == TERM_IDENT && strcmp (term->content.str->stryng->str, "transparent") == 0); } static int color_component_from_double (double component) { /* We want to spread the range 0-1 equally over 0..255, but * 1.0 should map to 255 not 256, so we need to special-case it. * See http://people.redhat.com/otaylor/pixel-converting.html * for (very) detailed discussion of related issues. */ if (component >= 1.0) return 255; else return (int)(component * 256); } static GetFromTermResult get_color_from_rgba_term (CRTerm *term, ClutterColor *color) { CRTerm *arg = term->ext_content.func_param; CRNum *num; double r = 0, g = 0, b = 0, a = 0; int i; for (i = 0; i < 4; i++) { double value; if (arg == NULL) return VALUE_NOT_FOUND; if ((i == 0 && arg->the_operator != NO_OP) || (i > 0 && arg->the_operator != COMMA)) return VALUE_NOT_FOUND; if (arg->type != TERM_NUMBER) return VALUE_NOT_FOUND; num = arg->content.num; /* For simplicity, we convert a,r,g,b to [0,1.0] floats and then * convert them back below. Then when we set them on a cairo content * we convert them back to floats, and then cairo converts them * back to integers to pass them to X, and so forth... */ if (i < 3) { if (num->type == NUM_PERCENTAGE) value = num->val / 100; else if (num->type == NUM_GENERIC) value = num->val / 255; else return VALUE_NOT_FOUND; } else { if (num->type != NUM_GENERIC) return VALUE_NOT_FOUND; value = num->val; } value = CLAMP (value, 0, 1); switch (i) { case 0: r = value; break; case 1: g = value; break; case 2: b = value; break; case 3: a = value; break; } arg = arg->next; } color->red = color_component_from_double (r); color->green = color_component_from_double (g); color->blue = color_component_from_double (b); color->alpha = color_component_from_double (a); return VALUE_FOUND; } static GetFromTermResult get_color_from_term (StThemeNode *node, CRTerm *term, ClutterColor *color) { CRRgb rgb; enum CRStatus status; /* Since libcroco doesn't know about rgba colors, it can't handle * the transparent keyword */ if (term_is_transparent (term)) { *color = TRANSPARENT_COLOR; return VALUE_FOUND; } /* rgba () colors - a CSS3 addition, are not supported by libcroco, * but they are parsed as a "function", so we can emulate the * functionality. */ else if (term->type == TERM_FUNCTION && term->content.str && term->content.str->stryng && term->content.str->stryng->str && strcmp (term->content.str->stryng->str, "rgba") == 0) { return get_color_from_rgba_term (term, color); } status = cr_rgb_set_from_term (&rgb, term); if (status != CR_OK) return VALUE_NOT_FOUND; if (rgb.inherit) return VALUE_INHERIT; if (rgb.is_percentage) cr_rgb_compute_from_percentage (&rgb); color->red = rgb.red; color->green = rgb.green; color->blue = rgb.blue; color->alpha = 0xff; return VALUE_FOUND; } /** * st_theme_node_lookup_color: * @node: a #StThemeNode * @property_name: The name of the color property * @inherit: if %TRUE, if a value is not found for the property on the * node, then it will be looked up on the parent node, and then on the * parent's parent, and so forth. Note that if the property has a * value of 'inherit' it will be inherited even if %FALSE is passed * in for @inherit; this only affects the default behavior for inheritance. * @color: (out caller-allocates): location to store the color that was * determined. If the property is not found, the value in this location * will not be changed. * * Generically looks up a property containing a single color value. When * specific getters (like st_theme_node_get_background_color()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * See also st_theme_node_get_color(), which provides a simpler API. * * Return value: %TRUE if the property was found in the properties for this * theme node (or in the properties of parent nodes when inheriting.) */ gboolean st_theme_node_lookup_color (StThemeNode *node, const char *property_name, gboolean inherit, ClutterColor *color) { int i; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, property_name) == 0) { GetFromTermResult result = get_color_from_term (node, decl->value, color); if (result == VALUE_FOUND) { return TRUE; } else if (result == VALUE_INHERIT) { if (node->parent_node) return st_theme_node_lookup_color (node->parent_node, property_name, inherit, color); else break; } } } if (inherit && node->parent_node) return st_theme_node_lookup_color (node->parent_node, property_name, inherit, color); return FALSE; } /** * st_theme_node_get_color: * @node: a #StThemeNode * @property_name: The name of the color property * @color: (out caller-allocates): location to store the color that * was determined. * * Generically looks up a property containing a single color value. When * specific getters (like st_theme_node_get_background_color()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * If @property_name is not found, a warning will be logged and a * default color returned. * * See also st_theme_node_lookup_color(), which provides more options, * and lets you handle the case where the theme does not specify the * indicated color. */ void st_theme_node_get_color (StThemeNode *node, const char *property_name, ClutterColor *color) { if (!st_theme_node_lookup_color (node, property_name, FALSE, color)) { g_warning ("Did not find color property '%s'", property_name); memset (color, 0, sizeof (ClutterColor)); } } /** * st_theme_node_lookup_double: * @node: a #StThemeNode * @property_name: The name of the numeric property * @inherit: if %TRUE, if a value is not found for the property on the * node, then it will be looked up on the parent node, and then on the * parent's parent, and so forth. Note that if the property has a * value of 'inherit' it will be inherited even if %FALSE is passed * in for @inherit; this only affects the default behavior for inheritance. * @value: (out): location to store the value that was determined. * If the property is not found, the value in this location * will not be changed. * * Generically looks up a property containing a single numeric value * without units. * * See also st_theme_node_get_double(), which provides a simpler API. * * Return value: %TRUE if the property was found in the properties for this * theme node (or in the properties of parent nodes when inheriting.) */ gboolean st_theme_node_lookup_double (StThemeNode *node, const char *property_name, gboolean inherit, double *value) { gboolean result = FALSE; int i; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, property_name) == 0) { CRTerm *term = decl->value; if (term->type != TERM_NUMBER || term->content.num->type != NUM_GENERIC) continue; *value = term->content.num->val; result = TRUE; break; } } if (!result && inherit && node->parent_node) result = st_theme_node_lookup_double (node->parent_node, property_name, inherit, value); return result; } /** * st_theme_node_get_double: * @node: a #StThemeNode * @property_name: The name of the numeric property * * Generically looks up a property containing a single numeric value * without units. * * See also st_theme_node_lookup_double(), which provides more options, * and lets you handle the case where the theme does not specify the * indicated value. * * Return value: the value found. If @property_name is not * found, a warning will be logged and 0 will be returned. */ gdouble st_theme_node_get_double (StThemeNode *node, const char *property_name) { gdouble value; if (st_theme_node_lookup_double (node, property_name, FALSE, &value)) return value; else { g_warning ("Did not find double property '%s'", property_name); return 0.0; } } static const PangoFontDescription * get_parent_font (StThemeNode *node) { if (node->parent_node) return st_theme_node_get_font (node->parent_node); else return st_theme_context_get_font (node->context); } static GetFromTermResult get_length_from_term (StThemeNode *node, CRTerm *term, gboolean use_parent_font, gdouble *length) { CRNum *num; enum { ABSOLUTE, POINTS, FONT_RELATIVE, } type = ABSOLUTE; double multiplier = 1.0; int scale_factor; g_object_get (node->context, "scale-factor", &scale_factor, NULL); if (term->type != TERM_NUMBER) { g_warning ("Ignoring length property that isn't a number"); return VALUE_NOT_FOUND; } num = term->content.num; switch (num->type) { case NUM_LENGTH_PX: type = ABSOLUTE; multiplier = 1 * scale_factor; break; case NUM_LENGTH_PT: type = POINTS; multiplier = 1; break; case NUM_LENGTH_IN: type = POINTS; multiplier = 72; break; case NUM_LENGTH_CM: type = POINTS; multiplier = 72. / 2.54; break; case NUM_LENGTH_MM: type = POINTS; multiplier = 72. / 25.4; break; case NUM_LENGTH_PC: type = POINTS; multiplier = 12. / 25.4; break; case NUM_LENGTH_EM: { type = FONT_RELATIVE; multiplier = 1; break; } case NUM_LENGTH_EX: { /* Doing better would require actually resolving the font description * to a specific font, and Pango doesn't have an ex metric anyways, * so we'd have to try and synthesize it by complicated means. * * The 0.5em is the CSS spec suggested thing to use when nothing * better is available. */ type = FONT_RELATIVE; multiplier = 0.5; break; } case NUM_INHERIT: return VALUE_INHERIT; case NUM_AUTO: g_warning ("'auto' not supported for lengths"); return VALUE_NOT_FOUND; case NUM_GENERIC: { if (num->val != 0) { g_warning ("length values must specify a unit"); return VALUE_NOT_FOUND; } else { type = ABSOLUTE; multiplier = 0; } break; } case NUM_PERCENTAGE: g_warning ("percentage lengths not currently supported"); return VALUE_NOT_FOUND; case NUM_ANGLE_DEG: case NUM_ANGLE_RAD: case NUM_ANGLE_GRAD: case NUM_TIME_MS: case NUM_TIME_S: case NUM_FREQ_HZ: case NUM_FREQ_KHZ: case NUM_UNKNOWN_TYPE: case NB_NUM_TYPE: g_warning ("Ignoring invalid type of number of length property"); return VALUE_NOT_FOUND; } double resolution; switch (type) { case ABSOLUTE: *length = num->val * multiplier; break; case POINTS: resolution = clutter_backend_get_resolution (clutter_get_default_backend ()); *length = num->val * multiplier * (resolution / 72.); break; case FONT_RELATIVE: { const PangoFontDescription *desc; double font_size; if (use_parent_font) desc = get_parent_font (node); else desc = st_theme_node_get_font (node); font_size = (double)pango_font_description_get_size (desc) / PANGO_SCALE; if (pango_font_description_get_size_is_absolute (desc)) { *length = num->val * multiplier * font_size; } else { double resolution = clutter_backend_get_resolution (clutter_get_default_backend ()); *length = num->val * multiplier * (resolution / 72.) * font_size; } } break; default: g_assert_not_reached (); } return VALUE_FOUND; } static GetFromTermResult get_length_from_term_int (StThemeNode *node, CRTerm *term, gboolean use_parent_font, gint *length) { double value; GetFromTermResult result; result = get_length_from_term (node, term, use_parent_font, &value); if (result == VALUE_FOUND) *length = (int) (0.5 + value); return result; } static GetFromTermResult get_length_internal (StThemeNode *node, const char *property_name, const char *suffixed, gdouble *length) { int i; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, property_name) == 0 || (suffixed != NULL && strcmp (decl->property->stryng->str, suffixed) == 0)) { GetFromTermResult result = get_length_from_term (node, decl->value, FALSE, length); if (result != VALUE_NOT_FOUND) return result; } } return VALUE_NOT_FOUND; } /** * st_theme_node_lookup_length: * @node: a #StThemeNode * @property_name: The name of the length property * @inherit: if %TRUE, if a value is not found for the property on the * node, then it will be looked up on the parent node, and then on the * parent's parent, and so forth. Note that if the property has a * value of 'inherit' it will be inherited even if %FALSE is passed * in for @inherit; this only affects the default behavior for inheritance. * @length: (out): location to store the length that was determined. * If the property is not found, the value in this location * will not be changed. The returned length is resolved * to pixels. * * Generically looks up a property containing a single length value. When * specific getters (like st_theme_node_get_border_width()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * See also st_theme_node_get_length(), which provides a simpler API. * * Return value: %TRUE if the property was found in the properties for this * theme node (or in the properties of parent nodes when inheriting.) */ gboolean st_theme_node_lookup_length (StThemeNode *node, const char *property_name, gboolean inherit, gdouble *length) { GetFromTermResult result = get_length_internal (node, property_name, NULL, length); if (result == VALUE_FOUND) return TRUE; else if (result == VALUE_INHERIT) inherit = TRUE; if (inherit && node->parent_node && st_theme_node_lookup_length (node->parent_node, property_name, inherit, length)) return TRUE; else return FALSE; } /** * st_theme_node_get_length: * @node: a #StThemeNode * @property_name: The name of the length property * * Generically looks up a property containing a single length value. When * specific getters (like st_theme_node_get_border_width()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * Unlike st_theme_node_get_color() and st_theme_node_get_double(), * this does not print a warning if the property is not found; it just * returns 0. * * See also st_theme_node_lookup_length(), which provides more options. * * Return value: the length, in pixels, or 0 if the property was not found. */ gdouble st_theme_node_get_length (StThemeNode *node, const char *property_name) { gdouble length; if (st_theme_node_lookup_length (node, property_name, FALSE, &length)) return length; else return 0.0; } static void do_border_radius_term (StThemeNode *node, CRTerm *term, gboolean topleft, gboolean topright, gboolean bottomright, gboolean bottomleft) { int value; if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND) return; if (topleft) node->border_radius[ST_CORNER_TOPLEFT] = value; if (topright) node->border_radius[ST_CORNER_TOPRIGHT] = value; if (bottomright) node->border_radius[ST_CORNER_BOTTOMRIGHT] = value; if (bottomleft) node->border_radius[ST_CORNER_BOTTOMLEFT] = value; } static void do_border_radius (StThemeNode *node, CRDeclaration *decl) { const char *property_name = decl->property->stryng->str + 13; /* Skip 'border-radius' */ if (strcmp (property_name, "") == 0) { /* Slight deviation ... if we don't understand some of the terms and understand others, * then we set the ones we understand and ignore the others instead of ignoring the * whole thing */ if (decl->value == NULL) /* 0 values */ return; else if (decl->value->next == NULL) /* 1 value */ { do_border_radius_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* all corners */ return; } else if (decl->value->next->next == NULL) /* 2 values */ { do_border_radius_term (node, decl->value, TRUE, FALSE, TRUE, FALSE); /* topleft/bottomright */ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */ } else if (decl->value->next->next->next == NULL) /* 3 values */ { do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, TRUE); /* topright/bottomleft */ do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */ } else if (decl->value->next->next->next->next == NULL) /* 4 values */ { do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); /* topleft */ do_border_radius_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* topright */ do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE, FALSE); /* bottomright */ do_border_radius_term (node, decl->value->next->next->next, FALSE, FALSE, FALSE, TRUE); /* bottomleft */ } else { g_warning ("Too many values for border-radius property"); return; } } else { if (decl->value == NULL || decl->value->next != NULL) return; if (strcmp (property_name, "-topleft") == 0) do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); else if (strcmp (property_name, "-topright") == 0) do_border_radius_term (node, decl->value, FALSE, TRUE, FALSE, FALSE); else if (strcmp (property_name, "-bottomright") == 0) do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); else if (strcmp (property_name, "-bottomleft") == 0) do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE); } } static void do_border_property (StThemeNode *node, CRDeclaration *decl) { const char *property_name = decl->property->stryng->str + 6; /* Skip 'border' */ StSide side = (StSide)-1; ClutterColor color; gboolean color_set = FALSE; int width = 0; /* suppress warning */ gboolean width_set = FALSE; int j; if (g_str_has_prefix (property_name, "-radius")) { do_border_radius (node, decl); return; } if (g_str_has_prefix (property_name, "-left")) { side = ST_SIDE_LEFT; property_name += 5; } else if (g_str_has_prefix (property_name, "-right")) { side = ST_SIDE_RIGHT; property_name += 6; } else if (g_str_has_prefix (property_name, "-top")) { side = ST_SIDE_TOP; property_name += 4; } else if (g_str_has_prefix (property_name, "-bottom")) { side = ST_SIDE_BOTTOM; property_name += 7; } if (strcmp (property_name, "") == 0) { /* Set value for width/color/style in any order */ CRTerm *term; for (term = decl->value; term; term = term->next) { GetFromTermResult result; if (term->type == TERM_IDENT) { const char *ident = term->content.str->stryng->str; if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) { width = 0; width_set = TRUE; continue; } else if (strcmp (ident, "solid") == 0) { /* The only thing we support */ continue; } else if (strcmp (ident, "dotted") == 0 || strcmp (ident, "dashed") == 0 || strcmp (ident, "double") == 0 || strcmp (ident, "groove") == 0 || strcmp (ident, "ridge") == 0 || strcmp (ident, "inset") == 0 || strcmp (ident, "outset") == 0) { /* Treat the same as solid */ continue; } /* Presumably a color, fall through */ } if (term->type == TERM_NUMBER) { result = get_length_from_term_int (node, term, FALSE, &width); if (result != VALUE_NOT_FOUND) { width_set = result == VALUE_FOUND; continue; } } result = get_color_from_term (node, term, &color); if (result != VALUE_NOT_FOUND) { color_set = result == VALUE_FOUND; continue; } } } else if (strcmp (property_name, "-color") == 0) { if (decl->value == NULL || decl->value->next != NULL) return; if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND) /* Ignore inherit */ color_set = TRUE; } else if (strcmp (property_name, "-width") == 0) { if (decl->value == NULL || decl->value->next != NULL) return; if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND) /* Ignore inherit */ width_set = TRUE; } if (side == (StSide)-1) { for (j = 0; j < 4; j++) { if (color_set) node->border_color[j] = color; if (width_set) node->border_width[j] = width; } } else { if (color_set) node->border_color[side] = color; if (width_set) node->border_width[side] = width; } } static void do_outline_property (StThemeNode *node, CRDeclaration *decl) { const char *property_name = decl->property->stryng->str + 7; /* Skip 'outline' */ ClutterColor color; gboolean color_set = FALSE; int width = 0; /* suppress warning */ gboolean width_set = FALSE; if (strcmp (property_name, "") == 0) { /* Set value for width/color/style in any order */ CRTerm *term; for (term = decl->value; term; term = term->next) { GetFromTermResult result; if (term->type == TERM_IDENT) { const char *ident = term->content.str->stryng->str; if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) { width = 0; width_set = TRUE; continue; } else if (strcmp (ident, "solid") == 0) { /* The only thing we support */ continue; } else if (strcmp (ident, "dotted") == 0 || strcmp (ident, "dashed") == 0 || strcmp (ident, "double") == 0 || strcmp (ident, "groove") == 0 || strcmp (ident, "ridge") == 0 || strcmp (ident, "inset") == 0 || strcmp (ident, "outset") == 0) { /* Treat the same as solid */ continue; } /* Presumably a color, fall through */ } if (term->type == TERM_NUMBER) { result = get_length_from_term_int (node, term, FALSE, &width); if (result != VALUE_NOT_FOUND) { width_set = result == VALUE_FOUND; continue; } } result = get_color_from_term (node, term, &color); if (result != VALUE_NOT_FOUND) { color_set = result == VALUE_FOUND; continue; } } } else if (strcmp (property_name, "-color") == 0) { if (decl->value == NULL || decl->value->next != NULL) return; if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND) /* Ignore inherit */ color_set = TRUE; } else if (strcmp (property_name, "-width") == 0) { if (decl->value == NULL || decl->value->next != NULL) return; if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND) /* Ignore inherit */ width_set = TRUE; } if (color_set) node->outline_color = color; if (width_set) node->outline_width = width; } static void do_padding_property_term (StThemeNode *node, CRTerm *term, gboolean left, gboolean right, gboolean top, gboolean bottom) { int value; if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND) return; if (left) node->padding[ST_SIDE_LEFT] = value; if (right) node->padding[ST_SIDE_RIGHT] = value; if (top) node->padding[ST_SIDE_TOP] = value; if (bottom) node->padding[ST_SIDE_BOTTOM] = value; } static void do_padding_property (StThemeNode *node, CRDeclaration *decl) { const char *property_name = decl->property->stryng->str + 7; /* Skip 'padding' */ if (strcmp (property_name, "") == 0) { /* Slight deviation ... if we don't understand some of the terms and understand others, * then we set the ones we understand and ignore the others instead of ignoring the * whole thing */ if (decl->value == NULL) /* 0 values */ return; else if (decl->value->next == NULL) /* 1 value */ { do_padding_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */ return; } else if (decl->value->next->next == NULL) /* 2 values */ { do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, TRUE); /* top/bottom */ do_padding_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */ } else if (decl->value->next->next->next == NULL) /* 3 values */ { do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */ do_padding_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */ do_padding_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */ } else if (decl->value->next->next->next->next == NULL) /* 4 values */ { do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */ do_padding_property_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* right */ do_padding_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */ do_padding_property_term (node, decl->value->next->next->next, TRUE, FALSE, FALSE, FALSE); /* left */ } else { g_warning ("Too many values for padding property"); return; } } else { if (decl->value == NULL || decl->value->next != NULL) return; if (strcmp (property_name, "-left") == 0) do_padding_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE); else if (strcmp (property_name, "-right") == 0) do_padding_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE); else if (strcmp (property_name, "-top") == 0) do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); else if (strcmp (property_name, "-bottom") == 0) do_padding_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE); } } static void do_size_property (StThemeNode *node, CRDeclaration *decl, int *node_value) { get_length_from_term_int (node, decl->value, FALSE, node_value); } void _st_theme_node_ensure_geometry (StThemeNode *node) { int i, j; if (node->geometry_computed) return; node->geometry_computed = TRUE; ensure_properties (node); for (j = 0; j < 4; j++) { node->border_width[j] = 0; node->border_color[j] = TRANSPARENT_COLOR; } node->outline_width = 0; node->outline_color = TRANSPARENT_COLOR; node->width = -1; node->height = -1; node->min_width = -1; node->min_height = -1; node->max_width = -1; node->max_height = -1; for (i = 0; i < node->n_properties; i++) { CRDeclaration *decl = node->properties[i]; const char *property_name = decl->property->stryng->str; if (g_str_has_prefix (property_name, "border")) do_border_property (node, decl); else if (g_str_has_prefix (property_name, "outline")) do_outline_property (node, decl); else if (g_str_has_prefix (property_name, "padding")) do_padding_property (node, decl); else if (strcmp (property_name, "width") == 0) do_size_property (node, decl, &node->width); else if (strcmp (property_name, "height") == 0) do_size_property (node, decl, &node->height); else if (strcmp (property_name, "min-width") == 0) do_size_property (node, decl, &node->min_width); else if (strcmp (property_name, "min-height") == 0) do_size_property (node, decl, &node->min_height); else if (strcmp (property_name, "max-width") == 0) do_size_property (node, decl, &node->max_width); else if (strcmp (property_name, "max-height") == 0) do_size_property (node, decl, &node->max_height); } if (node->width != -1) { if (node->min_width == -1) node->min_width = node->width; else if (node->width < node->min_width) node->width = node->min_width; if (node->max_width == -1) node->max_width = node->width; else if (node->width > node->max_width) node->width = node->max_width; } if (node->height != -1) { if (node->min_height == -1) node->min_height = node->height; else if (node->height < node->min_height) node->height = node->min_height; if (node->max_height == -1) node->max_height = node->height; else if (node->height > node->max_height) node->height = node->max_height; } } int st_theme_node_get_border_width (StThemeNode *node, StSide side) { g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.); _st_theme_node_ensure_geometry (node); return node->border_width[side]; } int st_theme_node_get_border_radius (StThemeNode *node, StCorner corner) { g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.); _st_theme_node_ensure_geometry (node); return node->border_radius[corner]; } int st_theme_node_get_outline_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), 0); _st_theme_node_ensure_geometry (node); return node->outline_width; } /** * st_theme_node_get_outline_color: * @node: a #StThemeNode * @color: (out caller-allocates): location to store the color * * Returns the color of @node's outline. */ void st_theme_node_get_outline_color (StThemeNode *node, ClutterColor *color) { g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_geometry (node); *color = node->outline_color; } int st_theme_node_get_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->width; } int st_theme_node_get_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->height; } int st_theme_node_get_min_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->min_width; } int st_theme_node_get_min_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->min_height; } int st_theme_node_get_max_width (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->max_width; } int st_theme_node_get_max_height (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), -1); _st_theme_node_ensure_geometry (node); return node->max_height; } static GetFromTermResult get_background_color_from_term (StThemeNode *node, CRTerm *term, ClutterColor *color) { GetFromTermResult result = get_color_from_term (node, term, color); if (result == VALUE_NOT_FOUND) { if (term_is_transparent (term)) { *color = TRANSPARENT_COLOR; return VALUE_FOUND; } } return result; } void _st_theme_node_ensure_background (StThemeNode *node) { int i; if (node->background_computed) return; node->background_repeat = FALSE; node->background_computed = TRUE; node->background_color = TRANSPARENT_COLOR; node->background_gradient_type = ST_GRADIENT_NONE; node->background_position_set = FALSE; node->background_size = ST_BACKGROUND_SIZE_AUTO; ensure_properties (node); for (i = 0; i < node->n_properties; i++) { CRDeclaration *decl = node->properties[i]; const char *property_name = decl->property->stryng->str; if (g_str_has_prefix (property_name, "background")) property_name += 10; else continue; if (strcmp (property_name, "") == 0) { /* We're very liberal here ... if we recognize any term in the expression we take it, and * we ignore the rest. The actual specification is: * * background: [<'background-color'> || <'background-image'> || <'background-repeat'> || <'background-attachment'> || <'background-position'>] | inherit */ CRTerm *term; /* background: property sets all terms to specified or default values */ node->background_color = TRANSPARENT_COLOR; g_free (node->background_image); node->background_image = NULL; node->background_position_set = FALSE; node->background_size = ST_BACKGROUND_SIZE_AUTO; for (term = decl->value; term; term = term->next) { GetFromTermResult result = get_background_color_from_term (node, term, &node->background_color); if (result == VALUE_FOUND) { /* color stored in node->background_color */ } else if (result == VALUE_INHERIT) { if (node->parent_node) { st_theme_node_get_background_color (node->parent_node, &node->background_color); node->background_image = g_strdup (st_theme_node_get_background_image (node->parent_node)); node->background_bumpmap = g_strdup (st_theme_node_get_background_bumpmap (node->parent_node)); } } else if (term_is_none (term)) { /* leave node->background_color as transparent */ } else if (term->type == TERM_URI) { CRStyleSheet *base_stylesheet; if (decl->parent_statement != NULL) base_stylesheet = decl->parent_statement->parent_sheet; else base_stylesheet = NULL; node->background_image = _st_theme_resolve_url (node->theme, base_stylesheet, term->content.str->stryng->str); } } } else if (strcmp (property_name, "-position") == 0) { GetFromTermResult result = get_length_from_term_int (node, decl->value, FALSE, &node->background_position_x); if (result == VALUE_NOT_FOUND) { node->background_position_set = FALSE; continue; } else node->background_position_set = TRUE; result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_position_y); if (result == VALUE_NOT_FOUND) { node->background_position_set = FALSE; continue; } else node->background_position_set = TRUE; } else if (strcmp (property_name, "-repeat") == 0) { if (decl->value->type == TERM_IDENT) { if (strcmp (decl->value->content.str->stryng->str, "repeat") == 0) node->background_repeat = TRUE; } } else if (strcmp (property_name, "-size") == 0) { if (decl->value->type == TERM_IDENT) { if (strcmp (decl->value->content.str->stryng->str, "contain") == 0) node->background_size = ST_BACKGROUND_SIZE_CONTAIN; else if (strcmp (decl->value->content.str->stryng->str, "cover") == 0) node->background_size = ST_BACKGROUND_SIZE_COVER; else if ((strcmp (decl->value->content.str->stryng->str, "auto") == 0) && (decl->value->next) && (decl->value->next->type == TERM_NUMBER)) { GetFromTermResult result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h); node->background_size_w = -1; node->background_size = (result == VALUE_FOUND) ? ST_BACKGROUND_SIZE_FIXED : ST_BACKGROUND_SIZE_AUTO; } else node->background_size = ST_BACKGROUND_SIZE_AUTO; } else if (decl->value->type == TERM_NUMBER) { GetFromTermResult result = get_length_from_term_int (node, decl->value, FALSE, &node->background_size_w); if (result == VALUE_NOT_FOUND) continue; node->background_size = ST_BACKGROUND_SIZE_FIXED; if ((decl->value->next) && (decl->value->next->type == TERM_NUMBER)) { result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h); if (result == VALUE_FOUND) continue; } node->background_size_h = -1; } else node->background_size = ST_BACKGROUND_SIZE_AUTO; } else if (strcmp (property_name, "-color") == 0) { GetFromTermResult result; if (decl->value == NULL || decl->value->next != NULL) continue; result = get_background_color_from_term (node, decl->value, &node->background_color); if (result == VALUE_FOUND) { /* color stored in node->background_color */ } else if (result == VALUE_INHERIT) { if (node->parent_node) st_theme_node_get_background_color (node->parent_node, &node->background_color); } } else if (strcmp (property_name, "-image") == 0) { if (decl->value == NULL || decl->value->next != NULL) continue; if (decl->value->type == TERM_URI) { CRStyleSheet *base_stylesheet; if (decl->parent_statement != NULL) base_stylesheet = decl->parent_statement->parent_sheet; else base_stylesheet = NULL; g_free (node->background_image); node->background_image = _st_theme_resolve_url (node->theme, base_stylesheet, decl->value->content.str->stryng->str); } else if (term_is_inherit (decl->value)) { g_free (node->background_image); node->background_image = g_strdup (st_theme_node_get_background_image (node->parent_node)); } else if (term_is_none (decl->value)) { g_free (node->background_image); node->background_image = NULL; } } else if (strcmp (property_name, "-bumpmap") == 0) { if (decl->value == NULL || decl->value->next != NULL) continue; if (decl->value->type == TERM_URI) { CRStyleSheet *base_stylesheet; if (decl->parent_statement != NULL) base_stylesheet = decl->parent_statement->parent_sheet; else base_stylesheet = NULL; g_free (node->background_bumpmap); node->background_bumpmap = _st_theme_resolve_url (node->theme, base_stylesheet, decl->value->content.str->stryng->str); } else if (term_is_inherit (decl->value)) { g_free (node->background_bumpmap); node->background_bumpmap = g_strdup (st_theme_node_get_background_bumpmap (node->parent_node)); } else if (term_is_none(decl->value)) { g_free (node->background_bumpmap); } } else if (strcmp (property_name, "-gradient-direction") == 0) { CRTerm *term = decl->value; if (strcmp (term->content.str->stryng->str, "vertical") == 0) { node->background_gradient_type = ST_GRADIENT_VERTICAL; } else if (strcmp (term->content.str->stryng->str, "horizontal") == 0) { node->background_gradient_type = ST_GRADIENT_HORIZONTAL; } else if (strcmp (term->content.str->stryng->str, "radial") == 0) { node->background_gradient_type = ST_GRADIENT_RADIAL; } else if (strcmp (term->content.str->stryng->str, "none") == 0) { node->background_gradient_type = ST_GRADIENT_NONE; } else { g_warning ("Unrecognized background-gradient-direction \"%s\"", term->content.str->stryng->str); } } else if (strcmp (property_name, "-gradient-start") == 0) { get_color_from_term (node, decl->value, &node->background_color); } else if (strcmp (property_name, "-gradient-end") == 0) { get_color_from_term (node, decl->value, &node->background_gradient_end); } } } /** * st_theme_node_get_background_color: * @node: a #StThemeNode * @color: (out caller-allocates): location to store the color * * Returns @node's background color. */ void st_theme_node_get_background_color (StThemeNode *node, ClutterColor *color) { g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_background (node); *color = node->background_color; } /** * st_theme_node_get_background_image: * @node: a #StThemeNode * * Returns @node's background image. */ const char * st_theme_node_get_background_image (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); _st_theme_node_ensure_background (node); return node->background_image; } /** * st_theme_node_get_background_bumpmap: * @node: a #StThemeNode * * Returns @node's background bumpmap. */ const char * st_theme_node_get_background_bumpmap (StThemeNode *node) { g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); _st_theme_node_ensure_background (node); return node->background_bumpmap; } /** * st_theme_node_get_foreground_color: * @node: a #StThemeNode * @color: (out caller-allocates): location to store the color * * Returns @node's foreground color. */ void st_theme_node_get_foreground_color (StThemeNode *node, ClutterColor *color) { g_return_if_fail (ST_IS_THEME_NODE (node)); if (!node->foreground_computed) { int i; node->foreground_computed = TRUE; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, "color") == 0) { GetFromTermResult result = get_color_from_term (node, decl->value, &node->foreground_color); if (result == VALUE_FOUND) goto out; else if (result == VALUE_INHERIT) break; } } if (node->parent_node) st_theme_node_get_foreground_color (node->parent_node, &node->foreground_color); else node->foreground_color = BLACK_COLOR; /* default to black */ } out: *color = node->foreground_color; } /** * st_theme_node_get_background_gradient: * @node: A #StThemeNode * @type: (out): Type of gradient * @start: (out caller-allocates): Color at start of gradient * @end: (out caller-allocates): Color at end of gradient * * The @start and @end arguments will only be set if @type is not #ST_GRADIENT_NONE. */ void st_theme_node_get_background_gradient (StThemeNode *node, StGradientType *type, ClutterColor *start, ClutterColor *end) { g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_background (node); *type = node->background_gradient_type; if (*type != ST_GRADIENT_NONE) { *start = node->background_color; *end = node->background_gradient_end; } } /** * st_theme_node_get_border_color: * @node: a #StThemeNode * @side: a #StSide * @color: (out caller-allocates): location to store the color * * Returns the color of @node's border on @side */ void st_theme_node_get_border_color (StThemeNode *node, StSide side, ClutterColor *color) { g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT); _st_theme_node_ensure_geometry (node); *color = node->border_color[side]; } double st_theme_node_get_padding (StThemeNode *node, StSide side) { g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.); g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.); _st_theme_node_ensure_geometry (node); return node->padding[side]; } /** * st_theme_node_get_transition_duration: * @node: an #StThemeNode * * Get the value of the transition-duration property, which * specifies the transition time between the previous #StThemeNode * and @node. * * Returns: the node's transition duration in milliseconds */ int st_theme_node_get_transition_duration (StThemeNode *node) { gdouble value = 0.0; g_return_val_if_fail (ST_IS_THEME_NODE (node), 0); if (node->transition_duration > -1) return st_slow_down_factor * node->transition_duration; st_theme_node_lookup_double (node, "transition-duration", FALSE, &value); node->transition_duration = (int)value; return st_slow_down_factor * node->transition_duration; } StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node) { int i; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, "text-decoration") == 0) { CRTerm *term = decl->value; StTextDecoration decoration = 0; /* Specification is none | [ underline || overline || line-through || blink ] | inherit * * We're a bit more liberal, and for example treat 'underline none' as the same as * none. */ for (; term; term = term->next) { if (term->type != TERM_IDENT) goto next_decl; if (strcmp (term->content.str->stryng->str, "none") == 0) { return 0; } else if (strcmp (term->content.str->stryng->str, "inherit") == 0) { if (node->parent_node) return st_theme_node_get_text_decoration (node->parent_node); } else if (strcmp (term->content.str->stryng->str, "underline") == 0) { decoration |= ST_TEXT_DECORATION_UNDERLINE; } else if (strcmp (term->content.str->stryng->str, "overline") == 0) { decoration |= ST_TEXT_DECORATION_OVERLINE; } else if (strcmp (term->content.str->stryng->str, "line-through") == 0) { decoration |= ST_TEXT_DECORATION_LINE_THROUGH; } else if (strcmp (term->content.str->stryng->str, "blink") == 0) { decoration |= ST_TEXT_DECORATION_BLINK; } else { goto next_decl; } } return decoration; } next_decl: ; } return 0; } StTextAlign st_theme_node_get_text_align(StThemeNode *node) { int i; ensure_properties(node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp(decl->property->stryng->str, "text-align") == 0) { CRTerm *term = decl->value; if (term->type != TERM_IDENT || term->next) continue; if (strcmp(term->content.str->stryng->str, "inherit") == 0) { if (node->parent_node) return st_theme_node_get_text_align(node->parent_node); return ST_TEXT_ALIGN_LEFT; } else if (strcmp(term->content.str->stryng->str, "left") == 0) { return ST_TEXT_ALIGN_LEFT; } else if (strcmp(term->content.str->stryng->str, "right") == 0) { return ST_TEXT_ALIGN_RIGHT; } else if (strcmp(term->content.str->stryng->str, "center") == 0) { return ST_TEXT_ALIGN_CENTER; } else if (strcmp(term->content.str->stryng->str, "justify") == 0) { return ST_TEXT_ALIGN_JUSTIFY; } } } if(node->parent_node) return st_theme_node_get_text_align(node->parent_node); return ST_TEXT_ALIGN_LEFT; } static gboolean font_family_from_terms (CRTerm *term, char **family) { GString *family_string; gboolean result = FALSE; gboolean last_was_quoted = FALSE; if (!term) return FALSE; family_string = g_string_new (NULL); while (term) { if (term->type != TERM_STRING && term->type != TERM_IDENT) { goto out; } if (family_string->len > 0) { if (term->the_operator != COMMA && term->the_operator != NO_OP) goto out; /* Can concatenate two bare words, but not two quoted strings */ if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING) goto out; if (term->the_operator == NO_OP) g_string_append (family_string, " "); else g_string_append (family_string, ", "); } else { if (term->the_operator != NO_OP) goto out; } g_string_append (family_string, term->content.str->stryng->str); term = term->next; } result = TRUE; out: if (result) { *family = g_string_free (family_string, FALSE); return TRUE; } else { *family = g_string_free (family_string, TRUE); return FALSE; } } /* In points */ static int font_sizes[] = { 6 * 1024, /* xx-small */ 8 * 1024, /* x-small */ 10 * 1024, /* small */ 12 * 1024, /* medium */ 16 * 1024, /* large */ 20 * 1024, /* x-large */ 24 * 1024, /* xx-large */ }; static gboolean font_size_from_term (StThemeNode *node, CRTerm *term, double *size) { if (term->type == TERM_IDENT) { double resolution = clutter_backend_get_resolution (clutter_get_default_backend ()); /* We work in integers to avoid double comparisons when converting back * from a size in pixels to a logical size. */ int size_points = (int)(0.5 + *size * (72. / resolution)); if (strcmp (term->content.str->stryng->str, "xx-small") == 0) size_points = font_sizes[0]; else if (strcmp (term->content.str->stryng->str, "x-small") == 0) size_points = font_sizes[1]; else if (strcmp (term->content.str->stryng->str, "small") == 0) size_points = font_sizes[2]; else if (strcmp (term->content.str->stryng->str, "medium") == 0) size_points = font_sizes[3]; else if (strcmp (term->content.str->stryng->str, "large") == 0) size_points = font_sizes[4]; else if (strcmp (term->content.str->stryng->str, "x-large") == 0) size_points = font_sizes[5]; else if (strcmp (term->content.str->stryng->str, "xx-large") == 0) size_points = font_sizes[6]; else if (strcmp (term->content.str->stryng->str, "smaller") == 0) { /* Find the standard size equal to or smaller than the current size */ int i = 0; while (i <= 6 && font_sizes[i] < size_points) i++; if (i > 6) { /* original size greater than any standard size */ size_points = (int)(0.5 + size_points / 1.2); } else { /* Go one smaller than that, if possible */ if (i > 0) i--; size_points = font_sizes[i]; } } else if (strcmp (term->content.str->stryng->str, "larger") == 0) { /* Find the standard size equal to or larger than the current size */ int i = 6; while (i >= 0 && font_sizes[i] > size_points) i--; if (i < 0) /* original size smaller than any standard size */ i = 0; /* Go one larger than that, if possible */ if (i < 6) i++; size_points = font_sizes[i]; } else { return FALSE; } *size = size_points * (resolution / 72.); return TRUE; } else if (term->type == TERM_NUMBER && term->content.num->type == NUM_PERCENTAGE) { *size *= term->content.num->val / 100.; return TRUE; } else if (get_length_from_term (node, term, TRUE, size) == VALUE_FOUND) { /* Convert from pixels to Pango units */ *size *= 1024; return TRUE; } return FALSE; } static gboolean font_weight_from_term (CRTerm *term, PangoWeight *weight, gboolean *weight_absolute) { if (term->type == TERM_NUMBER) { int weight_int; /* The spec only allows numeric weights from 100-900, though Pango * will handle any number. We just let anything through. */ if (term->content.num->type != NUM_GENERIC) return FALSE; weight_int = (int)(0.5 + term->content.num->val); *weight = weight_int; *weight_absolute = TRUE; } else if (term->type == TERM_IDENT) { /* FIXME: handle INHERIT */ if (strcmp (term->content.str->stryng->str, "bold") == 0) { *weight = PANGO_WEIGHT_BOLD; *weight_absolute = TRUE; } else if (strcmp (term->content.str->stryng->str, "normal") == 0) { *weight = PANGO_WEIGHT_NORMAL; *weight_absolute = TRUE; } else if (strcmp (term->content.str->stryng->str, "bolder") == 0) { *weight = PANGO_WEIGHT_BOLD; *weight_absolute = FALSE; } else if (strcmp (term->content.str->stryng->str, "lighter") == 0) { *weight = PANGO_WEIGHT_LIGHT; *weight_absolute = FALSE; } else { return FALSE; } } else { return FALSE; } return TRUE; } static gboolean font_style_from_term (CRTerm *term, PangoStyle *style) { if (term->type != TERM_IDENT) return FALSE; /* FIXME: handle INHERIT */ if (strcmp (term->content.str->stryng->str, "normal") == 0) *style = PANGO_STYLE_NORMAL; else if (strcmp (term->content.str->stryng->str, "oblique") == 0) *style = PANGO_STYLE_OBLIQUE; else if (strcmp (term->content.str->stryng->str, "italic") == 0) *style = PANGO_STYLE_ITALIC; else return FALSE; return TRUE; } static gboolean font_variant_from_term (CRTerm *term, PangoVariant *variant) { if (term->type != TERM_IDENT) return FALSE; /* FIXME: handle INHERIT */ if (strcmp (term->content.str->stryng->str, "normal") == 0) *variant = PANGO_VARIANT_NORMAL; else if (strcmp (term->content.str->stryng->str, "small-caps") == 0) *variant = PANGO_VARIANT_SMALL_CAPS; else return FALSE; return TRUE; } const PangoFontDescription * st_theme_node_get_font (StThemeNode *node) { /* Initialized despite _set flags to suppress compiler warnings */ PangoStyle font_style = PANGO_STYLE_NORMAL; gboolean font_style_set = FALSE; PangoVariant variant = PANGO_VARIANT_NORMAL; gboolean variant_set = FALSE; PangoWeight weight = PANGO_WEIGHT_NORMAL; gboolean weight_absolute = TRUE; gboolean weight_set = FALSE; double size = 0.; gboolean size_set = FALSE; char *family = NULL; double parent_size; int i; if (node->font_desc) return node->font_desc; node->font_desc = pango_font_description_copy (get_parent_font (node)); parent_size = pango_font_description_get_size (node->font_desc); if (!pango_font_description_get_size_is_absolute (node->font_desc)) { double resolution = clutter_backend_get_resolution (clutter_get_default_backend ()); parent_size *= (resolution / 72.); } ensure_properties (node); for (i = 0; i < node->n_properties; i++) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, "font") == 0) { PangoStyle tmp_style = PANGO_STYLE_NORMAL; PangoVariant tmp_variant = PANGO_VARIANT_NORMAL; PangoWeight tmp_weight = PANGO_WEIGHT_NORMAL; gboolean tmp_weight_absolute = TRUE; double tmp_size; CRTerm *term = decl->value; /* A font specification starts with node/variant/weight * in any order. Each is allowed to be specified only once, * but we don't enforce that. */ for (; term; term = term->next) { if (font_style_from_term (term, &tmp_style)) continue; if (font_variant_from_term (term, &tmp_variant)) continue; if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute)) continue; break; } /* The size is mandatory */ if (term == NULL || term->type != TERM_NUMBER) { g_warning ("Size missing from font property"); continue; } tmp_size = parent_size; if (!font_size_from_term (node, term, &tmp_size)) { g_warning ("Couldn't parse size in font property"); continue; } term = term->next; if (term != NULL && term->type && TERM_NUMBER && term->the_operator == DIVIDE) { /* Ignore line-height specification */ term = term->next; } /* the font family is mandatory - it is a comma-separated list of * names. */ if (!font_family_from_terms (term, &family)) { g_warning ("Couldn't parse family in font property"); continue; } font_style = tmp_style; font_style_set = TRUE; weight = tmp_weight; weight_absolute = tmp_weight_absolute; weight_set = TRUE; variant = tmp_variant; variant_set = TRUE; size = tmp_size; size_set = TRUE; } else if (strcmp (decl->property->stryng->str, "font-family") == 0) { if (!font_family_from_terms (decl->value, &family)) { g_warning ("Couldn't parse family in font property"); continue; } } else if (strcmp (decl->property->stryng->str, "font-weight") == 0) { if (decl->value == NULL || decl->value->next != NULL) continue; if (font_weight_from_term (decl->value, &weight, &weight_absolute)) weight_set = TRUE; } else if (strcmp (decl->property->stryng->str, "font-style") == 0) { if (decl->value == NULL || decl->value->next != NULL) continue; if (font_style_from_term (decl->value, &font_style)) font_style_set = TRUE; } else if (strcmp (decl->property->stryng->str, "font-variant") == 0) { if (decl->value == NULL || decl->value->next != NULL) continue; if (font_variant_from_term (decl->value, &variant)) variant_set = TRUE; } else if (strcmp (decl->property->stryng->str, "font-size") == 0) { gdouble tmp_size; if (decl->value == NULL || decl->value->next != NULL) continue; tmp_size = parent_size; if (font_size_from_term (node, decl->value, &tmp_size)) { size = tmp_size; size_set = TRUE; } } } if (family) { pango_font_description_set_family (node->font_desc, family); g_free (family); } if (size_set) pango_font_description_set_absolute_size (node->font_desc, size); if (weight_set) { if (!weight_absolute) { /* bolder/lighter are supposed to switch between available styles, but with * font substitution, that gets to be a pretty fuzzy concept. So we use * a fixed step of 200. (The spec says 100, but that might not take us from * normal to bold. */ PangoWeight old_weight = pango_font_description_get_weight (node->font_desc); if (weight == PANGO_WEIGHT_BOLD) weight = old_weight + 200; else weight = old_weight - 200; if (weight < 100) weight = 100; if (weight > 900) weight = 900; } pango_font_description_set_weight (node->font_desc, weight); } if (font_style_set) pango_font_description_set_style (node->font_desc, font_style); if (variant_set) pango_font_description_set_variant (node->font_desc, variant); return node->font_desc; } /** * st_theme_node_get_border_image: * @node: a #StThemeNode * * Gets the value for the border-image style property * * Return value: (transfer none): the border image, or %NULL * if there is no border image. */ StBorderImage * st_theme_node_get_border_image (StThemeNode *node) { int i, scale_factor; if (node->border_image_computed) return node->border_image; scale_factor = 1; g_object_get (node->context, "scale-factor", &scale_factor, NULL); node->border_image = NULL; node->border_image_computed = TRUE; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, "border-image") == 0) { CRTerm *term = decl->value; CRStyleSheet *base_stylesheet; int borders[4]; int n_borders = 0; int i; const char *url; int border_top; int border_right; int border_bottom; int border_left; char *filename; /* Support border-image: none; to suppress a previously specified border image */ if (term_is_none (term)) { if (term->next == NULL) return NULL; else goto next_property; } /* First term must be the URL to the image */ if (term->type != TERM_URI) goto next_property; url = term->content.str->stryng->str; term = term->next; /* Followed by 0 to 4 numbers or percentages. *Not lengths*. The interpretation * of a number is supposed to be pixels if the image is pixel based, otherwise CSS pixels. */ for (i = 0; i < 4; i++) { if (term == NULL) break; if (term->type != TERM_NUMBER) goto next_property; if (term->content.num->type == NUM_GENERIC) { borders[n_borders] = (int)(0.5 + term->content.num->val) * scale_factor; n_borders++; } else if (term->content.num->type == NUM_PERCENTAGE) { /* This would be easiest to support if we moved image handling into StBorderImage */ g_warning ("Percentages not supported for border-image"); goto next_property; } else goto next_property; term = term->next; } switch (n_borders) { case 0: border_top = border_right = border_bottom = border_left = 0; break; case 1: border_top = border_right = border_bottom = border_left = borders[0]; break; case 2: border_top = border_bottom = borders[0]; border_left = border_right = borders[1]; break; case 3: border_top = borders[0]; border_left = border_right = borders[1]; border_bottom = borders[2]; break; case 4: default: border_top = borders[0]; border_right = borders[1]; border_bottom = borders[2]; border_left = borders[3]; break; } if (decl->parent_statement != NULL) base_stylesheet = decl->parent_statement->parent_sheet; else base_stylesheet = NULL; filename = _st_theme_resolve_url (node->theme, base_stylesheet, url); if (filename == NULL) goto next_property; node->border_image = st_border_image_new (filename, border_top, border_right, border_bottom, border_left); g_free (filename); return node->border_image; } next_property: ; } return NULL; } /** * st_theme_node_get_horizontal_padding: * @node: a #StThemeNode * * Gets the total horizonal padding (left + right padding) * * Return value: the total horizonal padding * in pixels */ double st_theme_node_get_horizontal_padding (StThemeNode *node) { double padding = 0.0; padding += st_theme_node_get_padding (node, ST_SIDE_LEFT); padding += st_theme_node_get_padding (node, ST_SIDE_RIGHT); return padding; } /** * st_theme_node_get_vertical_padding: * @node: a #StThemeNode * * Gets the total vertical padding (top + bottom padding) * * Return value: the total vertical padding * in pixels */ double st_theme_node_get_vertical_padding (StThemeNode *node) { double padding = 0.0; padding += st_theme_node_get_padding (node, ST_SIDE_TOP); padding += st_theme_node_get_padding (node, ST_SIDE_BOTTOM); return padding; } static GetFromTermResult parse_shadow_property (StThemeNode *node, CRDeclaration *decl, ClutterColor *color, gdouble *xoffset, gdouble *yoffset, gdouble *blur, gdouble *spread, gboolean *inset) { GetFromTermResult result; CRTerm *term; int n_offsets = 0; /* default values */ color->red = 0x0; color->green = 0x0; color->blue = 0x0; color->alpha = 0xff; *xoffset = 0.; *yoffset = 0.; *blur = 0.; *spread = 0.; *inset = FALSE; /* The CSS3 draft of the box-shadow property[0] is a lot stricter * regarding the order of terms: * If the 'inset' keyword is specified, it has to be first or last, * and the color may not be mixed with the lengths; while we parse * length values in the correct order, we allow for arbitrary * placement of the color and 'inset' keyword. * * [0] http://www.w3.org/TR/css3-background/#box-shadow */ for (term = decl->value; term; term = term->next) { if (term->type == TERM_NUMBER) { gdouble value; gdouble multiplier; multiplier = (term->unary_op == MINUS_UOP) ? -1. : 1.; result = get_length_from_term (node, term, FALSE, &value); if (result == VALUE_INHERIT) { /* we only allow inherit on the line by itself */ if (n_offsets > 0) return VALUE_NOT_FOUND; else return VALUE_INHERIT; } else if (result == VALUE_FOUND) { switch (n_offsets++) { case 0: *xoffset = multiplier * value; break; case 1: *yoffset = multiplier * value; break; case 2: if (multiplier < 0) g_warning ("Negative blur values are " "not allowed"); *blur = value; break; case 3: if (multiplier < 0) g_warning ("Negative spread values are " "not allowed"); *spread = value; break; } continue; } } else if (term->type == TERM_IDENT && strcmp (term->content.str->stryng->str, "inset") == 0) { *inset = TRUE; continue; } result = get_color_from_term (node, term, color); if (result == VALUE_INHERIT) { if (n_offsets > 0) return VALUE_NOT_FOUND; else return VALUE_INHERIT; } else if (result == VALUE_FOUND) { continue; } } /* The only required terms are the x and y offsets */ if (n_offsets >= 2) return VALUE_FOUND; else return VALUE_NOT_FOUND; } /** * st_theme_node_lookup_shadow: * @node: a #StThemeNode * @property_name: The name of the shadow property * @inherit: if %TRUE, if a value is not found for the property on the * node, then it will be looked up on the parent node, and then on the * parent's parent, and so forth. Note that if the property has a * value of 'inherit' it will be inherited even if %FALSE is passed * in for @inherit; this only affects the default behavior for inheritance. * @shadow: (out): location to store the shadow * * If the property is not found, the value in the shadow variable will not * be changed. * * Generically looks up a property containing a set of shadow values. When * specific getters (like st_theme_node_get_box_shadow ()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * See also st_theme_node_get_shadow(), which provides a simpler API. * * Return value: %TRUE if the property was found in the properties for this * theme node (or in the properties of parent nodes when inheriting.) */ gboolean st_theme_node_lookup_shadow (StThemeNode *node, const char *property_name, gboolean inherit, StShadow **shadow) { ClutterColor color = { 0., }; gdouble xoffset = 0.; gdouble yoffset = 0.; gdouble blur = 0.; gdouble spread = 0.; gboolean inset = FALSE; int i; ensure_properties (node); for (i = node->n_properties - 1; i >= 0; i--) { CRDeclaration *decl = node->properties[i]; if (strcmp (decl->property->stryng->str, property_name) == 0) { GetFromTermResult result = parse_shadow_property (node, decl, &color, &xoffset, &yoffset, &blur, &spread, &inset); if (result == VALUE_FOUND) { *shadow = st_shadow_new (&color, xoffset, yoffset, blur, spread, inset); return TRUE; } else if (result == VALUE_INHERIT) { if (node->parent_node) return st_theme_node_lookup_shadow (node->parent_node, property_name, inherit, shadow); else break; } } } if (inherit && node->parent_node) return st_theme_node_lookup_shadow (node->parent_node, property_name, inherit, shadow); return FALSE; } /** * st_theme_node_get_shadow: * @node: a #StThemeNode * @property_name: The name of the shadow property * * Generically looks up a property containing a set of shadow values. When * specific getters (like st_theme_node_get_box_shadow()) exist, they * should be used instead. They are cached, so more efficient, and have * handling for shortcut properties and other details of CSS. * * Like st_theme_get_length(), this does not print a warning if the property is * not found; it just returns %NULL * * See also st_theme_node_lookup_shadow (), which provides more options. * * Return value: (transfer full): the shadow, or %NULL if the property was not found. */ StShadow * st_theme_node_get_shadow (StThemeNode *node, const char *property_name) { StShadow *shadow; if (st_theme_node_lookup_shadow (node, property_name, FALSE, &shadow)) return shadow; else return NULL; } /** * st_theme_node_get_box_shadow: * @node: a #StThemeNode * * Gets the value for the box-shadow style property * * Return value: (transfer none): the node's shadow, or %NULL * if node has no shadow */ StShadow * st_theme_node_get_box_shadow (StThemeNode *node) { StShadow *shadow; if (node->box_shadow_computed) return node->box_shadow; node->box_shadow = NULL; node->box_shadow_computed = TRUE; if (st_theme_node_lookup_shadow (node, "box-shadow", FALSE, &shadow)) { node->box_shadow = shadow; return node->box_shadow; } return NULL; } /** * st_theme_node_get_background_image_shadow: * @node: a #StThemeNode * * Gets the value for the -st-background-image-shadow style property * * Return value: (transfer none): the node's background image shadow, or %NULL * if node has no such shadow */ StShadow * st_theme_node_get_background_image_shadow (StThemeNode *node) { StShadow *shadow; if (node->background_image_shadow_computed) return node->background_image_shadow; node->background_image_shadow = NULL; node->background_image_shadow_computed = TRUE; if (st_theme_node_lookup_shadow (node, "-st-background-image-shadow", FALSE, &shadow)) { if (shadow->inset) { g_warning ("The -st-background-image-shadow property does not " "support inset shadows"); st_shadow_unref (shadow); shadow = NULL; } node->background_image_shadow = shadow; return node->background_image_shadow; } return NULL; } /** * st_theme_node_get_text_shadow: * @node: a #StThemeNode * * Gets the value for the text-shadow style property * * Return value: (transfer none): the node's text-shadow, or %NULL * if node has no text-shadow */ StShadow * st_theme_node_get_text_shadow (StThemeNode *node) { StShadow *result = NULL; if (node->text_shadow_computed) return node->text_shadow; ensure_properties (node); if (!st_theme_node_lookup_shadow (node, "text-shadow", FALSE, &result)) { if (node->parent_node) { result = st_theme_node_get_text_shadow (node->parent_node); if (result) st_shadow_ref (result); } } if (result && result->inset) { g_warning ("The text-shadow property does not support inset shadows"); st_shadow_unref (result); result = NULL; } node->text_shadow = result; node->text_shadow_computed = TRUE; return result; } /** * st_theme_node_get_icon_colors: * @node: a #StThemeNode * * Gets the colors that should be used for colorizing symbolic icons according * the style of this node. * * Return value: (transfer none): the icon colors to use for this theme node */ StIconColors * st_theme_node_get_icon_colors (StThemeNode *node) { /* Foreground here will always be the same as st_theme_node_get_foreground_color(), * but there's a loss of symmetry and little efficiency win if we try to exploit * that. */ enum { FOREGROUND = 1 << 0, WARNING = 1 << 1, ERROR = 1 << 2, SUCCESS = 1 << 3 }; gboolean shared_with_parent; int i; ClutterColor color = { 0, }; guint still_need = FOREGROUND | WARNING | ERROR | SUCCESS; g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL); if (node->icon_colors) return node->icon_colors; if (node->parent_node) { node->icon_colors = st_theme_node_get_icon_colors (node->parent_node); shared_with_parent = TRUE; } else { node->icon_colors = st_icon_colors_new (); node->icon_colors->foreground = BLACK_COLOR; node->icon_colors->warning = DEFAULT_WARNING_COLOR; node->icon_colors->error = DEFAULT_ERROR_COLOR; node->icon_colors->success = DEFAULT_SUCCESS_COLOR; shared_with_parent = FALSE; } ensure_properties (node); for (i = node->n_properties - 1; i >= 0 && still_need != 0; i--) { CRDeclaration *decl = node->properties[i]; GetFromTermResult result = VALUE_NOT_FOUND; guint found = 0; if ((still_need & FOREGROUND) != 0 && strcmp (decl->property->stryng->str, "color") == 0) { found = FOREGROUND; result = get_color_from_term (node, decl->value, &color); } else if ((still_need & WARNING) != 0 && strcmp (decl->property->stryng->str, "warning-color") == 0) { found = WARNING; result = get_color_from_term (node, decl->value, &color); } else if ((still_need & ERROR) != 0 && strcmp (decl->property->stryng->str, "error-color") == 0) { found = ERROR; result = get_color_from_term (node, decl->value, &color); } else if ((still_need & SUCCESS) != 0 && strcmp (decl->property->stryng->str, "success-color") == 0) { found = SUCCESS; result = get_color_from_term (node, decl->value, &color); } if (result == VALUE_INHERIT) { still_need &= ~found; } else if (result == VALUE_FOUND) { still_need &= ~found; if (shared_with_parent) { node->icon_colors = st_icon_colors_copy (node->icon_colors); shared_with_parent = FALSE; } switch (found) { case FOREGROUND: node->icon_colors->foreground = color; break; case WARNING: node->icon_colors->warning = color; break; case ERROR: node->icon_colors->error = color; break; case SUCCESS: node->icon_colors->success = color; break; } } } if (shared_with_parent) st_icon_colors_ref (node->icon_colors); return node->icon_colors; } static float get_width_inc (StThemeNode *node) { return ((int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT] + (int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]); } static float get_height_inc (StThemeNode *node) { return ((int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP] + (int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]); } /** * st_theme_node_adjust_for_height: * @node: a #StThemeNode * @for_height: (inout): the "for height" to adjust * * Adjusts a "for height" passed to clutter_actor_get_preferred_width() to * account for borders and padding. This is a convenience function meant * to be called from a get_preferred_width() method of a #ClutterActor * subclass. The value after adjustment is the height available for the actor's * content. */ void st_theme_node_adjust_for_height (StThemeNode *node, float *for_height) { g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (for_height != NULL); if (*for_height >= 0) { float height_inc = get_height_inc (node); *for_height = MAX (0, *for_height - height_inc); } } /** * st_theme_node_adjust_preferred_width: * @node: a #StThemeNode * @min_width_p: (inout) (allow-none): the minimum width to adjust * @natural_width_p: (inout): the natural width to adjust * * Adjusts the minimum and natural width computed for an actor by * adding on the necessary space for borders and padding and taking * into account any minimum or maximum width. This is a convenience * function meant to be called from the get_preferred_width() method * of a #ClutterActor subclass */ void st_theme_node_adjust_preferred_width (StThemeNode *node, float *min_width_p, float *natural_width_p) { float width_inc; g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_geometry (node); width_inc = get_width_inc (node); if (min_width_p) { if (node->min_width != -1) *min_width_p = node->min_width; *min_width_p += width_inc; } if (natural_width_p) { if (node->width != -1) *natural_width_p = node->width; if (node->max_width != -1) *natural_width_p = MIN (*natural_width_p, node->max_width); *natural_width_p += width_inc; } } /** * st_theme_node_adjust_for_width: * @node: a #StThemeNode * @for_width: (inout): the "for width" to adjust * * Adjusts a "for width" passed to clutter_actor_get_preferred_height() to * account for borders and padding. This is a convenience function meant * to be called from a get_preferred_height() method of a #ClutterActor * subclass. The value after adjustment is the width available for the actor's * content. */ void st_theme_node_adjust_for_width (StThemeNode *node, float *for_width) { g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (for_width != NULL); if (*for_width >= 0) { float width_inc = get_width_inc (node); *for_width = MAX (0, *for_width - width_inc); } } /** * st_theme_node_adjust_preferred_height: * @node: a #StThemeNode * @min_height_p: (inout) (allow-none): the minimum height to adjust * @natural_height_p: (inout): the natural height to adjust * * Adjusts the minimum and natural height computed for an actor by * adding on the necessary space for borders and padding and taking * into account any minimum or maximum height. This is a convenience * function meant to be called from the get_preferred_height() method * of a #ClutterActor subclass */ void st_theme_node_adjust_preferred_height (StThemeNode *node, float *min_height_p, float *natural_height_p) { float height_inc; g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_geometry (node); height_inc = get_height_inc (node); if (min_height_p) { if (node->min_height != -1) *min_height_p = node->min_height; *min_height_p += height_inc; } if (natural_height_p) { if (node->height != -1) *natural_height_p = node->height; if (node->max_height != -1) *natural_height_p = MIN (*natural_height_p, node->max_height); *natural_height_p += height_inc; } } /** * st_theme_node_get_content_box: * @node: a #StThemeNode * @allocation: the box allocated to a #ClutterAlctor * @content_box: (out caller-allocates): computed box occupied by the actor's content * * Gets the box within an actor's allocation that contents the content * of an actor (excluding borders and padding). This is a convenience function * meant to be used from the allocate() or paint() methods of a #ClutterActor * subclass. */ void st_theme_node_get_content_box (StThemeNode *node, const ClutterActorBox *allocation, ClutterActorBox *content_box) { double noncontent_left, noncontent_top, noncontent_right, noncontent_bottom; double avail_width, avail_height, content_width, content_height; g_return_if_fail (ST_IS_THEME_NODE (node)); _st_theme_node_ensure_geometry (node); avail_width = allocation->x2 - allocation->x1; avail_height = allocation->y2 - allocation->y1; noncontent_left = node->border_width[ST_SIDE_LEFT] + node->padding[ST_SIDE_LEFT]; noncontent_top = node->border_width[ST_SIDE_TOP] + node->padding[ST_SIDE_TOP]; noncontent_right = node->border_width[ST_SIDE_RIGHT] + node->padding[ST_SIDE_RIGHT]; noncontent_bottom = node->border_width[ST_SIDE_BOTTOM] + node->padding[ST_SIDE_BOTTOM]; content_box->x1 = (int)(0.5 + noncontent_left); content_box->y1 = (int)(0.5 + noncontent_top); content_width = avail_width - noncontent_left - noncontent_right; if (content_width < 0) content_width = 0; content_height = avail_height - noncontent_top - noncontent_bottom; if (content_height < 0) content_height = 0; content_box->x2 = (int)(0.5 + content_box->x1 + content_width); content_box->y2 = (int)(0.5 + content_box->y1 + content_height); } /** * st_theme_node_get_background_paint_box: * @node: a #StThemeNode * @allocation: the box allocated to a #ClutterActor * @paint_box: (out caller-allocates): computed box occupied when painting the actor's background * * Gets the box used to paint the actor's background, including the area * occupied by properties which paint outside the actor's assigned allocation. */ void st_theme_node_get_background_paint_box (StThemeNode *node, const ClutterActorBox *actor_box, ClutterActorBox *paint_box) { StShadow *background_image_shadow; ClutterActorBox shadow_box; g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (actor_box != NULL); g_return_if_fail (paint_box != NULL); background_image_shadow = st_theme_node_get_background_image_shadow (node); *paint_box = *actor_box; if (!background_image_shadow) return; st_shadow_get_box (background_image_shadow, actor_box, &shadow_box); paint_box->x1 = MIN (paint_box->x1, shadow_box.x1); paint_box->x2 = MAX (paint_box->x2, shadow_box.x2); paint_box->y1 = MIN (paint_box->y1, shadow_box.y1); paint_box->y2 = MAX (paint_box->y2, shadow_box.y2); } /** * st_theme_node_get_paint_box: * @node: a #StThemeNode * @allocation: the box allocated to a #ClutterActor * @paint_box: (out caller-allocates): computed box occupied when painting the actor * * Gets the box used to paint the actor, including the area occupied * by properties which paint outside the actor's assigned allocation. * When painting @node to an offscreen buffer, this function can be * used to determine the necessary size of the buffer. */ void st_theme_node_get_paint_box (StThemeNode *node, const ClutterActorBox *actor_box, ClutterActorBox *paint_box) { StShadow *box_shadow; ClutterActorBox shadow_box; int outline_width; g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (actor_box != NULL); g_return_if_fail (paint_box != NULL); box_shadow = st_theme_node_get_box_shadow (node); outline_width = st_theme_node_get_outline_width (node); st_theme_node_get_background_paint_box (node, actor_box, paint_box); if (!box_shadow && !outline_width) return; paint_box->x1 -= outline_width; paint_box->x2 += outline_width; paint_box->y1 -= outline_width; paint_box->y2 += outline_width; if (box_shadow) { st_shadow_get_box (box_shadow, actor_box, &shadow_box); paint_box->x1 = MIN (paint_box->x1, shadow_box.x1); paint_box->x2 = MAX (paint_box->x2, shadow_box.x2); paint_box->y1 = MIN (paint_box->y1, shadow_box.y1); paint_box->y2 = MAX (paint_box->y2, shadow_box.y2); } } /** * st_theme_node_geometry_equal: * @node: a #StThemeNode * @other: a different #StThemeNode * * Tests if two theme nodes have the same borders and padding; this can be * used to optimize having to relayout when the style applied to a Clutter * actor changes colors without changing the geometry. */ gboolean st_theme_node_geometry_equal (StThemeNode *node, StThemeNode *other) { StSide side; g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE); if (node == other) return TRUE; g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE); _st_theme_node_ensure_geometry (node); _st_theme_node_ensure_geometry (other); for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++) { if (node->border_width[side] != other->border_width[side]) return FALSE; if (node->padding[side] != other->padding[side]) return FALSE; } if (node->width != other->width || node->height != other->height) return FALSE; if (node->min_width != other->min_width || node->min_height != other->min_height) return FALSE; if (node->max_width != other->max_width || node->max_height != other->max_height) return FALSE; return TRUE; } /** * st_theme_node_paint_equal: * @node: a #StThemeNode * @other: a different #StThemeNode * * Check if st_theme_node_paint() will paint identically for @node as it does * for @other. Note that in some cases this function may return %TRUE even * if there is no visible difference in the painting. * * Return value: %TRUE if the two theme nodes paint identically. %FALSE if the * two nodes potentially paint differently. */ gboolean st_theme_node_paint_equal (StThemeNode *node, StThemeNode *other) { StBorderImage *border_image, *other_border_image; StShadow *shadow, *other_shadow; int i; g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE); if (node == other) return TRUE; g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE); _st_theme_node_ensure_background (node); _st_theme_node_ensure_background (other); if (!clutter_color_equal (&node->background_color, &other->background_color)) return FALSE; if (node->background_gradient_type != other->background_gradient_type) return FALSE; if (node->background_gradient_type != ST_GRADIENT_NONE && !clutter_color_equal (&node->background_gradient_end, &other->background_gradient_end)) return FALSE; if (g_strcmp0 (node->background_image, other->background_image) != 0) return FALSE; _st_theme_node_ensure_geometry (node); _st_theme_node_ensure_geometry (other); for (i = 0; i < 4; i++) { if (node->border_width[i] != other->border_width[i]) return FALSE; if (node->border_width[i] > 0 && !clutter_color_equal (&node->border_color[i], &other->border_color[i])) return FALSE; if (node->border_radius[i] != other->border_radius[i]) return FALSE; } if (node->outline_width != other->outline_width) return FALSE; if (node->outline_width > 0 && !clutter_color_equal (&node->outline_color, &other->outline_color)) return FALSE; border_image = st_theme_node_get_border_image (node); other_border_image = st_theme_node_get_border_image (other); if ((border_image == NULL) != (other_border_image == NULL)) return FALSE; if (border_image != NULL && !st_border_image_equal (border_image, other_border_image)) return FALSE; shadow = st_theme_node_get_box_shadow (node); other_shadow = st_theme_node_get_box_shadow (other); if ((shadow == NULL) != (other_shadow == NULL)) return FALSE; if (shadow != NULL && !st_shadow_equal (shadow, other_shadow)) return FALSE; shadow = st_theme_node_get_background_image_shadow (node); other_shadow = st_theme_node_get_background_image_shadow (other); if ((shadow == NULL) != (other_shadow == NULL)) return FALSE; if (shadow != NULL && !st_shadow_equal (shadow, other_shadow)) return FALSE; return TRUE; } Cinnamon-2.8.6/src/st/st-scroll-view.c0000664000175000017500000012314712625666430016512 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-view.h: Container with scroll-bars * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010 Maxim Ermilov * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-scroll-view * @short_description: a container for scrollable children * * #StScrollView is a single child container for actors that implement * #StScrollable. It provides scrollbars around the edge of the child to * allow the user to move around the scrollable area. */ /* TODO: The code here currently only deals with height-for-width * allocation; width-for-height allocation would need a second set of * code paths through get_preferred_height()/get_preferred_width()/allocate() * that reverse the roles of the horizontal and vertical scrollbars. * * TODO: The multiple layout passes with and without scrollbars when * using the automatic policy causes considerable inefficiency because * it breaks request caching; we should saved the last size passed * into allocate() and if it's the same as previous size not repeat * the determination of scrollbar visibility. This requires overriding * queue_relayout() so we know when to discard the saved value. * * The size negotiation between the #StScrollView and the child is * described in the documentation for #StScrollable; the significant * part to note there is that reported minimum sizes for a scrolled * child are the minimum sizes when no scrollbar is needed. This allows * us to determine what scrollbars are visible without a need to look * inside the #StAdjustment. * * The second simplification that we make that allows us to implement * a straighforward height-for-width negotiation without multiple * allocate passes is that when the vertical scrollbar policy is * AUTO, we always reserve space for the vertical scrollbar in the * reported minimum and natural size. * * See https://bugzilla.gnome.org/show_bug.cgi?id=611740 for a more * detailed description of the considerations involved. */ #include "st-scroll-view.h" #include "st-marshal.h" #include "st-scroll-bar.h" #include "st-scrollable.h" #include "st-scroll-view-fade.h" #include #include static void clutter_container_iface_init (ClutterContainerIface *iface); static ClutterContainerIface *st_scroll_view_parent_iface = NULL; G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)) #define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ ST_TYPE_SCROLL_VIEW, \ StScrollViewPrivate)) #define AUTO_SCROLL_POLL_INTERVAL 15 #define AUTO_SCROLL_TOTAL_REGION 100 #define AUTO_SCROLL_OVERLAP 10 /* autoscroll region extends this far into the scroll view * remaining portion of region is above or below the view */ /* gdouble - from 0 to TOTAL_REGION / this number to get scroll delta */ #define AUTO_SCROLL_SPEED_DIVISOR 4.0 struct _StScrollViewPrivate { /* a pointer to the child; this is actually stored * inside StBin:child, but we keep it to avoid * calling st_bin_get_child() every time we need it */ ClutterActor *child; StAdjustment *hadjustment; ClutterActor *hscroll; StAdjustment *vadjustment; ClutterActor *vscroll; ClutterInputDevice *mouse_pointer; GtkPolicyType hscrollbar_policy; GtkPolicyType vscrollbar_policy; gfloat row_size; gfloat column_size; GSettings *settings; gint setting_connect_id; StScrollViewFade *vfade_effect; gboolean row_size_set : 1; gboolean column_size_set : 1; guint mouse_scroll : 1; guint hscrollbar_visible : 1; guint vscrollbar_visible : 1; gboolean auto_scroll : 1; guint auto_scroll_timeout_id; }; enum { PROP_0, PROP_HSCROLL, PROP_VSCROLL, PROP_HSCROLLBAR_POLICY, PROP_VSCROLLBAR_POLICY, PROP_HSCROLLBAR_VISIBLE, PROP_VSCROLLBAR_VISIBLE, PROP_MOUSE_SCROLL, PROP_AUTO_SCROLL }; static void st_scroll_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { StScrollViewPrivate *priv = ((StScrollView *) object)->priv; switch (property_id) { case PROP_HSCROLL: g_value_set_object (value, priv->hscroll); break; case PROP_VSCROLL: g_value_set_object (value, priv->vscroll); break; case PROP_HSCROLLBAR_POLICY: g_value_set_enum (value, priv->hscrollbar_policy); break; case PROP_VSCROLLBAR_POLICY: g_value_set_enum (value, priv->vscrollbar_policy); break; case PROP_HSCROLLBAR_VISIBLE: g_value_set_boolean (value, priv->hscrollbar_visible); break; case PROP_VSCROLLBAR_VISIBLE: g_value_set_boolean (value, priv->vscrollbar_visible); break; case PROP_MOUSE_SCROLL: g_value_set_boolean (value, priv->mouse_scroll); break; case PROP_AUTO_SCROLL: g_value_set_boolean (value, priv->auto_scroll); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } /** * st_scroll_view_update_vfade_effect: * @self: a #StScrollView * @fade_offset: The length of the fade effect, in pixels. * * Sets the height of the fade area area in pixels. A value of 0 * disables the effect. */ static void st_scroll_view_update_vfade_effect (StScrollView *self, float fade_offset) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; /* A fade amount of more than 0 enables the effect. */ if (fade_offset > 0.) { if (priv->vfade_effect == NULL) { priv->vfade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL); clutter_actor_add_effect_with_name (CLUTTER_ACTOR (self), "vfade", CLUTTER_EFFECT (priv->vfade_effect)); } g_object_set (priv->vfade_effect, "fade-offset", fade_offset, NULL); } else { if (priv->vfade_effect != NULL) { clutter_actor_remove_effect (CLUTTER_ACTOR (self), CLUTTER_EFFECT (priv->vfade_effect)); priv->vfade_effect = NULL; } } clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } static gboolean get_sub_region_y (gint mouse_y, gfloat box_y, gfloat height, gint *sub_region) { gboolean up = mouse_y < (box_y + (height / 2)); gfloat real_y_upper_limit = box_y + AUTO_SCROLL_OVERLAP - AUTO_SCROLL_TOTAL_REGION; gfloat real_y_lower_limit = box_y + height - AUTO_SCROLL_OVERLAP + AUTO_SCROLL_TOTAL_REGION; if (up) { *sub_region = (real_y_upper_limit + AUTO_SCROLL_TOTAL_REGION) - mouse_y; } else { *sub_region = mouse_y - (real_y_lower_limit - AUTO_SCROLL_TOTAL_REGION); } return up; } static void calculate_and_scroll (ClutterActor *self, gint mouse_x, gint mouse_y, gfloat box_x, gfloat box_y, gfloat width, gfloat height) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; gboolean up; gint sub_region; gdouble delta, vvalue; up = get_sub_region_y (mouse_y, box_y, height, &sub_region); if (up) delta = sub_region * -1.0 / AUTO_SCROLL_SPEED_DIVISOR; else delta = sub_region / AUTO_SCROLL_SPEED_DIVISOR; g_object_get (priv->vadjustment, "value", &vvalue, NULL); st_adjustment_set_value (priv->vadjustment, vvalue + delta); } static void get_pointer_and_view_coords (ClutterActor *self, gint *mouse_x, gint *mouse_y, gfloat *box_x, gfloat *box_y, gfloat *width, gfloat *height) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; clutter_input_device_get_device_coords (priv->mouse_pointer, mouse_x, mouse_y); clutter_actor_get_transformed_position (self, box_x, box_y); clutter_actor_get_transformed_size (self, width, height); } static gboolean is_in_auto_scroll_regions (gint mouse_x, gint mouse_y, gfloat box_x, gfloat box_y, gfloat width, gfloat height) { gfloat real_y_upper_limit = box_y + AUTO_SCROLL_OVERLAP - AUTO_SCROLL_TOTAL_REGION; gfloat real_y_lower_limit = box_y + height - AUTO_SCROLL_OVERLAP + AUTO_SCROLL_TOTAL_REGION; if (mouse_x < box_x || mouse_x > box_x + width) return FALSE; if (((mouse_y < real_y_upper_limit + AUTO_SCROLL_TOTAL_REGION) && (mouse_y > real_y_upper_limit)) || ((mouse_y > real_y_lower_limit - AUTO_SCROLL_TOTAL_REGION) && (mouse_y < real_y_lower_limit))) { return TRUE; } else { return FALSE; } } static gboolean do_auto_scroll (ClutterActor *self) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; gfloat box_x, box_y, width, height; gint mouse_x, mouse_y; get_pointer_and_view_coords (self, &mouse_x, &mouse_y, &box_x, &box_y, &width, &height); if (is_in_auto_scroll_regions (mouse_x, mouse_y, box_x, box_y, width, height)) { calculate_and_scroll (self, mouse_x, mouse_y, box_x, box_y, width, height); return TRUE; } else { priv->auto_scroll_timeout_id = 0; return FALSE; } } static void motion_event_cb (ClutterActor *self, ClutterMotionEvent *event, gpointer data) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; gfloat box_x, box_y, width, height; gint mouse_x, mouse_y; if (priv->auto_scroll_timeout_id > 0) return; get_pointer_and_view_coords (self, &mouse_x, &mouse_y, &box_x, &box_y, &width, &height); if (is_in_auto_scroll_regions (mouse_x, mouse_y, box_x, box_y, width, height)) { priv->auto_scroll_timeout_id = g_timeout_add (AUTO_SCROLL_POLL_INTERVAL, (GSourceFunc) do_auto_scroll, self); } } static void st_scroll_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StScrollView *self = ST_SCROLL_VIEW (object); StScrollViewPrivate *priv = self->priv; switch (property_id) { case PROP_MOUSE_SCROLL: st_scroll_view_set_mouse_scrolling (self, g_value_get_boolean (value)); break; case PROP_HSCROLLBAR_POLICY: st_scroll_view_set_policy (self, g_value_get_enum (value), priv->vscrollbar_policy); break; case PROP_VSCROLLBAR_POLICY: st_scroll_view_set_policy (self, priv->hscrollbar_policy, g_value_get_enum (value)); break; case PROP_AUTO_SCROLL: st_scroll_view_set_auto_scrolling (self, g_value_get_boolean (value)); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void st_scroll_view_dispose (GObject *object) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv; if (priv->vfade_effect) { clutter_actor_remove_effect (CLUTTER_ACTOR (object), CLUTTER_EFFECT (priv->vfade_effect)); priv->vfade_effect = NULL; } if (priv->vscroll) clutter_actor_destroy (priv->vscroll); if (priv->hscroll) clutter_actor_destroy (priv->hscroll); /* For most reliable freeing of memory, an object with signals * like StAdjustment should be explicitly disposed. Since we own * the adjustments, we take care of that. This also disconnects * the signal handlers that we established on creation. */ if (priv->hadjustment) { g_object_run_dispose (G_OBJECT (priv->hadjustment)); g_object_unref (priv->hadjustment); priv->hadjustment = NULL; } if (priv->vadjustment) { g_object_run_dispose (G_OBJECT (priv->vadjustment)); g_object_unref (priv->vadjustment); priv->vadjustment = NULL; } if (priv->setting_connect_id > 0) { g_signal_handler_disconnect (priv->settings, priv->setting_connect_id); priv->setting_connect_id = 0; } g_signal_handlers_disconnect_by_func (ST_SCROLL_VIEW (object), motion_event_cb, ST_SCROLL_VIEW (object)); G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object); } static void st_scroll_view_paint (ClutterActor *actor) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; /* StBin will paint the child */ CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->paint (actor); /* paint our custom children */ if (priv->hscrollbar_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) clutter_actor_paint (priv->hscroll); if (priv->vscrollbar_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) clutter_actor_paint (priv->vscroll); } static void st_scroll_view_pick (ClutterActor *actor, const ClutterColor *color) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; /* Chain up so we get a bounding box pained (if we are reactive) */ CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, color); /* paint our custom children */ if (priv->hscrollbar_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) clutter_actor_paint (priv->hscroll); if (priv->vscrollbar_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) clutter_actor_paint (priv->vscroll); } static double get_scrollbar_width (StScrollView *scroll, gfloat for_height) { StScrollViewPrivate *priv = scroll->priv; if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { gfloat min_size; clutter_actor_get_preferred_width (CLUTTER_ACTOR (priv->vscroll), for_height, &min_size, NULL); return min_size; } else return 0; } static double get_scrollbar_height (StScrollView *scroll, gfloat for_width) { StScrollViewPrivate *priv = scroll->priv; if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { gfloat min_size; clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->hscroll), for_width, &min_size, NULL); return min_size; } else return 0; } static void st_scroll_view_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gfloat min_width = 0, natural_width; gfloat child_min_width, child_natural_width; if (!priv->child) return; st_theme_node_adjust_for_height (theme_node, &for_height); clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, &child_natural_width); natural_width = child_natural_width; switch (priv->hscrollbar_policy) { case GTK_POLICY_NEVER: min_width = child_min_width; break; case GTK_POLICY_ALWAYS: case GTK_POLICY_AUTOMATIC: /* Should theoretically use the min width of the hscrollbar, * but that's not cleanly defined at the moment */ min_width = 0; break; } if (priv->vscrollbar_policy != GTK_POLICY_NEVER) { float sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), for_height); min_width += sb_width; natural_width += sb_width; } if (min_width_p) *min_width_p = min_width; if (natural_width_p) *natural_width_p = natural_width; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_scroll_view_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gboolean account_for_hscrollbar = FALSE; gfloat min_height = 0, natural_height; gfloat child_min_height, child_natural_height; gfloat child_min_width; gfloat sb_width; if (!priv->child) return; st_theme_node_adjust_for_width (theme_node, &for_width); clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, NULL); if (min_height_p) *min_height_p = 0; sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1); switch (priv->vscrollbar_policy) { case GTK_POLICY_NEVER: break; case GTK_POLICY_ALWAYS: case GTK_POLICY_AUTOMATIC: /* We've requested space for the scrollbar, subtract it back out */ for_width -= sb_width; break; } switch (priv->hscrollbar_policy) { case GTK_POLICY_NEVER: account_for_hscrollbar = FALSE; break; case GTK_POLICY_ALWAYS: account_for_hscrollbar = TRUE; break; case GTK_POLICY_AUTOMATIC: account_for_hscrollbar = for_width < child_min_width; break; } clutter_actor_get_preferred_height (priv->child, for_width, &child_min_height, &child_natural_height); natural_height = child_natural_height; switch (priv->vscrollbar_policy) { case GTK_POLICY_NEVER: min_height = child_min_height; break; case GTK_POLICY_ALWAYS: case GTK_POLICY_AUTOMATIC: /* Should theoretically use the min height of the vscrollbar, * but that's not cleanly defined at the moment */ min_height = 0; break; } if (account_for_hscrollbar) { float sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), for_width); min_height += sb_height; natural_height += sb_height; } if (min_height_p) *min_height_p = min_height; if (natural_height_p) *natural_height_p = natural_height; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_scroll_view_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox content_box, child_box; ClutterActorClass *parent_parent_class; gfloat avail_width, avail_height, sb_width, sb_height; gboolean hscrollbar_visible, vscrollbar_visible; StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); /* Chain up to the parent's parent class * * We do this because we do not want StBin to allocate the child, as we * give it a different allocation later, depending on whether the scrollbars * are visible */ parent_parent_class = g_type_class_peek_parent (st_scroll_view_parent_class); CLUTTER_ACTOR_CLASS (parent_parent_class)-> allocate (actor, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); avail_width = content_box.x2 - content_box.x1; avail_height = content_box.y2 - content_box.y1; if (clutter_actor_get_request_mode (actor) == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1); sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), sb_width); } else { sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), -1); sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), sb_height); } /* Determine what scrollbars are visible. The basic idea of the * handling of an automatic scrollbars is that we start off with the * assumption that we don't need any scrollbars, see if that works, * and if not add horizontal and vertical scrollbars until we are no * longer overflowing. */ if (priv->child) { gfloat child_min_width; gfloat child_min_height; clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, NULL); if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) { if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) { /* Pass one, try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height; hscrollbar_visible = child_min_width > avail_width - (vscrollbar_visible ? sb_width : 0); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); /* Pass two - if we needed a vertical scrollbar, get a new preferred height */ if (vscrollbar_visible) { clutter_actor_get_preferred_height (priv->child, MAX (avail_width - sb_width, 0), &child_min_height, NULL); hscrollbar_visible = child_min_width > avail_width - sb_width; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; /* try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); } } else { vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) hscrollbar_visible = child_min_width > avail_height - (vscrollbar_visible ? 0 : sb_width); else hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; } /* Whether or not we show the scrollbars, if the scrollbars are visible * actors, we need to give them some allocation, so we unconditionally * give them the "right" allocation; that might overlap the child when * the scrollbars are not visible, but it doesn't matter because we * don't include them in pick or paint. */ /* Vertical scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1; child_box.x2 = content_box.x1 + sb_width; } else { child_box.x1 = content_box.x2 - sb_width; child_box.x2 = content_box.x2; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - (hscrollbar_visible ? sb_height : 0); clutter_actor_allocate (priv->vscroll, &child_box, flags); } /* Horizontal scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + (vscrollbar_visible ? sb_width : 0); child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - (vscrollbar_visible ? sb_width : 0); } child_box.y1 = content_box.y2 - sb_height; child_box.y2 = content_box.y2; clutter_actor_allocate (priv->hscroll, &child_box, flags); } /* Now fold visibility into the scrollbar sizes to simplify the rest * of the computations. */ if (!hscrollbar_visible) sb_height = 0; if (!vscrollbar_visible) sb_width = 0; /* Child */ if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + sb_width; child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - sb_width; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - sb_height; if (priv->child) clutter_actor_allocate (priv->child, &child_box, flags); if (priv->hscrollbar_visible != hscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->hscrollbar_visible = hscrollbar_visible; g_object_notify (G_OBJECT (actor), "hscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } if (priv->vscrollbar_visible != vscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->vscrollbar_visible = vscrollbar_visible; g_object_notify (G_OBJECT (actor), "vscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } } static void st_scroll_view_style_changed (StWidget *widget) { StScrollView *self = ST_SCROLL_VIEW (widget); StScrollViewPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (widget); gdouble fade_offset = st_theme_node_get_length (theme_node, "-st-vfade-offset"); gboolean vfade_enabled = g_settings_get_boolean (priv->settings, "enable-vfade"); if (!vfade_enabled) fade_offset = 0; st_scroll_view_update_vfade_effect (self, fade_offset); st_widget_style_changed (ST_WIDGET (priv->hscroll)); st_widget_style_changed (ST_WIDGET (priv->vscroll)); ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget); } static void vfade_setting_changed_cb (GSettings *settings, gchar *key, gpointer user_data) { StWidget *widget = ST_WIDGET (user_data); g_return_if_fail (ST_IS_SCROLL_VIEW (widget)); st_scroll_view_style_changed (widget); } static gboolean st_scroll_view_scroll_event (ClutterActor *self, ClutterScrollEvent *event) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; gdouble value, step, hvalue, vvalue, delta_x, delta_y; /* don't handle scroll events if requested not to */ if (!priv->mouse_scroll) return FALSE; switch (event->direction) { case CLUTTER_SCROLL_SMOOTH: clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y); g_object_get (priv->hadjustment, "value", &hvalue, NULL); g_object_get (priv->vadjustment, "value", &vvalue, NULL); break; case CLUTTER_SCROLL_UP: case CLUTTER_SCROLL_DOWN: g_object_get (priv->vadjustment, "step-increment", &step, "value", &value, NULL); break; case CLUTTER_SCROLL_LEFT: case CLUTTER_SCROLL_RIGHT: g_object_get (priv->hadjustment, "step-increment", &step, "value", &value, NULL); break; } switch (event->direction) { case CLUTTER_SCROLL_SMOOTH: st_adjustment_set_value (priv->hadjustment, hvalue + delta_x); st_adjustment_set_value (priv->vadjustment, vvalue + delta_y); break; case CLUTTER_SCROLL_UP: st_adjustment_set_value (priv->vadjustment, value - step); break; case CLUTTER_SCROLL_DOWN: st_adjustment_set_value (priv->vadjustment, value + step); break; case CLUTTER_SCROLL_LEFT: st_adjustment_set_value (priv->hadjustment, value - step); break; case CLUTTER_SCROLL_RIGHT: st_adjustment_set_value (priv->hadjustment, value + step); break; } return TRUE; } static void st_scroll_view_class_init (StScrollViewClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (StScrollViewPrivate)); object_class->get_property = st_scroll_view_get_property; object_class->set_property = st_scroll_view_set_property; object_class->dispose = st_scroll_view_dispose; actor_class->paint = st_scroll_view_paint; actor_class->pick = st_scroll_view_pick; actor_class->get_preferred_width = st_scroll_view_get_preferred_width; actor_class->get_preferred_height = st_scroll_view_get_preferred_height; actor_class->allocate = st_scroll_view_allocate; actor_class->scroll_event = st_scroll_view_scroll_event; widget_class->style_changed = st_scroll_view_style_changed; g_object_class_install_property (object_class, PROP_HSCROLL, g_param_spec_object ("hscroll", "StScrollBar", "Horizontal scroll indicator", ST_TYPE_SCROLL_BAR, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_VSCROLL, g_param_spec_object ("vscroll", "StScrollBar", "Vertical scroll indicator", ST_TYPE_SCROLL_BAR, G_PARAM_READABLE)); pspec = g_param_spec_enum ("vscrollbar-policy", "Vertical Scrollbar Policy", "When the vertical scrollbar is displayed", GTK_TYPE_POLICY_TYPE, GTK_POLICY_AUTOMATIC, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VSCROLLBAR_POLICY, pspec); pspec = g_param_spec_enum ("hscrollbar-policy", "Horizontal Scrollbar Policy", "When the horizontal scrollbar is displayed", GTK_TYPE_POLICY_TYPE, GTK_POLICY_AUTOMATIC, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_HSCROLLBAR_POLICY, pspec); pspec = g_param_spec_boolean ("hscrollbar-visible", "Horizontal Scrollbar Visibility", "Whether the horizontal scrollbar is visible", TRUE, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_HSCROLLBAR_VISIBLE, pspec); pspec = g_param_spec_boolean ("vscrollbar-visible", "Vertical Scrollbar Visibility", "Whether the vertical scrollbar is visible", TRUE, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_VSCROLLBAR_VISIBLE, pspec); pspec = g_param_spec_boolean ("enable-mouse-scrolling", "Enable Mouse Scrolling", "Enable automatic mouse wheel scrolling", TRUE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MOUSE_SCROLL, pspec); pspec = g_param_spec_boolean ("enable-auto-scrolling", "Enable Auto Scrolling", "Enable automatic scrolling", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_AUTO_SCROLL, pspec); } static void st_scroll_view_init (StScrollView *self) { StScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self); priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC; priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC; priv->hadjustment = g_object_new (ST_TYPE_ADJUSTMENT, NULL); priv->hscroll = g_object_new (ST_TYPE_SCROLL_BAR, "adjustment", priv->hadjustment, "vertical", FALSE, NULL); priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT, NULL); priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR, "adjustment", priv->vadjustment, "vertical", TRUE, NULL); clutter_actor_add_child (CLUTTER_ACTOR (self), priv->hscroll); clutter_actor_add_child (CLUTTER_ACTOR (self), priv->vscroll); /* mouse scroll is enabled by default, so we also need to be reactive */ priv->mouse_scroll = TRUE; g_object_set (G_OBJECT (self), "reactive", TRUE, NULL); priv->auto_scroll = FALSE; priv->auto_scroll_timeout_id = 0; priv->mouse_pointer = clutter_device_manager_get_core_device (clutter_device_manager_get_default (), CLUTTER_POINTER_DEVICE); priv->settings = g_settings_new("org.cinnamon"); priv->setting_connect_id = g_signal_connect (priv->settings, "changed::enable-vfade", G_CALLBACK (vfade_setting_changed_cb), self); } static void st_scroll_view_add (ClutterContainer *container, ClutterActor *actor) { StScrollView *self = ST_SCROLL_VIEW (container); StScrollViewPrivate *priv = self->priv; if (ST_IS_SCROLLABLE (actor)) { priv->child = actor; /* chain up to StBin::add() */ st_scroll_view_parent_iface->add (container, actor); st_scrollable_set_adjustments (ST_SCROLLABLE (actor), priv->hadjustment, priv->vadjustment); } else { g_warning ("Attempting to add an actor of type %s to " "a StScrollView, but the actor does " "not implement StScrollable.", g_type_name (G_OBJECT_TYPE (actor))); } } static void st_scroll_view_remove (ClutterContainer *container, ClutterActor *actor) { StScrollView *self = ST_SCROLL_VIEW (container); StScrollViewPrivate *priv = self->priv; if (actor == priv->child) { g_object_ref (priv->child); /* chain up to StBin::remove() */ st_scroll_view_parent_iface->remove (container, actor); st_scrollable_set_adjustments (ST_SCROLLABLE (priv->child), NULL, NULL); g_object_unref (priv->child); priv->child = NULL; } else { if (actor == priv->vscroll) priv->vscroll = NULL; else if (actor == priv->hscroll) priv->hscroll = NULL; else g_assert ("Unknown child removed from StScrollView"); clutter_actor_unparent (actor); } } static void st_scroll_view_foreach_with_internals (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv; if (priv->child != NULL) callback (priv->child, user_data); if (priv->hscroll != NULL) callback (priv->hscroll, user_data); if (priv->vscroll != NULL) callback (priv->vscroll, user_data); } static void clutter_container_iface_init (ClutterContainerIface *iface) { /* store a pointer to the StBin implementation of * ClutterContainer so that we can chain up when * overriding the methods */ st_scroll_view_parent_iface = g_type_interface_peek_parent (iface); iface->add = st_scroll_view_add; iface->remove = st_scroll_view_remove; iface->foreach_with_internals = st_scroll_view_foreach_with_internals; } StWidget * st_scroll_view_new (void) { return g_object_new (ST_TYPE_SCROLL_VIEW, NULL); } /** * st_scroll_view_get_hscroll_bar: * @scroll: a #StScrollView * * Gets the horizontal scrollbar of the scrollbiew * * Return value: (transfer none): the horizontal #StScrollbar */ ClutterActor * st_scroll_view_get_hscroll_bar (StScrollView *scroll) { g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL); return scroll->priv->hscroll; } /** * st_scroll_view_get_vscroll_bar: * @scroll: a #StScrollView * * Gets the vertical scrollbar of the scrollbiew * * Return value: (transfer none): the vertical #StScrollbar */ ClutterActor * st_scroll_view_get_vscroll_bar (StScrollView *scroll) { g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL); return scroll->priv->vscroll; } gfloat st_scroll_view_get_column_size (StScrollView *scroll) { gdouble column_size; g_return_val_if_fail (scroll, 0); g_object_get (scroll->priv->hadjustment, "step-increment", &column_size, NULL); return column_size; } void st_scroll_view_set_column_size (StScrollView *scroll, gfloat column_size) { g_return_if_fail (scroll); if (column_size < 0) { scroll->priv->column_size_set = FALSE; scroll->priv->column_size = -1; } else { scroll->priv->column_size_set = TRUE; scroll->priv->column_size = column_size; g_object_set (scroll->priv->hadjustment, "step-increment", (gdouble) scroll->priv->column_size, NULL); } } gfloat st_scroll_view_get_row_size (StScrollView *scroll) { gdouble row_size; g_return_val_if_fail (scroll, 0); g_object_get (scroll->priv->vadjustment, "step-increment", &row_size, NULL); return row_size; } void st_scroll_view_set_row_size (StScrollView *scroll, gfloat row_size) { g_return_if_fail (scroll); if (row_size < 0) { scroll->priv->row_size_set = FALSE; scroll->priv->row_size = -1; } else { scroll->priv->row_size_set = TRUE; scroll->priv->row_size = row_size; g_object_set (scroll->priv->vadjustment, "step-increment", (gdouble) scroll->priv->row_size, NULL); } } void st_scroll_view_set_mouse_scrolling (StScrollView *scroll, gboolean enabled) { StScrollViewPrivate *priv; g_return_if_fail (ST_IS_SCROLL_VIEW (scroll)); priv = ST_SCROLL_VIEW (scroll)->priv; if (priv->mouse_scroll != enabled) { priv->mouse_scroll = enabled; /* make sure we can receive mouse wheel events */ if (enabled) clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE); } } gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll) { StScrollViewPrivate *priv; g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE); priv = ST_SCROLL_VIEW (scroll)->priv; return priv->mouse_scroll; } void st_scroll_view_set_auto_scrolling (StScrollView *scroll, gboolean enabled) { StScrollViewPrivate *priv; g_return_if_fail (ST_IS_SCROLL_VIEW (scroll)); priv = ST_SCROLL_VIEW (scroll)->priv; if (priv->auto_scroll != enabled) { priv->auto_scroll = enabled; /* make sure we can receive mouse wheel events */ if (enabled) { clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE); g_signal_connect (scroll, "motion-event", G_CALLBACK (motion_event_cb), scroll); } else { g_signal_handlers_disconnect_by_func (scroll, motion_event_cb, scroll); if (priv->auto_scroll_timeout_id > 0) { g_source_remove (priv->auto_scroll_timeout_id); priv->auto_scroll_timeout_id = 0; } } } } gboolean st_scroll_view_get_auto_scrolling (StScrollView *scroll) { StScrollViewPrivate *priv; g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE); priv = ST_SCROLL_VIEW (scroll)->priv; return priv->auto_scroll; } /** * st_scroll_view_set_policy: * @scroll: A #StScrollView * @hscroll: Whether to enable horizontal scrolling * @vscroll: Whether to enable vertical scrolling * * Set the scroll policy. */ void st_scroll_view_set_policy (StScrollView *scroll, GtkPolicyType hscroll, GtkPolicyType vscroll) { StScrollViewPrivate *priv; g_return_if_fail (ST_IS_SCROLL_VIEW (scroll)); priv = ST_SCROLL_VIEW (scroll)->priv; if (priv->hscrollbar_policy == hscroll && priv->vscrollbar_policy == vscroll) return; g_object_freeze_notify ((GObject *) scroll); if (priv->hscrollbar_policy != hscroll) { priv->hscrollbar_policy = hscroll; g_object_notify ((GObject *) scroll, "hscrollbar-policy"); } if (priv->vscrollbar_policy != vscroll) { priv->vscrollbar_policy = vscroll; g_object_notify ((GObject *) scroll, "vscrollbar-policy"); } clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll)); g_object_thaw_notify ((GObject *) scroll); } Cinnamon-2.8.6/src/st/st-table-private.h0000664000175000017500000000205312625666430017000 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-private-private.h: Private declarations for StTable * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_TABLE_PRIVATE_H__ #define __ST_TABLE_PRIVATE_H__ #include "st-table.h" G_BEGIN_DECLS void _st_table_update_row_col (StTable *table, gint row, gint col); G_END_DECLS #endif /* __ST_TABLE_PRIVATE_H__ */ Cinnamon-2.8.6/src/st/st-container.c0000664000175000017500000005324712625666430016231 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-container.c: Base class for St container actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * Copyright 2010 Florian Müllner * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-container.h" #define ST_CONTAINER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),ST_TYPE_CONTAINER, StContainerPrivate)) struct _StContainerPrivate { GList *children; ClutterActor *first_child; ClutterActor *last_child; gboolean block_update_pseudo_classes; }; static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (StContainer, st_container, ST_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)); static void st_container_update_pseudo_classes (StContainer *container) { GList *first_item, *last_item; ClutterActor *first_child, *last_child; StContainerPrivate *priv = container->priv; if (priv->block_update_pseudo_classes) return; first_item = priv->children; first_child = first_item ? first_item->data : NULL; if (first_child != priv->first_child) { if (priv->first_child && ST_IS_WIDGET (priv->first_child)) st_widget_remove_style_pseudo_class (ST_WIDGET (priv->first_child), "first-child"); if (priv->first_child) { g_object_unref (priv->first_child); priv->first_child = NULL; } if (first_child && ST_IS_WIDGET (first_child)) st_widget_add_style_pseudo_class (ST_WIDGET (first_child), "first-child"); if (first_child) priv->first_child = g_object_ref (first_child); } last_item = g_list_last (priv->children); last_child = last_item ? last_item->data : NULL; if (last_child != priv->last_child) { if (priv->last_child && ST_IS_WIDGET (priv->last_child)) st_widget_remove_style_pseudo_class (ST_WIDGET (priv->last_child), "last-child"); if (priv->last_child) { g_object_unref (priv->last_child); priv->last_child = NULL; } if (last_child && ST_IS_WIDGET (last_child)) st_widget_add_style_pseudo_class (ST_WIDGET (last_child), "last-child"); if (last_child) priv->last_child = g_object_ref (last_child); } } /** * st_container_destroy_children: * @container: An #StContainer * * Destroys all child actors from @container. */ void st_container_destroy_children (StContainer *container) { StContainerPrivate *priv = container->priv; priv->block_update_pseudo_classes = TRUE; while (priv->children) clutter_actor_destroy (priv->children->data); priv->block_update_pseudo_classes = FALSE; st_container_update_pseudo_classes (container); } void st_container_move_child (StContainer *container, ClutterActor *actor, int pos) { StContainerPrivate *priv = container->priv; GList *item = NULL; item = g_list_find (priv->children, actor); if (item == NULL) { g_warning ("Actor of type '%s' is not a child of the %s container", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } priv->children = g_list_delete_link (priv->children, item); priv->children = g_list_insert (priv->children, actor, pos); st_container_update_pseudo_classes (container); clutter_actor_queue_relayout ((ClutterActor*) container); } void st_container_move_before (StContainer *container, ClutterActor *actor, ClutterActor *sibling) { StContainerPrivate *priv = container->priv; GList *actor_item = NULL; GList *sibling_item = NULL; actor_item = g_list_find (priv->children, actor); sibling_item = g_list_find (priv->children, sibling); g_return_if_fail (actor_item != NULL); g_return_if_fail (sibling_item != NULL); priv->children = g_list_delete_link (priv->children, actor_item); priv->children = g_list_insert_before (priv->children, sibling_item, actor); st_container_update_pseudo_classes (container); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); } /** * st_container_get_children_list: * @container: An #StContainer * * Get the internal list of @container's child actors. This function * should only be used by subclasses of StContainer * * Returns: (element-type Clutter.Actor) (transfer none): list of @container's child actors */ GList * st_container_get_children_list (StContainer *container) { g_return_val_if_fail (ST_IS_CONTAINER (container), NULL); return container->priv->children; } static GList * st_container_real_get_focus_chain (StContainer *container) { GList *chain, *children; chain = NULL; for (children = container->priv->children; children; children = children->next) { ClutterActor *child = children->data; if (CLUTTER_ACTOR_IS_VISIBLE (child)) chain = g_list_prepend (chain, child); } return g_list_reverse (chain); } /** * st_container_get_focus_chain: * @container: An #StContainer * * Gets a list of the focusable children of @container, in "Tab" * order. By default, this returns all visible * (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @container. * * Returns: (element-type Clutter.Actor) (transfer container): * @container's focusable children */ GList * st_container_get_focus_chain (StContainer *container) { return ST_CONTAINER_GET_CLASS (container)->get_focus_chain (container); } static gint sort_z_order (gconstpointer a, gconstpointer b) { float depth_a, depth_b; depth_a = clutter_actor_get_depth (CLUTTER_ACTOR (a)); depth_b = clutter_actor_get_depth (CLUTTER_ACTOR (b)); if (depth_a < depth_b) return -1; if (depth_a > depth_b) return 1; return 0; } static void st_container_add (ClutterContainer *container, ClutterActor *actor) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; g_object_ref (actor); priv->children = g_list_append (priv->children, actor); clutter_actor_add_child (CLUTTER_ACTOR (container), actor); /* queue a relayout, to get the correct positioning inside * the ::actor-added signal handlers */ clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-added", actor); clutter_container_sort_depth_order (container); st_container_update_pseudo_classes (ST_CONTAINER (container)); g_object_unref (actor); } static void st_container_remove (ClutterContainer *container, ClutterActor *actor) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; g_object_ref (actor); priv->children = g_list_remove (priv->children, actor); clutter_actor_unparent (actor); /* queue a relayout, to get the correct positioning inside * the ::actor-removed signal handlers */ clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); /* at this point, the actor passed to the "actor-removed" signal * handlers is not parented anymore to the container but since we * are holding a reference on it, it's still valid */ g_signal_emit_by_name (container, "actor-removed", actor); st_container_update_pseudo_classes (ST_CONTAINER (container)); if (CLUTTER_ACTOR_IS_VISIBLE (container)) clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); g_object_unref (actor); } static void st_container_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; /* Using g_list_foreach instead of iterating the list manually * because it has better protection against the current node being * removed. This will happen for example if someone calls * clutter_container_foreach(container, clutter_actor_destroy) */ g_list_foreach (priv->children, (GFunc) callback, user_data); } static void st_container_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; priv->children = g_list_remove (priv->children, actor); /* Raise at the top */ if (!sibling) { GList *last_item; last_item = g_list_last (priv->children); if (last_item) sibling = last_item->data; priv->children = g_list_append (priv->children, actor); } else { gint pos; pos = g_list_index (priv->children, sibling) + 1; priv->children = g_list_insert (priv->children, actor, pos); } /* set Z ordering a value below, this will then call sort * as values are equal ordering shouldn't change but Z * values will be correct. * * FIXME: optimise */ if (sibling && clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) { clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); } st_container_update_pseudo_classes (ST_CONTAINER (container)); if (CLUTTER_ACTOR_IS_VISIBLE (container)) clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void st_container_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; priv->children = g_list_remove (priv->children, actor); /* Push to bottom */ if (!sibling) { GList *last_item; last_item = g_list_first (priv->children); if (last_item) sibling = last_item->data; priv->children = g_list_prepend (priv->children, actor); } else { gint pos; pos = g_list_index (priv->children, sibling); priv->children = g_list_insert (priv->children, actor, pos); } /* See comment in st_container_raise() for this */ if (sibling && clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) { clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); } st_container_update_pseudo_classes (ST_CONTAINER (container)); if (CLUTTER_ACTOR_IS_VISIBLE (container)) clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void st_container_sort_depth_order (ClutterContainer *container) { StContainerPrivate *priv = ST_CONTAINER (container)->priv; priv->children = g_list_sort (priv->children, sort_z_order); if (CLUTTER_ACTOR_IS_VISIBLE (container)) clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void st_container_dispose (GObject *object) { StContainerPrivate *priv = ST_CONTAINER (object)->priv; if (priv->children) { clutter_actor_destroy_all_children (CLUTTER_ACTOR (object)); g_list_free (priv->children); priv->children = NULL; } if (priv->first_child) g_object_unref (priv->first_child); priv->first_child = NULL; if (priv->last_child) g_object_unref (priv->last_child); priv->last_child = NULL; G_OBJECT_CLASS (st_container_parent_class)->dispose (object); } static gboolean st_container_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { StContainerPrivate *priv = ST_CONTAINER (actor)->priv; GList *l; if (!CLUTTER_ACTOR_CLASS (st_container_parent_class)->get_paint_volume (actor, volume)) return FALSE; if (!clutter_actor_get_clip_to_allocation (actor)) { /* Based on ClutterGroup/ClutterBox; include the children's * paint volumes, since they may paint outside our allocation. */ for (l = priv->children; l != NULL; l = l->next) { ClutterActor *child = l->data; const ClutterPaintVolume *child_volume; child_volume = clutter_actor_get_transformed_paint_volume (child, actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } } return TRUE; } /* filter @children to contain only only actors that overlap @rbox * when moving in @direction. (Assuming no transformations.) */ static GList * filter_by_position (GList *children, ClutterActorBox *rbox, GtkDirectionType direction) { ClutterActorBox cbox; GList *l, *ret; ClutterActor *child; for (l = children, ret = NULL; l; l = l->next) { child = l->data; clutter_actor_get_allocation_box (child, &cbox); /* Filter out children if they are in the wrong direction from * @rbox, or if they don't overlap it. To account for floating- * point imprecision, an actor is "down" (etc.) from an another * actor even if it overlaps it by up to 0.1 pixels. */ switch (direction) { case GTK_DIR_UP: if (cbox.y2 > rbox->y1 + 0.1) continue; if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1) continue; break; case GTK_DIR_DOWN: if (cbox.y1 < rbox->y2 - 0.1) continue; if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1) continue; break; case GTK_DIR_LEFT: if (cbox.x2 > rbox->x1 + 0.1) continue; if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1) continue; break; case GTK_DIR_RIGHT: if (cbox.x1 < rbox->x2 - 0.1) continue; if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1) continue; break; default: g_return_val_if_reached (NULL); } ret = g_list_prepend (ret, child); } g_list_free (children); return ret; } typedef struct { GtkDirectionType direction; ClutterActorBox box; } StContainerChildSortData; static int sort_by_position (gconstpointer a, gconstpointer b, gpointer user_data) { ClutterActor *actor_a = (ClutterActor *)a; ClutterActor *actor_b = (ClutterActor *)b; StContainerChildSortData *sort_data = user_data; GtkDirectionType direction = sort_data->direction; ClutterActorBox abox, bbox; int ax, ay, bx, by; int cmp, fmid; /* Determine the relationship, relative to motion in @direction, of * the center points of the two actors. Eg, for %GTK_DIR_UP, we * return a negative number if @actor_a's center is below @actor_b's * center, and postive if vice versa, which will result in an * overall list sorted bottom-to-top. */ clutter_actor_get_allocation_box (actor_a, &abox); ax = (int)(abox.x1 + abox.x2) / 2; ay = (int)(abox.y1 + abox.y2) / 2; clutter_actor_get_allocation_box (actor_b, &bbox); bx = (int)(bbox.x1 + bbox.x2) / 2; by = (int)(bbox.y1 + bbox.y2) / 2; switch (direction) { case GTK_DIR_UP: cmp = by - ay; break; case GTK_DIR_DOWN: cmp = ay - by; break; case GTK_DIR_LEFT: cmp = bx - ax; break; case GTK_DIR_RIGHT: cmp = ax - bx; break; default: g_return_val_if_reached (0); } if (cmp) return cmp; /* If two actors have the same center on the axis being sorted, * prefer the one that is closer to the center of the current focus * actor on the other axis. Eg, for %GTK_DIR_UP, prefer whichever * of @actor_a and @actor_b has a horizontal center closest to the * current focus actor's horizontal center. * * (This matches GTK's behavior.) */ switch (direction) { case GTK_DIR_UP: case GTK_DIR_DOWN: fmid = (int)(sort_data->box.x1 + sort_data->box.x2) / 2; return abs (ax - fmid) - abs (bx - fmid); case GTK_DIR_LEFT: case GTK_DIR_RIGHT: fmid = (int)(sort_data->box.y1 + sort_data->box.y2) / 2; return abs (ay - fmid) - abs (by - fmid); default: g_return_val_if_reached (0); } } static gboolean st_container_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction) { StContainer *container = ST_CONTAINER (widget); ClutterActor *container_actor, *focus_child; GList *children, *l; container_actor = CLUTTER_ACTOR (widget); if (from == container_actor) return FALSE; /* Figure out if @from is a descendant of @container, and if so, * set @focus_child to the immediate child of @container that * contains (or *is*) @from. */ focus_child = from; while (focus_child && clutter_actor_get_parent (focus_child) != container_actor) focus_child = clutter_actor_get_parent (focus_child); if (st_widget_get_can_focus (widget)) { if (!focus_child) { /* Accept focus from outside */ clutter_actor_grab_key_focus (container_actor); return TRUE; } else { /* Yield focus from within: since @container itself is * focusable we don't allow the focus to be navigated * within @container. */ return FALSE; } } /* See if we can navigate within @focus_child */ if (focus_child && ST_IS_WIDGET (focus_child)) { if (st_widget_navigate_focus (ST_WIDGET (focus_child), from, direction, FALSE)) return TRUE; } /* At this point we know that we want to navigate focus to one of * @container's immediate children; the next one after @focus_child, * or the first one if @focus_child is %NULL. (With "next" and * "first" being determined by @direction.) */ children = st_container_get_focus_chain (container); if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD) { if (direction == GTK_DIR_TAB_BACKWARD) children = g_list_reverse (children); if (focus_child) { /* Remove focus_child and any earlier children */ while (children && children->data != focus_child) children = g_list_delete_link (children, children); if (children) children = g_list_delete_link (children, children); } } else /* direction is an arrow key, not tab */ { StContainerChildSortData sort_data; /* Compute the allocation box of the previous focused actor, in * @container's coordinate space. If there was no previous focus, * use the coordinates of the appropriate edge of @container. * * Note that all of this code assumes the actors are not * transformed (or at most, they are all scaled by the same * amount). If @container or any of its children is rotated, or * any child is inconsistently scaled, then the focus chain will * probably be unpredictable. */ if (focus_child) { clutter_actor_get_allocation_box (focus_child, &sort_data.box); } else { clutter_actor_get_allocation_box (CLUTTER_ACTOR (container), &sort_data.box); switch (direction) { case GTK_DIR_UP: sort_data.box.y1 = sort_data.box.y2; break; case GTK_DIR_DOWN: sort_data.box.y2 = sort_data.box.y1; break; case GTK_DIR_LEFT: sort_data.box.x1 = sort_data.box.x2; break; case GTK_DIR_RIGHT: sort_data.box.x2 = sort_data.box.x1; break; default: g_warn_if_reached (); } } sort_data.direction = direction; if (focus_child) children = filter_by_position (children, &sort_data.box, direction); if (children) children = g_list_sort_with_data (children, sort_by_position, &sort_data); } /* Now try each child in turn */ for (l = children; l; l = l->next) { if (ST_IS_WIDGET (l->data)) { if (st_widget_navigate_focus (l->data, from, direction, FALSE)) { g_list_free (children); return TRUE; } } } g_list_free (children); return FALSE; } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = st_container_add; iface->remove = st_container_remove; iface->foreach = st_container_foreach; iface->raise = st_container_raise; iface->lower = st_container_lower; iface->sort_depth_order = st_container_sort_depth_order; } static void st_container_init (StContainer *container) { container->priv = ST_CONTAINER_GET_PRIVATE (container); } static void st_container_class_init (StContainerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); StContainerClass *container_class = ST_CONTAINER_CLASS (klass); g_type_class_add_private (klass, sizeof (StContainerPrivate)); object_class->dispose = st_container_dispose; actor_class->get_paint_volume = st_container_get_paint_volume; widget_class->navigate_focus = st_container_navigate_focus; container_class->get_focus_chain = st_container_real_get_focus_chain; } Cinnamon-2.8.6/src/st/st-scrollable.h0000664000175000017500000000463112625666430016367 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scrollable.h: Scrollable interface * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_SCROLLABLE_H__ #define __ST_SCROLLABLE_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_SCROLLABLE (st_scrollable_get_type ()) #define ST_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLLABLE, StScrollable)) #define ST_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLLABLE)) #define ST_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), ST_TYPE_SCROLLABLE, StScrollableInterface)) typedef struct _StScrollable StScrollable; /* Dummy object */ typedef struct _StScrollableInterface StScrollableInterface; struct _StScrollableInterface { GTypeInterface parent; void (* set_adjustments) (StScrollable *scrollable, StAdjustment *hadjustment, StAdjustment *vadjustment); void (* get_adjustments) (StScrollable *scrollable, StAdjustment **hadjustment, StAdjustment **vadjustment); }; GType st_scrollable_get_type (void) G_GNUC_CONST; void st_scrollable_set_adjustments (StScrollable *scrollable, StAdjustment *hadjustment, StAdjustment *vadjustment); void st_scrollable_get_adjustments (StScrollable *scrollable, StAdjustment **hadjustment, StAdjustment **vadjustment); G_END_DECLS #endif /* __ST_SCROLLABLE_H__ */ Cinnamon-2.8.6/src/st/st-theme.h0000664000175000017500000000447112625666430015351 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme.h: A set of CSS stylesheets used for rule matching * * Copyright 2008, 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_H__ #define __ST_THEME_H__ #include #include "st-theme-node.h" G_BEGIN_DECLS /** * SECTION:StTheme * @short_description: a set of stylesheets * * #StTheme holds a set of stylesheets. (The "cascade" of the name * Cascading Stylesheets.) A #StTheme can be set to apply to all the actors * in a stage using st_theme_context_set_theme() or applied to a subtree * of actors using st_widget_set_theme(). */ typedef struct _StThemeClass StThemeClass; #define ST_TYPE_THEME (st_theme_get_type ()) #define ST_THEME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME, StTheme)) #define ST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME, StThemeClass)) #define ST_IS_THEME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME)) #define ST_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME)) #define ST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME, StThemeClass)) GType st_theme_get_type (void) G_GNUC_CONST; StTheme *st_theme_new (const char *application_stylesheet, const char *theme_stylesheet, const char *default_stylesheet); gboolean st_theme_load_stylesheet (StTheme *theme, const char *path, GError **error); void st_theme_unload_stylesheet (StTheme *theme, const char *path); GSList *st_theme_get_custom_stylesheets (StTheme *theme); G_END_DECLS #endif /* __ST_THEME_H__ */ Cinnamon-2.8.6/src/st/st-private.h0000664000175000017500000000712612625666430015721 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-private.h: Private declarations and functions * * Copyright 2007 OpenedHand * Copyright 2009 Intel Corporation. * Copyright 2010 Red Hat, Inc. * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_PRIVATE_H__ #define __ST_PRIVATE_H__ #include #include #include "st-widget.h" #include "st-bin.h" #include "st-shadow.h" G_BEGIN_DECLS #define I_(str) (g_intern_static_string ((str))) #define ST_PARAM_READABLE \ (G_PARAM_READABLE | \ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) #define ST_PARAM_READWRITE \ (G_PARAM_READABLE | G_PARAM_WRITABLE | \ G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) G_END_DECLS ClutterActor *_st_widget_get_dnd_clone (StWidget *widget); void _st_get_align_factors (StWidget *widget, StAlign x_align, StAlign y_align, gdouble *x_align_out, gdouble *y_align_out); void _st_actor_get_preferred_width (ClutterActor *actor, gfloat for_height, gboolean y_fill, gfloat *min_width_p, gfloat *natural_width_p); void _st_actor_get_preferred_height (ClutterActor *actor, gfloat for_width, gboolean x_fill, gfloat *min_height_p, gfloat *natural_height_p); void _st_allocate_fill (StWidget *parent, ClutterActor *child, ClutterActorBox *childbox, StAlign x_align, StAlign y_align, gboolean x_fill, gboolean y_fill); void _st_set_text_from_style (ClutterText *text, StThemeNode *theme_node); CoglHandle _st_create_texture_material (CoglHandle src_texture); /* Helper for widgets which need to draw additional shadows */ CoglHandle _st_create_shadow_material (StShadow *shadow_spec, CoglHandle src_texture); CoglHandle _st_create_shadow_material_from_actor (StShadow *shadow_spec, ClutterActor *actor); cairo_pattern_t *_st_create_shadow_cairo_pattern (StShadow *shadow_spec, cairo_pattern_t *src_pattern); void _st_paint_shadow_with_opacity (StShadow *shadow_spec, CoglHandle shadow_material, ClutterActorBox *box, guint8 paint_opacity); #endif /* __ST_PRIVATE_H__ */ Cinnamon-2.8.6/src/st/st-drawing-area.h0000664000175000017500000000450312625666430016604 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-drawing-area.h: A dynamically-sized Cairo drawing area * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_DRAWING_AREA_H__ #define __ST_DRAWING_AREA_H__ #include "st-widget.h" #include #define ST_TYPE_DRAWING_AREA (st_drawing_area_get_type ()) #define ST_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_DRAWING_AREA, StDrawingArea)) #define ST_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_DRAWING_AREA, StDrawingAreaClass)) #define ST_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_DRAWING_AREA)) #define ST_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_DRAWING_AREA)) #define ST_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_DRAWING_AREA, StDrawingAreaClass)) typedef struct _StDrawingArea StDrawingArea; typedef struct _StDrawingAreaClass StDrawingAreaClass; typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate; struct _StDrawingArea { StWidget parent; StDrawingAreaPrivate *priv; }; struct _StDrawingAreaClass { StWidgetClass parent_class; void (*repaint) (StDrawingArea *area); }; GType st_drawing_area_get_type (void) G_GNUC_CONST; void st_drawing_area_queue_repaint (StDrawingArea *area); cairo_t *st_drawing_area_get_context (StDrawingArea *area); void st_drawing_area_get_surface_size (StDrawingArea *area, guint *width, guint *height); #endif /* __ST_DRAWING_AREA_H__ */ Cinnamon-2.8.6/src/st/st-shadow.h0000664000175000017500000000504212625666430015527 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-shadow.h: Boxed type holding for -st-shadow attributes * * Copyright 2009, 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_SHADOW__ #define __ST_SHADOW__ #include G_BEGIN_DECLS #define ST_TYPE_SHADOW (st_shadow_get_type ()) typedef struct _StShadow StShadow; /** * StShadow: * @color: shadow's color * @xoffset: horizontal offset - positive values mean placement to the right, * negative values placement to the left of the element. * @yoffset: vertical offset - positive values mean placement below, negative * values placement above the element. * @blur: shadow's blur radius - a value of 0.0 will result in a hard shadow. * @spread: shadow's spread radius - grow the shadow without enlarging the * blur. * * Attributes of the -st-shadow property. */ struct _StShadow { ClutterColor color; gdouble xoffset; gdouble yoffset; gdouble blur; gdouble spread; gboolean inset; volatile int ref_count; }; GType st_shadow_get_type (void) G_GNUC_CONST; StShadow *st_shadow_new (ClutterColor *color, gdouble xoffset, gdouble yoffset, gdouble blur, gdouble spread, gboolean inset); StShadow *st_shadow_ref (StShadow *shadow); void st_shadow_unref (StShadow *shadow); gboolean st_shadow_equal (StShadow *shadow, StShadow *other); void st_shadow_get_box (StShadow *shadow, const ClutterActorBox *actor_box, ClutterActorBox *shadow_box); G_END_DECLS #endif /* __ST_SHADOW__ */ Cinnamon-2.8.6/src/st/st-box-layout.h0000664000175000017500000000576012625666430016354 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-box-layout.h: box layout actor * * Copyright 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _ST_BOX_LAYOUT_H #define _ST_BOX_LAYOUT_H #include G_BEGIN_DECLS #define ST_TYPE_BOX_LAYOUT st_box_layout_get_type() #define ST_BOX_LAYOUT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_BOX_LAYOUT, StBoxLayout)) #define ST_BOX_LAYOUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_BOX_LAYOUT, StBoxLayoutClass)) #define ST_IS_BOX_LAYOUT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_BOX_LAYOUT)) #define ST_IS_BOX_LAYOUT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_BOX_LAYOUT)) #define ST_BOX_LAYOUT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_BOX_LAYOUT, StBoxLayoutClass)) typedef struct _StBoxLayout StBoxLayout; typedef struct _StBoxLayoutClass StBoxLayoutClass; typedef struct _StBoxLayoutPrivate StBoxLayoutPrivate; /** * StBoxLayout: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _StBoxLayout { /*< private >*/ StContainer parent; StBoxLayoutPrivate *priv; }; struct _StBoxLayoutClass { StContainerClass parent_class; }; GType st_box_layout_get_type (void); StWidget *st_box_layout_new (void); void st_box_layout_set_vertical (StBoxLayout *box, gboolean vertical); gboolean st_box_layout_get_vertical (StBoxLayout *box); void st_box_layout_set_pack_start (StBoxLayout *box, gboolean pack_start); gboolean st_box_layout_get_pack_start (StBoxLayout *box); void st_box_layout_set_align_end (StBoxLayout *box, gboolean align_end); gboolean st_box_layout_get_align_end (StBoxLayout *box); void st_box_layout_insert_actor (StBoxLayout *self, ClutterActor *actor, int pos); void st_box_layout_insert_before (StBoxLayout *self, ClutterActor *actor, ClutterActor *sibling); G_END_DECLS #endif /* _ST_BOX_LAYOUT_H */ Cinnamon-2.8.6/src/st/st-im-text.c0000664000175000017500000003765612625666430015644 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-im-text.c: Text widget with input method support * * Copyright 2009 Red Hat, Inc. * * This started as a copy of ClutterIMText converted to use * GtkIMContext rather than ClutterIMContext. Original code: * * Author: raymond liu * * Copyright (C) 2009, Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:StIMText * @short_description: Text widget with input method support * @stability: Unstable * @see_also: #ClutterText * @include: st-imtext/st-imtext.h * * #StIMText derives from ClutterText and hooks up better text input * via #GtkIMContext. It is meant to be a drop-in replacement for * ClutterIMText but using GtkIMContext rather than ClutterIMContext. */ /* Places where this actor doesn't support all of GtkIMContext: * * A) It doesn't support preedit. This makes it fairly useless for * most complicated input methods. Fixing this requires support * directly in ClutterText, since there is no way to wedge a * preedit string in externally. * B) It doesn't support surrounding context via the * :retrieve-surrounding and :delete-surrounding signals. This could * be added here, but only affects a small number of input methods * and really doesn't make a lot of sense without A) * * Another problem that will show up with usage in Cinnamon's overview * is that the user may have trouble seeing and interacting with ancilliary * windows shown by the IM. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "st-im-text.h" #define ST_IM_TEXT_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_IM_TEXT, StIMTextPrivate)) struct _StIMTextPrivate { GtkIMContext *im_context; GdkWindow *window; guint need_im_reset : 1; }; static void st_im_text_commit_cb (GtkIMContext *context, const gchar *str, StIMText *imtext); G_DEFINE_TYPE (StIMText, st_im_text, CLUTTER_TYPE_TEXT) static void st_im_text_dispose (GObject *object) { StIMTextPrivate *priv = ST_IM_TEXT (object)->priv; G_OBJECT_CLASS (st_im_text_parent_class)->dispose (object); if (priv->im_context != NULL) { g_signal_handlers_disconnect_by_func (priv->im_context, (void *) st_im_text_commit_cb, object); g_object_unref (priv->im_context); priv->im_context = NULL; } } static void update_im_cursor_location (StIMText *self) { StIMTextPrivate *priv = self->priv; ClutterText *clutter_text = CLUTTER_TEXT (self); gint position; gfloat cursor_x, cursor_y, cursor_height; gfloat actor_x, actor_y; GdkRectangle area; position = clutter_text_get_cursor_position (clutter_text); clutter_text_position_to_coords (clutter_text, position, &cursor_x, &cursor_y, &cursor_height); clutter_actor_get_transformed_position (CLUTTER_ACTOR (self), &actor_x, &actor_y); area.x = (int)(0.5 + cursor_x + actor_x); area.y = (int)(0.5 + cursor_y + actor_y); area.width = 0; area.height = (int)(0.5 + cursor_height); gtk_im_context_set_cursor_location (priv->im_context, &area); } static void st_im_text_commit_cb (GtkIMContext *context, const gchar *str, StIMText *imtext) { ClutterText *clutter_text = CLUTTER_TEXT (imtext); if (clutter_text_get_editable (clutter_text)) { clutter_text_delete_selection (clutter_text); clutter_text_insert_text (clutter_text, str, clutter_text_get_cursor_position (clutter_text)); } } static void st_im_text_preedit_changed_cb (GtkIMContext *context, StIMText *imtext) { ClutterText *clutter_text = CLUTTER_TEXT (imtext); gchar *preedit_str = NULL; PangoAttrList *preedit_attrs = NULL; gint cursor_pos = 0; gtk_im_context_get_preedit_string (context, &preedit_str, &preedit_attrs, &cursor_pos); clutter_text_set_preedit_string (clutter_text, preedit_str, preedit_attrs, cursor_pos); g_free (preedit_str); pango_attr_list_unref (preedit_attrs); } static gboolean st_im_text_retrieve_surrounding_cb (GtkIMContext *context, StIMText *imtext) { ClutterText *clutter_text = CLUTTER_TEXT (imtext); ClutterTextBuffer *buffer; const gchar *text; gint cursor_pos; buffer = clutter_text_get_buffer (clutter_text); text = clutter_text_buffer_get_text (buffer); cursor_pos = clutter_text_get_cursor_position (clutter_text); if (cursor_pos < 0) cursor_pos = clutter_text_buffer_get_length (buffer); gtk_im_context_set_surrounding (context, text, /* length and cursor_index are in bytes */ clutter_text_buffer_get_bytes (buffer), g_utf8_offset_to_pointer (text, cursor_pos) - text); return TRUE; } static gboolean st_im_text_delete_surrounding_cb (GtkIMContext *context, gint offset, gint n_chars, StIMText *imtext) { ClutterText *clutter_text = CLUTTER_TEXT (imtext); if (clutter_text_get_editable (clutter_text)) { gint cursor_pos = clutter_text_get_cursor_position (clutter_text); clutter_text_delete_text (clutter_text, cursor_pos + offset, cursor_pos + offset + n_chars); } return TRUE; } static void reset_im_context (StIMText *self) { StIMTextPrivate *priv = self->priv; if (priv->need_im_reset) { gtk_im_context_reset (priv->im_context); priv->need_im_reset = FALSE; } } static void st_im_text_paint (ClutterActor *actor) { StIMText *self = ST_IM_TEXT (actor); ClutterText *clutter_text = CLUTTER_TEXT (actor); /* This updates the cursor position as a side-effect */ if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint) CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint (actor); if (clutter_text_get_editable (clutter_text)) update_im_cursor_location (self); } static gboolean st_im_text_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { return clutter_paint_volume_set_from_allocation (volume, self); } /* Returns a new reference to window */ static GdkWindow * window_for_actor (ClutterActor *actor) { GdkDisplay *display = gdk_display_get_default (); ClutterActor *stage; Window xwindow; GdkWindow *window; stage = clutter_actor_get_stage (actor); xwindow = clutter_x11_get_stage_window ((ClutterStage *)stage); window = gdk_x11_window_lookup_for_display (display, xwindow); if (window) g_object_ref (window); else window = gdk_x11_window_foreign_new_for_display (display, xwindow); return window; } static void st_im_text_realize (ClutterActor *actor) { StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv; priv->window = window_for_actor (actor); gtk_im_context_set_client_window (priv->im_context, priv->window); } static void st_im_text_unrealize (ClutterActor *actor) { StIMText *self = ST_IM_TEXT (actor); StIMTextPrivate *priv = self->priv; reset_im_context (self); gtk_im_context_set_client_window (priv->im_context, NULL); g_object_unref (priv->window); priv->window = NULL; } static gboolean key_is_modifier (guint16 keyval) { /* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this * really should be implemented */ switch (keyval) { case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: case GDK_KEY_Control_L: case GDK_KEY_Control_R: case GDK_KEY_Caps_Lock: case GDK_KEY_Shift_Lock: case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: case GDK_KEY_Super_L: case GDK_KEY_Super_R: case GDK_KEY_Hyper_L: case GDK_KEY_Hyper_R: case GDK_KEY_ISO_Lock: case GDK_KEY_ISO_Level2_Latch: case GDK_KEY_ISO_Level3_Shift: case GDK_KEY_ISO_Level3_Latch: case GDK_KEY_ISO_Level3_Lock: case GDK_KEY_ISO_Level5_Shift: case GDK_KEY_ISO_Level5_Latch: case GDK_KEY_ISO_Level5_Lock: case GDK_KEY_ISO_Group_Shift: case GDK_KEY_ISO_Group_Latch: case GDK_KEY_ISO_Group_Lock: return TRUE; default: return FALSE; } } static GdkEventKey * key_event_to_gdk (ClutterKeyEvent *event_clutter) { GdkEventKey *event_gdk; event_gdk = (GdkEventKey *)gdk_event_new ((event_clutter->type == CLUTTER_KEY_PRESS) ? GDK_KEY_PRESS : GDK_KEY_RELEASE); event_gdk->window = window_for_actor ((ClutterActor *)event_clutter->stage); event_gdk->send_event = FALSE; event_gdk->time = event_clutter->time; /* This depends on ClutterModifierType and GdkModifierType being * identical, which they are currently. (They both match the X * modifier state in the low 16-bits and have the same extensions.) */ event_gdk->state = event_clutter->modifier_state; event_gdk->keyval = event_clutter->keyval; event_gdk->hardware_keycode = event_clutter->hardware_keycode; /* For non-proper non-XKB support, we'd need a huge cut-and-paste * from gdkkeys-x11.c; this is a macro that just shifts a few bits * out of state, so won't make the situation worse if the server * doesn't support XKB; we'll just end up with group == 0 */ event_gdk->group = XkbGroupForCoreState (event_gdk->state); if (event_clutter->unicode_value) { /* This is not particularly close to what GDK does - event_gdk->string * is supposed to be in the locale encoding, and have control keys * as control characters, etc. See gdkevents-x11.c:translate_key_event(). * Hopefully no input method is using event.string. */ char buf[6]; event_gdk->length = g_unichar_to_utf8 (event_clutter->unicode_value, buf); event_gdk->string = g_strndup (buf, event_gdk->length); } event_gdk->is_modifier = key_is_modifier (event_gdk->keyval); return event_gdk; } static gboolean st_im_text_button_press_event (ClutterActor *actor, ClutterButtonEvent *event) { /* The button press indicates the user moving the cursor, or selecting * etc, so we should abort any current preedit operation. ClutterText * treats all buttons identically, so so do we. */ reset_im_context (ST_IM_TEXT (actor)); if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event) return CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event (actor, event); else return FALSE; } static gboolean st_im_text_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { StIMText *self = ST_IM_TEXT (actor); StIMTextPrivate *priv = self->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); gboolean result = FALSE; int old_position; if (clutter_text_get_editable (clutter_text)) { GdkEventKey *event_gdk = key_event_to_gdk (event); if (gtk_im_context_filter_keypress (priv->im_context, event_gdk)) { priv->need_im_reset = TRUE; result = TRUE; } gdk_event_free ((GdkEvent *)event_gdk); } old_position = clutter_text_get_cursor_position (clutter_text); if (!result && CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event) result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event (actor, event); if (clutter_text_get_cursor_position (clutter_text) != old_position) reset_im_context (self); return result; } static gboolean st_im_text_key_release_event (ClutterActor *actor, ClutterKeyEvent *event) { StIMText *self = ST_IM_TEXT (actor); StIMTextPrivate *priv = self->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); GdkEventKey *event_gdk; gboolean result = FALSE; if (clutter_text_get_editable (clutter_text)) { event_gdk = key_event_to_gdk (event); if (gtk_im_context_filter_keypress (priv->im_context, event_gdk)) { priv->need_im_reset = TRUE; result = TRUE; } gdk_event_free ((GdkEvent *)event_gdk); } if (!result && CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event) result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event (actor, event); return result; } static void st_im_text_key_focus_in (ClutterActor *actor) { StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); if (clutter_text_get_editable (clutter_text)) { priv->need_im_reset = TRUE; gtk_im_context_focus_in (priv->im_context); } if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in) CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in (actor); } static void st_im_text_key_focus_out (ClutterActor *actor) { StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); if (clutter_text_get_editable (clutter_text)) { priv->need_im_reset = TRUE; gtk_im_context_focus_out (priv->im_context); } if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out) CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out (actor); } static void st_im_text_class_init (StIMTextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (StIMTextPrivate)); object_class->dispose = st_im_text_dispose; actor_class->paint = st_im_text_paint; actor_class->get_paint_volume = st_im_text_get_paint_volume; actor_class->realize = st_im_text_realize; actor_class->unrealize = st_im_text_unrealize; actor_class->button_press_event = st_im_text_button_press_event; actor_class->key_press_event = st_im_text_key_press_event; actor_class->key_release_event = st_im_text_key_release_event; actor_class->key_focus_in = st_im_text_key_focus_in; actor_class->key_focus_out = st_im_text_key_focus_out; } static void st_im_text_init (StIMText *self) { StIMTextPrivate *priv; self->priv = priv = ST_IM_TEXT_GET_PRIVATE (self); priv->im_context = gtk_im_multicontext_new (); g_signal_connect (priv->im_context, "commit", G_CALLBACK (st_im_text_commit_cb), self); g_signal_connect (priv->im_context, "preedit-changed", G_CALLBACK (st_im_text_preedit_changed_cb), self); g_signal_connect (priv->im_context, "retrieve-surrounding", G_CALLBACK (st_im_text_retrieve_surrounding_cb), self); g_signal_connect (priv->im_context, "delete-surrounding", G_CALLBACK (st_im_text_delete_surrounding_cb), self); } /** * st_im_text_new: * @text: text to set to * * Create a new #StIMText with the specified text * * Returns: a new #ClutterActor */ ClutterActor * st_im_text_new (const gchar *text) { return g_object_new (ST_TYPE_IM_TEXT, "text", text, NULL); } Cinnamon-2.8.6/src/st/st-overflow-box.h0000664000175000017500000000460112625666430016673 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-overflow-box.h: A vertical box which paints as many actors as it can fit * * Copyright 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _ST_OVERFLOW_BOX_H #define _ST_OVERFLOW_BOX_H #include G_BEGIN_DECLS #define ST_TYPE_OVERFLOW_BOX st_overflow_box_get_type() #define ST_OVERFLOW_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_OVERFLOW_BOX, StOverflowBox)) #define ST_OVERFLOW_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_OVERFLOW_BOX, StOverflowBoxClass)) #define ST_IS_OVERFLOW_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_OVERFLOW_BOX)) #define ST_IS_OVERFLOW_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_OVERFLOW_BOX)) #define ST_OVERFLOW_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_OVERFLOW_BOX, StOverflowBoxClass)) typedef struct _StOverflowBox StOverflowBox; typedef struct _StOverflowBoxClass StOverflowBoxClass; typedef struct _StOverflowBoxPrivate StOverflowBoxPrivate; /** * StOverflowBox: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _StOverflowBox { /*< private >*/ StContainer parent; StOverflowBoxPrivate *priv; }; struct _StOverflowBoxClass { StContainerClass parent_class; }; GType st_overflow_box_get_type (void); void st_overflow_box_set_min_children (StOverflowBox *self, guint min_children); guint st_overflow_box_get_n_children (StOverflowBox *box); guint st_overflow_box_get_n_visible (StOverflowBox *box); gboolean st_overflow_box_get_min_children (StOverflowBox *box); G_END_DECLS #endif /* _ST_OVERFLOW_BOX_H */ Cinnamon-2.8.6/src/st/st-widget.h0000664000175000017500000002077312625666430015535 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-widget.h: Base class for St actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Abderrahim Kitouni * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_WIDGET_H__ #define __ST_WIDGET_H__ #include #include #include #include G_BEGIN_DECLS typedef enum { ST_TEXT_DIRECTION_NONE, ST_TEXT_DIRECTION_LTR, ST_TEXT_DIRECTION_RTL } StTextDirection; #define ST_TYPE_WIDGET (st_widget_get_type ()) #define ST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_WIDGET, StWidget)) #define ST_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_WIDGET)) #define ST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_WIDGET, StWidgetClass)) #define ST_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_WIDGET)) #define ST_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_WIDGET, StWidgetClass)) typedef struct _StWidget StWidget; typedef struct _StWidgetPrivate StWidgetPrivate; typedef struct _StWidgetClass StWidgetClass; /** * StWidget: * * Base class for stylable actors. The contents of the #StWidget * structure are private and should only be accessed through the * public API. */ struct _StWidget { /*< private >*/ ClutterActor parent_instance; StWidgetPrivate *priv; }; /** * StWidgetClass: * * Base class for stylable actors. */ struct _StWidgetClass { /*< private >*/ ClutterActorClass parent_class; /* signals */ void (* style_changed) (StWidget *self); void (* popup_menu) (StWidget *self); /* vfuncs */ gboolean (* navigate_focus) (StWidget *self, ClutterActor *from, GtkDirectionType direction); GType (* get_accessible_type) (void); }; GType st_widget_get_type (void) G_GNUC_CONST; void st_widget_set_style_pseudo_class (StWidget *actor, const gchar *pseudo_class_list); void st_widget_change_style_pseudo_class (StWidget *actor, const gchar *pseudo_class, gboolean add); void st_widget_add_style_pseudo_class (StWidget *actor, const gchar *pseudo_class); void st_widget_remove_style_pseudo_class (StWidget *actor, const gchar *pseudo_class); const gchar * st_widget_get_style_pseudo_class (StWidget *actor); gboolean st_widget_has_style_pseudo_class (StWidget *actor, const gchar *pseudo_class); void st_widget_set_style_class_name (StWidget *actor, const gchar *style_class_list); void st_widget_add_style_class_name (StWidget *actor, const gchar *style_class); void st_widget_remove_style_class_name (StWidget *actor, const gchar *style_class); const gchar * st_widget_get_style_class_name (StWidget *actor); gboolean st_widget_has_style_class_name (StWidget *actor, const gchar *style_class); void st_widget_set_style (StWidget *actor, const gchar *style); const gchar * st_widget_get_style (StWidget *actor); void st_widget_set_important (StWidget *actor, gboolean important); gboolean st_widget_get_important (StWidget *actor); void st_widget_set_theme (StWidget *actor, StTheme *theme); StTheme * st_widget_get_theme (StWidget *actor); void st_widget_set_track_hover (StWidget *widget, gboolean track_hover); gboolean st_widget_get_track_hover (StWidget *widget); void st_widget_set_hover (StWidget *widget, gboolean hover); void st_widget_sync_hover (StWidget *widget); gboolean st_widget_get_hover (StWidget *widget); void st_widget_ensure_style (StWidget *widget); StTextDirection st_widget_get_default_direction (void); void st_widget_set_default_direction (StTextDirection dir); StTextDirection st_widget_get_direction (StWidget *self); void st_widget_set_direction (StWidget *self, StTextDirection dir); void st_widget_set_can_focus (StWidget *widget, gboolean can_focus); gboolean st_widget_get_can_focus (StWidget *widget); gboolean st_widget_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction, gboolean wrap_around); ClutterActor * st_widget_get_label_actor (StWidget *widget); void st_widget_set_label_actor (StWidget *widget, ClutterActor *label); /* Only to be used by sub-classes of StWidget */ void st_widget_style_changed (StWidget *widget); StThemeNode * st_widget_get_theme_node (StWidget *widget); StThemeNode * st_widget_peek_theme_node (StWidget *widget); /* debug methods */ char *st_describe_actor (ClutterActor *actor); void st_set_slow_down_factor (gfloat factor); gfloat st_get_slow_down_factor (void); /* accessibility methods */ void st_widget_set_accessible_role (StWidget *widget, AtkRole role); AtkRole st_widget_get_accessible_role (StWidget *widget); void st_widget_add_accessible_state (StWidget *widget, AtkStateType state); void st_widget_remove_accessible_state (StWidget *widget, AtkStateType state); void st_widget_set_accessible_name (StWidget *widget, const gchar *name); const gchar * st_widget_get_accessible_name (StWidget *widget); G_END_DECLS #endif /* __ST_WIDGET_H__ */ Cinnamon-2.8.6/src/st/st-scroll-view.h0000664000175000017500000000672112625666430016515 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-view.h: Container with scroll-bars * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * Copyright 2010 Red Hat, Inc. * Copyright 2010 Maxim Ermilov * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_SCROLL_VIEW_H__ #define __ST_SCROLL_VIEW_H__ #include G_BEGIN_DECLS #define ST_TYPE_SCROLL_VIEW (st_scroll_view_get_type()) #define ST_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW, StScrollView)) #define ST_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW)) #define ST_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW, StScrollViewClass)) #define ST_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW)) #define ST_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW, StScrollViewClass)) typedef struct _StScrollView StScrollView; typedef struct _StScrollViewPrivate StScrollViewPrivate; typedef struct _StScrollViewClass StScrollViewClass; /** * StScrollView: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _StScrollView { /*< private >*/ StBin parent_instance; StScrollViewPrivate *priv; }; struct _StScrollViewClass { StBinClass parent_class; }; GType st_scroll_view_get_type (void) G_GNUC_CONST; StWidget *st_scroll_view_new (void); ClutterActor *st_scroll_view_get_hscroll_bar (StScrollView *scroll); ClutterActor *st_scroll_view_get_vscroll_bar (StScrollView *scroll); gfloat st_scroll_view_get_column_size (StScrollView *scroll); void st_scroll_view_set_column_size (StScrollView *scroll, gfloat column_size); gfloat st_scroll_view_get_row_size (StScrollView *scroll); void st_scroll_view_set_row_size (StScrollView *scroll, gfloat row_size); void st_scroll_view_set_mouse_scrolling (StScrollView *scroll, gboolean enabled); gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll); void st_scroll_view_set_auto_scrolling (StScrollView *scroll, gboolean enabled); gboolean st_scroll_view_get_auto_scrolling (StScrollView *scroll); void st_scroll_view_set_policy (StScrollView *scroll, GtkPolicyType hscroll, GtkPolicyType vscroll); G_END_DECLS #endif /* __ST_SCROLL_VIEW_H__ */ Cinnamon-2.8.6/src/st/st-clipboard.h0000664000175000017500000000537712625666430016214 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-clipboard.h: clipboard object * * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _ST_CLIPBOARD_H #define _ST_CLIPBOARD_H #include G_BEGIN_DECLS #define ST_TYPE_CLIPBOARD st_clipboard_get_type() #define ST_CLIPBOARD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_CLIPBOARD, StClipboard)) #define ST_CLIPBOARD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_CLIPBOARD, StClipboardClass)) #define ST_IS_CLIPBOARD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_CLIPBOARD)) #define ST_IS_CLIPBOARD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_CLIPBOARD)) #define ST_CLIPBOARD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_CLIPBOARD, StClipboardClass)) typedef struct _StClipboard StClipboard; typedef struct _StClipboardClass StClipboardClass; typedef struct _StClipboardPrivate StClipboardPrivate; /** * StClipboard: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _StClipboard { /*< private >*/ GObject parent; StClipboardPrivate *priv; }; struct _StClipboardClass { GObjectClass parent_class; }; /** * StClipboardCallbackFunc: * @clipboard: A #StClipboard * @text: text from the clipboard * @user_data: user data * * Callback function called when text is retrieved from the clipboard. */ typedef void (*StClipboardCallbackFunc) (StClipboard *clipboard, const gchar *text, gpointer user_data); GType st_clipboard_get_type (void); StClipboard* st_clipboard_get_default (void); void st_clipboard_get_text (StClipboard *clipboard, StClipboardCallbackFunc callback, gpointer user_data); void st_clipboard_set_text (StClipboard *clipboard, const gchar *text); G_END_DECLS #endif /* _ST_CLIPBOARD_H */ Cinnamon-2.8.6/src/st/st-scroll-bar.h0000664000175000017500000000475112625666430016310 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-bar.h: Scroll bar actor * * Copyright 2008 OpenedHand * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_SCROLL_BAR_H__ #define __ST_SCROLL_BAR_H__ #include #include G_BEGIN_DECLS #define ST_TYPE_SCROLL_BAR (st_scroll_bar_get_type()) #define ST_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_BAR, StScrollBar)) #define ST_IS_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_BAR)) #define ST_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_BAR, StScrollBarClass)) #define ST_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_BAR)) #define ST_SCROLL_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_BAR, StScrollBarClass)) typedef struct _StScrollBar StScrollBar; typedef struct _StScrollBarPrivate StScrollBarPrivate; typedef struct _StScrollBarClass StScrollBarClass; /** * StScrollBar: * * The contents of this structure are private and should only be accessed * through the public API. */ struct _StScrollBar { /*< private >*/ StWidget parent_instance; StScrollBarPrivate *priv; }; struct _StScrollBarClass { StWidgetClass parent_class; /* signals */ void (*scroll_start) (StScrollBar *bar); void (*scroll_stop) (StScrollBar *bar); }; GType st_scroll_bar_get_type (void) G_GNUC_CONST; StWidget *st_scroll_bar_new (StAdjustment *adjustment); void st_scroll_bar_set_adjustment (StScrollBar *bar, StAdjustment *adjustment); StAdjustment *st_scroll_bar_get_adjustment (StScrollBar *bar); G_END_DECLS #endif /* __ST_SCROLL_BAR_H__ */ Cinnamon-2.8.6/src/st/st-cogl-wrapper.h0000664000175000017500000000267512625666430016655 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-cogl-wrapper.h: compatibility wrappers for various cogl api calls * */ #ifndef __ST_COGL_WRAPPER_H__ #define __ST_COGL_WRAPPER_H__ #include G_BEGIN_DECLS CoglTexture * st_cogl_texture_new_from_data_wrapper (int width, int height, CoglTextureFlags flags, CoglPixelFormat format, CoglPixelFormat internal_format, int rowstride, const uint8_t *data); CoglTexture * st_cogl_texture_new_from_file_wrapper (const char *filename, CoglTextureFlags flags, CoglPixelFormat internal_format); CoglTexture * st_cogl_texture_new_with_size_wrapper (int width, int height, CoglTextureFlags flags, CoglPixelFormat internal_format); G_END_DECLS #endif /* __ST_COGL_WRAPPER_H__ */ Cinnamon-2.8.6/src/st/st-label.c0000664000175000017500000003527012625666430015322 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-label.c: Plain label actor * * Copyright 2008,2009 Intel Corporation * Copyright 2009 Red Hat, Inc. * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-label * @short_description: Widget for displaying text * * #StLabel is a simple widget for displaying text. It derives from * #StWidget to add extra style and placement functionality over * #ClutterText. The internal #ClutterText is publicly accessibly to allow * applications to set further properties. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "st-label.h" #include "st-private.h" #include "st-widget.h" #include enum { PROP_0, PROP_CLUTTER_TEXT, PROP_TEXT }; #define ST_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_LABEL, StLabelPrivate)) struct _StLabelPrivate { ClutterActor *label; gboolean orphan; CoglHandle text_shadow_material; float shadow_width; float shadow_height; }; G_DEFINE_TYPE (StLabel, st_label, ST_TYPE_WIDGET); static GType st_label_accessible_get_type (void) G_GNUC_CONST; static void st_label_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StLabel *label = ST_LABEL (gobject); switch (prop_id) { case PROP_TEXT: st_label_set_text (label, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_label_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StLabelPrivate *priv = ST_LABEL (gobject)->priv; switch (prop_id) { case PROP_CLUTTER_TEXT: g_value_set_object (value, priv->label); break; case PROP_TEXT: g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_label_style_changed (StWidget *self) { StLabelPrivate *priv = ST_LABEL(self)->priv; if (priv->text_shadow_material != COGL_INVALID_HANDLE) { cogl_handle_unref (priv->text_shadow_material); priv->text_shadow_material = COGL_INVALID_HANDLE; } _st_set_text_from_style ((ClutterText *)priv->label, st_widget_get_theme_node (self)); ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self); } static void st_label_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StLabelPrivate *priv = ST_LABEL (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_height (theme_node, &for_height); clutter_actor_get_preferred_width (priv->label, for_height, min_width_p, natural_width_p); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_label_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StLabelPrivate *priv = ST_LABEL (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_width (theme_node, &for_width); clutter_actor_get_preferred_height (priv->label, for_width, min_height_p, natural_height_p); st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_label_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StLabelPrivate *priv = ST_LABEL (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorClass *parent_class; ClutterActorBox content_box; st_theme_node_get_content_box (theme_node, box, &content_box); parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class); parent_class->allocate (actor, box, flags); clutter_actor_allocate (priv->label, &content_box, flags); } static void st_label_dispose (GObject *object) { StLabelPrivate *priv = ST_LABEL (object)->priv; if (priv->label) { clutter_actor_destroy (priv->label); priv->label = NULL; } if (priv->text_shadow_material != COGL_INVALID_HANDLE) { cogl_handle_unref (priv->text_shadow_material); priv->text_shadow_material = COGL_INVALID_HANDLE; } G_OBJECT_CLASS (st_label_parent_class)->dispose (object); } static void st_label_paint (ClutterActor *actor) { StLabelPrivate *priv = ST_LABEL (actor)->priv; ClutterActorClass *parent_class; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); StShadow *shadow_spec = st_theme_node_get_text_shadow (theme_node); parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class); parent_class->paint (actor); if (shadow_spec) { ClutterActorBox allocation; float width, height; clutter_actor_get_allocation_box (priv->label, &allocation); clutter_actor_box_get_size (&allocation, &width, &height); allocation.x1 = allocation.y1 = 0; allocation.x2 = width; allocation.y2 = height; if (priv->text_shadow_material == COGL_INVALID_HANDLE || width != priv->shadow_width || height != priv->shadow_height) { CoglHandle material; if (priv->text_shadow_material != COGL_INVALID_HANDLE) cogl_handle_unref (priv->text_shadow_material); material = _st_create_shadow_material_from_actor (shadow_spec, priv->label); priv->shadow_width = width; priv->shadow_height = height; priv->text_shadow_material = material; } if (priv->text_shadow_material != COGL_INVALID_HANDLE) _st_paint_shadow_with_opacity (shadow_spec, priv->text_shadow_material, &allocation, clutter_actor_get_paint_opacity (priv->label)); } clutter_actor_paint (priv->label); } static void st_label_class_init (StLabelClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StLabelPrivate)); gobject_class->set_property = st_label_set_property; gobject_class->get_property = st_label_get_property; gobject_class->dispose = st_label_dispose; actor_class->paint = st_label_paint; actor_class->allocate = st_label_allocate; actor_class->get_preferred_width = st_label_get_preferred_width; actor_class->get_preferred_height = st_label_get_preferred_height; widget_class->style_changed = st_label_style_changed; widget_class->get_accessible_type = st_label_accessible_get_type; pspec = g_param_spec_object ("clutter-text", "Clutter Text", "Internal ClutterText actor", CLUTTER_TYPE_TEXT, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec); pspec = g_param_spec_string ("text", "Text", "Text of the label", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); } static void st_label_init (StLabel *label) { StLabelPrivate *priv; label->priv = priv = ST_LABEL_GET_PRIVATE (label); label->priv->label = g_object_new (CLUTTER_TYPE_TEXT, "ellipsize", PANGO_ELLIPSIZE_END, NULL); label->priv->text_shadow_material = COGL_INVALID_HANDLE; label->priv->shadow_width = -1.; label->priv->shadow_height = -1.; label->priv->orphan = FALSE; clutter_actor_add_child (CLUTTER_ACTOR (label), priv->label); } /** * st_label_new: * @text: text to set the label to * * Create a new #StLabel with the specified label * * Returns: a new #StLabel */ StWidget * st_label_new (const gchar *text) { if (text == NULL || *text == '\0') return g_object_new (ST_TYPE_LABEL, NULL); else return g_object_new (ST_TYPE_LABEL, "text", text, NULL); } /** * st_label_get_text: * @label: a #StLabel * * Get the text displayed on the label * * Returns: the text for the label. This must not be freed by the application */ const gchar * st_label_get_text (StLabel *label) { StLabelPrivate *priv; ClutterText *ctext; g_return_val_if_fail (ST_IS_LABEL (label), NULL); priv = label->priv; ctext = CLUTTER_TEXT (priv->label); if (priv->orphan) return NULL; if (ctext == NULL) { g_printerr ("Cinnamon WARNING: Possible orphan label being accessed via st_label_get_text(). Check your timers and handlers!\n" "Address: %p\n", (void *) label); priv->orphan = TRUE; return NULL; } return clutter_text_get_text (ctext); } /** * st_label_set_text: * @label: a #StLabel * @text: text to set the label to * * Sets the text displayed on the label */ void st_label_set_text (StLabel *label, const gchar *text) { StLabelPrivate *priv; ClutterText *ctext; g_return_if_fail (ST_IS_LABEL (label)); g_return_if_fail (text != NULL); priv = label->priv; ctext = CLUTTER_TEXT (priv->label); if (priv->orphan) return; if (ctext == NULL) { g_printerr ("Cinnamon WARNING: Possible orphan label being accessed via st_label_set_text(). Check your timers and handlers!\n" "Address: %p Text: %s\n", (void *) label, text); priv->orphan = TRUE; return; } if (clutter_text_get_editable (ctext) || g_strcmp0 (clutter_text_get_text (ctext), text) != 0) { if (priv->text_shadow_material != COGL_INVALID_HANDLE) { cogl_handle_unref (priv->text_shadow_material); priv->text_shadow_material = COGL_INVALID_HANDLE; } clutter_text_set_text (ctext, text); g_object_notify (G_OBJECT (label), "text"); } } /** * st_label_get_clutter_text: * @label: a #StLabel * * Retrieve the internal #ClutterText so that extra parameters can be set * * Returns: (transfer none): ethe #ClutterText used by #StLabel. The label * is owned by the #StLabel and should not be unref'ed by the application. */ ClutterActor* st_label_get_clutter_text (StLabel *label) { g_return_val_if_fail (ST_LABEL (label), NULL); return label->priv->label; } /******************************************************************************/ /*************************** ACCESSIBILITY SUPPORT ****************************/ /******************************************************************************/ #define ST_TYPE_LABEL_ACCESSIBLE st_label_accessible_get_type () #define ST_LABEL_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessible)) #define ST_IS_LABEL_ACCESSIBLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ ST_TYPE_LABEL_ACCESSIBLE)) #define ST_LABEL_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass)) #define ST_IS_LABEL_ACCESSIBLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ ST_TYPE_LABEL_ACCESSIBLE)) #define ST_LABEL_ACCESSIBLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass)) typedef struct _StLabelAccessible StLabelAccessible; typedef struct _StLabelAccessibleClass StLabelAccessibleClass; struct _StLabelAccessible { StWidgetAccessible parent; }; struct _StLabelAccessibleClass { StWidgetAccessibleClass parent_class; }; static void st_label_accessible_class_init (StLabelAccessibleClass *klass); static void st_label_accessible_init (StLabelAccessible *label); /* AtkObject */ static void st_label_accessible_initialize (AtkObject *obj, gpointer data); static const gchar * st_label_accessible_get_name (AtkObject *obj); G_DEFINE_TYPE (StLabelAccessible, st_label_accessible, ST_TYPE_WIDGET_ACCESSIBLE) static void st_label_accessible_class_init (StLabelAccessibleClass *klass) { AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); atk_class->initialize = st_label_accessible_initialize; atk_class->get_name = st_label_accessible_get_name; } static void st_label_accessible_init (StLabelAccessible *self) { /* initialization done on AtkObject->initialize */ } static void label_text_notify_cb (StLabel *label, GParamSpec *pspec, AtkObject *accessible) { g_object_notify (G_OBJECT (accessible), "accessible-name"); } static void st_label_accessible_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data); g_signal_connect (data, "notify::text", G_CALLBACK (label_text_notify_cb), obj); obj->role = ATK_ROLE_LABEL; } static const gchar * st_label_accessible_get_name (AtkObject *obj) { const gchar *name = NULL; g_return_val_if_fail (ST_IS_LABEL_ACCESSIBLE (obj), NULL); name = ATK_OBJECT_CLASS (st_label_accessible_parent_class)->get_name (obj); if (name == NULL) { ClutterActor *actor = NULL; actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (actor == NULL) /* State is defunct */ name = NULL; else name = st_label_get_text (ST_LABEL (actor)); } return name; } Cinnamon-2.8.6/src/st/st-widget.c0000664000175000017500000021743212625666430015530 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-widget.c: Base class for St actors * * Copyright 2007 OpenedHand * Copyright 2008, 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Abderrahim Kitouni * Copyright 2009, 2010 Florian Müllner * Copyright 2010 Adel Gadllah * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "st-widget.h" #include "st-background-effect.h" #include "st-label.h" #include "st-marshal.h" #include "st-private.h" #include "st-texture-cache.h" #include "st-theme-context.h" #include "st-theme-node-transition.h" #include "st-widget-accessible.h" #include #include /* * Forward declaration for sake of StWidgetChild */ struct _StWidgetPrivate { StTheme *theme; StThemeNode *theme_node; gchar *pseudo_class; gchar *style_class; gchar *inline_style; StThemeNodeTransition *transition_animation; gboolean is_stylable : 1; gboolean is_style_dirty : 1; gboolean draw_bg_color : 1; gboolean draw_border_internal : 1; gboolean track_hover : 1; gboolean hover : 1; gboolean can_focus : 1; gboolean important : 1; StTextDirection direction; AtkObject *accessible; AtkRole accessible_role; AtkStateSet *local_state_set; ClutterActor *label_actor; gchar *accessible_name; }; /** * SECTION:st-widget * @short_description: Base class for stylable actors * * #StWidget is a simple abstract class on top of #ClutterActor. It * provides basic themeing properties. * * Actors in the St library should subclass #StWidget if they plan * to obey to a certain #StStyle. */ enum { PROP_0, PROP_THEME, PROP_PSEUDO_CLASS, PROP_STYLE_CLASS, PROP_STYLE, PROP_STYLABLE, PROP_TRACK_HOVER, PROP_HOVER, PROP_CAN_FOCUS, PROP_LABEL_ACTOR, PROP_IMPORTANT, PROP_ACCESSIBLE_ROLE, PROP_ACCESSIBLE_NAME }; enum { STYLE_CHANGED, POPUP_MENU, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; gfloat st_slow_down_factor = 1.0; G_DEFINE_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR); #define ST_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET, StWidgetPrivate)) static void st_widget_recompute_style (StWidget *widget, StThemeNode *old_theme_node); static gboolean st_widget_real_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction); static AtkObject * st_widget_get_accessible (ClutterActor *actor); static void st_widget_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StWidget *actor = ST_WIDGET (gobject); switch (prop_id) { case PROP_THEME: st_widget_set_theme (actor, g_value_get_object (value)); break; case PROP_PSEUDO_CLASS: st_widget_set_style_pseudo_class (actor, g_value_get_string (value)); break; case PROP_STYLE_CLASS: st_widget_set_style_class_name (actor, g_value_get_string (value)); break; case PROP_STYLE: st_widget_set_style (actor, g_value_get_string (value)); break; case PROP_STYLABLE: if (actor->priv->is_stylable != g_value_get_boolean (value)) { actor->priv->is_stylable = g_value_get_boolean (value); clutter_actor_queue_relayout ((ClutterActor *) gobject); } break; case PROP_TRACK_HOVER: st_widget_set_track_hover (actor, g_value_get_boolean (value)); break; case PROP_HOVER: st_widget_set_hover (actor, g_value_get_boolean (value)); break; case PROP_CAN_FOCUS: st_widget_set_can_focus (actor, g_value_get_boolean (value)); break; case PROP_LABEL_ACTOR: st_widget_set_label_actor (actor, g_value_get_object (value)); break; case PROP_IMPORTANT: st_widget_set_important (actor, g_value_get_boolean (value)); break; case PROP_ACCESSIBLE_ROLE: st_widget_set_accessible_role (actor, g_value_get_enum (value)); break; case PROP_ACCESSIBLE_NAME: st_widget_set_accessible_name (actor, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_widget_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StWidget *actor = ST_WIDGET (gobject); StWidgetPrivate *priv = actor->priv; switch (prop_id) { case PROP_THEME: g_value_set_object (value, priv->theme); break; case PROP_PSEUDO_CLASS: g_value_set_string (value, priv->pseudo_class); break; case PROP_STYLE_CLASS: g_value_set_string (value, priv->style_class); break; case PROP_STYLE: g_value_set_string (value, priv->inline_style); break; case PROP_STYLABLE: g_value_set_boolean (value, priv->is_stylable); break; case PROP_TRACK_HOVER: g_value_set_boolean (value, priv->track_hover); break; case PROP_HOVER: g_value_set_boolean (value, priv->hover); break; case PROP_CAN_FOCUS: g_value_set_boolean (value, priv->can_focus); break; case PROP_LABEL_ACTOR: g_value_set_object (value, priv->label_actor); break; case PROP_IMPORTANT: g_value_set_boolean (value, priv->important); break; case PROP_ACCESSIBLE_ROLE: g_value_set_enum (value, st_widget_get_accessible_role (actor)); break; case PROP_ACCESSIBLE_NAME: g_value_set_string (value, priv->accessible_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_widget_remove_transition (StWidget *widget) { if (widget->priv->transition_animation) { g_object_run_dispose (G_OBJECT (widget->priv->transition_animation)); g_object_unref (widget->priv->transition_animation); widget->priv->transition_animation = NULL; } } static void st_widget_dispose (GObject *gobject) { StWidget *actor = ST_WIDGET (gobject); StWidgetPrivate *priv = ST_WIDGET (actor)->priv; if (priv->theme) { g_object_unref (priv->theme); priv->theme = NULL; } if (priv->theme_node) { g_object_unref (priv->theme_node); priv->theme_node = NULL; } st_widget_remove_transition (actor); /* The real dispose of this accessible is done on * AtkGObjectAccessible weak ref callback */ if (priv->accessible) priv->accessible = NULL; if (priv->label_actor) { g_object_unref (priv->label_actor); priv->label_actor = NULL; } G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject); } static void st_widget_finalize (GObject *gobject) { StWidgetPrivate *priv = ST_WIDGET (gobject)->priv; g_free (priv->style_class); g_free (priv->pseudo_class); g_object_unref (priv->local_state_set); g_free (priv->accessible_name); G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject); } static void st_widget_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); /* Most subclasses will override this and not chain down. However, * if they do not, then we need to override ClutterActor's default * behavior (request 0x0) to take CSS-specified minimums into * account (which st_theme_node_adjust_preferred_width() will do.) */ if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_widget_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); /* See st_widget_get_preferred_width() */ if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_widget_paint (ClutterActor *actor) { StWidget *self = ST_WIDGET (actor); StThemeNode *theme_node; ClutterActorBox allocation; guint8 opacity; theme_node = st_widget_get_theme_node (self); clutter_actor_get_allocation_box (actor, &allocation); opacity = clutter_actor_get_paint_opacity (actor); if (self->priv->transition_animation) st_theme_node_transition_paint (self->priv->transition_animation, &allocation, opacity); else st_theme_node_paint (theme_node, &allocation, opacity); ClutterEffect *effect = clutter_actor_get_effect (actor, "background-effect"); if (effect == NULL) { effect = st_background_effect_new (); clutter_actor_add_effect_with_name (actor, "background-effect", effect); } const char *bumpmap_path = st_theme_node_get_background_bumpmap(theme_node); g_object_set (effect, "bumpmap", bumpmap_path, NULL); } static void st_widget_parent_set (ClutterActor *widget, ClutterActor *old_parent) { ClutterActorClass *parent_class; ClutterActor *new_parent; parent_class = CLUTTER_ACTOR_CLASS (st_widget_parent_class); if (parent_class->parent_set) parent_class->parent_set (widget, old_parent); new_parent = clutter_actor_get_parent (widget); /* don't send the style changed signal if we no longer have a parent actor */ if (new_parent) st_widget_style_changed (ST_WIDGET (widget)); } static void st_widget_map (ClutterActor *actor) { StWidget *self = ST_WIDGET (actor); CLUTTER_ACTOR_CLASS (st_widget_parent_class)->map (actor); st_widget_ensure_style (self); } static void st_widget_unmap (ClutterActor *actor) { StWidget *self = ST_WIDGET (actor); StWidgetPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor); if (priv->track_hover && priv->hover) st_widget_set_hover (self, FALSE); } static void notify_children_of_style_change (ClutterContainer *container); static void notify_children_of_style_change_foreach (ClutterActor *actor, gpointer user_data) { if (ST_IS_WIDGET (actor)) st_widget_style_changed (ST_WIDGET (actor)); else if (CLUTTER_IS_CONTAINER (actor)) notify_children_of_style_change ((ClutterContainer *)actor); } static void notify_children_of_style_change (ClutterContainer *container) { /* notify our children that their parent stylable has changed */ clutter_container_foreach (container, notify_children_of_style_change_foreach, NULL); } static void st_widget_real_style_changed (StWidget *self) { StWidgetPrivate *priv = ST_WIDGET (self)->priv; /* application has request this widget is not stylable */ if (!priv->is_stylable) return; clutter_actor_queue_redraw ((ClutterActor *) self); if (CLUTTER_IS_CONTAINER (self)) notify_children_of_style_change ((ClutterContainer *)self); } void st_widget_style_changed (StWidget *widget) { StThemeNode *old_theme_node = NULL; widget->priv->is_style_dirty = TRUE; if (widget->priv->theme_node) { old_theme_node = widget->priv->theme_node; widget->priv->theme_node = NULL; } /* update the style only if we are mapped */ if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (widget))) st_widget_recompute_style (widget, old_theme_node); if (old_theme_node) g_object_unref (old_theme_node); } static void on_theme_context_changed (StThemeContext *context, ClutterStage *stage) { notify_children_of_style_change (CLUTTER_CONTAINER (stage)); } static StThemeNode * get_root_theme_node (ClutterStage *stage) { StThemeContext *context = st_theme_context_get_for_stage (stage); if (!g_object_get_data (G_OBJECT (context), "st-theme-initialized")) { g_object_set_data (G_OBJECT (context), "st-theme-initialized", GUINT_TO_POINTER (1)); g_signal_connect (G_OBJECT (context), "changed", G_CALLBACK (on_theme_context_changed), stage); } return st_theme_context_get_root_node (context); } /** * st_widget_get_theme_node: * @widget: a #StWidget * * Gets the theme node holding style information for the widget. * The theme node is used to access standard and custom CSS * properties of the widget. * * Note: this should only be called on a widget that has been * added to the stage * * Return value: (transfer none): the theme node for the widget. * This is owned by the widget. When attributes of the widget * or the environment that affect the styling change (for example * the style_class property of the widget), it will be recreated, * and the ::style-changed signal will be emitted on the widget. */ StThemeNode * st_widget_get_theme_node (StWidget *widget) { StWidgetPrivate *priv = widget->priv; if (priv->theme_node == NULL) { StThemeNode *parent_node = NULL; ClutterStage *stage = NULL; ClutterActor *parent; StThemeContext *context; StThemeNode *tmp_node; char *pseudo_class, *direction_pseudo_class; parent = clutter_actor_get_parent (CLUTTER_ACTOR (widget)); while (parent != NULL) { if (parent_node == NULL && ST_IS_WIDGET (parent)) parent_node = st_widget_get_theme_node (ST_WIDGET (parent)); else if (CLUTTER_IS_STAGE (parent)) stage = CLUTTER_STAGE (parent); parent = clutter_actor_get_parent (parent); } if (stage == NULL) { g_critical ("st_widget_get_theme_node called on the widget %s which is not in the stage.", st_describe_actor (CLUTTER_ACTOR (widget))); return g_object_new (ST_TYPE_THEME_NODE, NULL); } if (parent_node == NULL) parent_node = get_root_theme_node (CLUTTER_STAGE (stage)); /* Always append a "magic" pseudo class indicating the text * direction, to allow to adapt the CSS when necessary without * requiring separate style sheets. */ if (st_widget_get_direction (widget) == ST_TEXT_DIRECTION_RTL) direction_pseudo_class = "rtl"; else direction_pseudo_class = "ltr"; if (priv->pseudo_class) pseudo_class = g_strconcat(priv->pseudo_class, " ", direction_pseudo_class, NULL); else pseudo_class = direction_pseudo_class; context = st_theme_context_get_for_stage (stage); tmp_node = st_theme_node_new (context, parent_node, priv->theme, G_OBJECT_TYPE (widget), clutter_actor_get_name (CLUTTER_ACTOR (widget)), priv->style_class, pseudo_class, priv->inline_style, priv->important); if (pseudo_class != direction_pseudo_class) g_free (pseudo_class); priv->theme_node = g_object_ref (st_theme_context_intern_node (context, tmp_node)); g_object_unref (tmp_node); } return priv->theme_node; } /** * st_widget_peek_theme_node: * @widget: a #StWidget * * Returns the theme node for the widget if it has already been * computed, %NULL if the widget hasn't been added to a stage or the theme * node hasn't been computed. If %NULL is returned, then ::style-changed * will be reliably emitted before the widget is allocated or painted. * * Return value: (transfer none): the theme node for the widget. * This is owned by the widget. When attributes of the widget * or the environment that affect the styling change (for example * the style_class property of the widget), it will be recreated, * and the ::style-changed signal will be emitted on the widget. */ StThemeNode * st_widget_peek_theme_node (StWidget *widget) { StWidgetPrivate *priv = widget->priv; return priv->theme_node; } static gboolean st_widget_enter (ClutterActor *actor, ClutterCrossingEvent *event) { StWidgetPrivate *priv = ST_WIDGET (actor)->priv; if (priv->track_hover) { if (clutter_actor_contains (actor, event->source)) st_widget_set_hover (ST_WIDGET (actor), TRUE); else { /* The widget has a grab and is being told about an * enter-event outside its hierarchy. Hopefully we already * got a leave-event, but if not, handle it now. */ st_widget_set_hover (ST_WIDGET (actor), FALSE); } } if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event) return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event); else return FALSE; } static gboolean st_widget_leave (ClutterActor *actor, ClutterCrossingEvent *event) { StWidgetPrivate *priv = ST_WIDGET (actor)->priv; if (priv->track_hover) { if (!event->related || !clutter_actor_contains (actor, event->related)) st_widget_set_hover (ST_WIDGET (actor), FALSE); } if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event) return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event); else return FALSE; } static void st_widget_key_focus_in (ClutterActor *actor) { StWidget *widget = ST_WIDGET (actor); st_widget_add_style_pseudo_class (widget, "focus"); } static void st_widget_key_focus_out (ClutterActor *actor) { StWidget *widget = ST_WIDGET (actor); st_widget_remove_style_pseudo_class (widget, "focus"); } static gboolean st_widget_key_press_event (ClutterActor *actor, ClutterKeyEvent *event) { if (event->keyval == CLUTTER_KEY_Menu || (event->keyval == CLUTTER_KEY_F10 && (event->modifier_state & CLUTTER_SHIFT_MASK))) { g_signal_emit (actor, signals[POPUP_MENU], 0); return TRUE; } return FALSE; } static gboolean st_widget_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterActorBox paint_box, alloc_box; StThemeNode *theme_node; StWidgetPrivate *priv; ClutterVertex origin; /* Setting the paint volume does not make sense when we don't have any allocation */ if (!clutter_actor_has_allocation (self)) return FALSE; priv = ST_WIDGET(self)->priv; theme_node = st_widget_get_theme_node (ST_WIDGET(self)); clutter_actor_get_allocation_box (self, &alloc_box); if (priv->transition_animation) st_theme_node_transition_get_paint_box (priv->transition_animation, &alloc_box, &paint_box); else st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box); origin.x = paint_box.x1 - alloc_box.x1; origin.y = paint_box.y1 - alloc_box.y1; origin.z = 0.0f; clutter_paint_volume_set_origin (volume, &origin); clutter_paint_volume_set_width (volume, paint_box.x2 - paint_box.x1); clutter_paint_volume_set_height (volume, paint_box.y2 - paint_box.y1); return TRUE; } static void st_widget_class_init (StWidgetClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StWidgetPrivate)); gobject_class->set_property = st_widget_set_property; gobject_class->get_property = st_widget_get_property; gobject_class->dispose = st_widget_dispose; gobject_class->finalize = st_widget_finalize; actor_class->get_preferred_width = st_widget_get_preferred_width; actor_class->get_preferred_height = st_widget_get_preferred_height; actor_class->paint = st_widget_paint; actor_class->get_paint_volume = st_widget_get_paint_volume; actor_class->parent_set = st_widget_parent_set; actor_class->map = st_widget_map; actor_class->unmap = st_widget_unmap; actor_class->enter_event = st_widget_enter; actor_class->leave_event = st_widget_leave; actor_class->key_focus_in = st_widget_key_focus_in; actor_class->key_focus_out = st_widget_key_focus_out; actor_class->key_press_event = st_widget_key_press_event; actor_class->get_accessible = st_widget_get_accessible; klass->style_changed = st_widget_real_style_changed; klass->navigate_focus = st_widget_real_navigate_focus; klass->get_accessible_type = st_widget_accessible_get_type; /** * StWidget:pseudo-class: * * The pseudo-class of the actor. Typical values include "hover", "active", * "focus". */ g_object_class_install_property (gobject_class, PROP_PSEUDO_CLASS, g_param_spec_string ("pseudo-class", "Pseudo Class", "Pseudo class for styling", "", ST_PARAM_READWRITE)); /** * StWidget:style-class: * * The style-class of the actor for use in styling. */ g_object_class_install_property (gobject_class, PROP_STYLE_CLASS, g_param_spec_string ("style-class", "Style Class", "Style class for styling", "", ST_PARAM_READWRITE)); /** * StWidget:style: * * Inline style information for the actor as a ';'-separated list of * CSS properties. */ g_object_class_install_property (gobject_class, PROP_STYLE, g_param_spec_string ("style", "Style", "Inline style string", "", ST_PARAM_READWRITE)); /** * StWidget:theme: * * A theme set on this actor overriding the global theming for this actor * and its descendants */ g_object_class_install_property (gobject_class, PROP_THEME, g_param_spec_object ("theme", "Theme", "Theme override", ST_TYPE_THEME, ST_PARAM_READWRITE)); /** * StWidget:stylable: * * Enable or disable styling of the widget */ pspec = g_param_spec_boolean ("stylable", "Stylable", "Whether the table should be styled", TRUE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_STYLABLE, pspec); /** * StWidget:track-hover: * * Determines whether the widget tracks pointer hover state. If * %TRUE (and the widget is visible and reactive), the * #StWidget:hover property and "hover" style pseudo class will be * adjusted automatically as the pointer moves in and out of the * widget. */ pspec = g_param_spec_boolean ("track-hover", "Track hover", "Determines whether the widget tracks hover state", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TRACK_HOVER, pspec); /** * StWidget:hover: * * Whether or not the pointer is currently hovering over the widget. This is * only tracked automatically if #StWidget:track-hover is %TRUE, but you can * adjust it manually in any case. */ pspec = g_param_spec_boolean ("hover", "Hover", "Whether the pointer is hovering over the widget", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_HOVER, pspec); /** * StWidget:can-focus: * * Whether or not the widget can be focused via keyboard navigation. */ pspec = g_param_spec_boolean ("can-focus", "Can focus", "Whether the widget can be focused via keyboard navigation", FALSE, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CAN_FOCUS, pspec); /** * StWidget:important: * * Whether or not the fallback theme should be used for lookups in case the user theme fails. */ pspec = g_param_spec_boolean ("important", "Important", "Whether the widget styling should be looked up in the fallback theme", FALSE, ST_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (gobject_class, PROP_IMPORTANT, pspec); /** * ClutterActor:label-actor: * * An actor that labels this widget. */ g_object_class_install_property (gobject_class, PROP_LABEL_ACTOR, g_param_spec_object ("label-actor", "Label", "Label that identifies this widget", CLUTTER_TYPE_ACTOR, ST_PARAM_READWRITE)); /** * StWidget:accessible-role: * * The accessible role of this object */ g_object_class_install_property (gobject_class, PROP_ACCESSIBLE_ROLE, g_param_spec_enum ("accessible-role", "Accessible Role", "The accessible role of this object", ATK_TYPE_ROLE, ATK_ROLE_INVALID, G_PARAM_READWRITE)); /** * StWidget:accessible-name: * * Object instance's name for assistive technology access. */ g_object_class_install_property (gobject_class, PROP_ACCESSIBLE_NAME, g_param_spec_string ("accessible-name", "Accessible name", "Object instance's name for assistive technology access.", NULL, ST_PARAM_READWRITE)); /** * StWidget::style-changed: * @widget: the #StWidget * * Emitted when the style information that the widget derives from the * theme changes */ signals[STYLE_CHANGED] = g_signal_new ("style-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StWidgetClass, style_changed), NULL, NULL, _st_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * StWidget::popup-menu: * @widget: the #StWidget * * Emitted when the user has requested a context menu (eg, via a * keybinding) */ signals[POPUP_MENU] = g_signal_new ("popup-menu", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StWidgetClass, popup_menu), NULL, NULL, _st_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * st_widget_set_theme: * @actor: a #StWidget * @theme: a new style class string * * Overrides the theme that would be inherited from the actor's parent * or the stage with an entirely new theme (set of stylesheets). */ void st_widget_set_theme (StWidget *actor, StTheme *theme) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (actor)); priv = actor->priv; if (theme != priv->theme) { if (priv->theme) g_object_unref (priv->theme); priv->theme = g_object_ref (theme); st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "theme"); } } /** * st_widget_get_theme: * @actor: a #StWidget * * Gets the overriding theme set on the actor. See st_widget_set_theme() * * Return value: (transfer none): the overriding theme, or %NULL */ StTheme * st_widget_get_theme (StWidget *actor) { g_return_val_if_fail (ST_IS_WIDGET (actor), NULL); return actor->priv->theme; } static const gchar * find_class_name (const gchar *class_list, const gchar *class_name) { gint len = strlen (class_name); const gchar *match; if (!class_list) return NULL; for (match = strstr (class_list, class_name); match; match = strstr (match + 1, class_name)) { if ((match == class_list || g_ascii_isspace (match[-1])) && (match[len] == '\0' || g_ascii_isspace (match[len]))) return match; } return NULL; } static gboolean set_class_list (gchar **class_list, const gchar *new_class_list) { if (g_strcmp0 (*class_list, new_class_list) != 0) { g_free (*class_list); *class_list = g_strdup (new_class_list); return TRUE; } else return FALSE; } static gboolean add_class_name (gchar **class_list, const gchar *class_name) { gchar *new_class_list; if (*class_list) { if (find_class_name (*class_list, class_name)) return FALSE; new_class_list = g_strdup_printf ("%s %s", *class_list, class_name); g_free (*class_list); *class_list = new_class_list; } else *class_list = g_strdup (class_name); return TRUE; } static gboolean remove_class_name (gchar **class_list, const gchar *class_name) { const gchar *match, *end; gchar *new_class_list; if (!*class_list) return FALSE; if (strcmp (*class_list, class_name) == 0) { g_free (*class_list); *class_list = NULL; return TRUE; } match = find_class_name (*class_list, class_name); if (!match) return FALSE; end = match + strlen (class_name); /* Adjust either match or end to include a space as well. * (One or the other must be possible at this point.) */ if (match != *class_list) match--; else end++; new_class_list = g_strdup_printf ("%.*s%s", (int)(match - *class_list), *class_list, end); g_free (*class_list); *class_list = new_class_list; return TRUE; } /** * st_widget_set_style_class_name: * @actor: a #StWidget * @style_class_list: (allow-none): a new style class list string * * Set the style class name list. @style_class_list can either be * %NULL, for no classes, or a space-separated list of style class * names. See also st_widget_add_style_class_name() and * st_widget_remove_style_class_name(). */ void st_widget_set_style_class_name (StWidget *actor, const gchar *style_class_list) { g_return_if_fail (ST_IS_WIDGET (actor)); if (set_class_list (&actor->priv->style_class, style_class_list)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "style-class"); } } /** * st_widget_add_style_class_name: * @actor: a #StWidget * @style_class: a style class name string * * Adds @style_class to @actor's style class name list, if it is not * already present. */ void st_widget_add_style_class_name (StWidget *actor, const gchar *style_class) { g_return_if_fail (ST_IS_WIDGET (actor)); g_return_if_fail (style_class != NULL); if (add_class_name (&actor->priv->style_class, style_class)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "style-class"); } } /** * st_widget_remove_style_class_name: * @actor: a #StWidget * @style_class: a style class name string * * Removes @style_class from @actor's style class name, if it is * present. */ void st_widget_remove_style_class_name (StWidget *actor, const gchar *style_class) { g_return_if_fail (ST_IS_WIDGET (actor)); g_return_if_fail (style_class != NULL); if (remove_class_name (&actor->priv->style_class, style_class)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "style-class"); } } /** * st_widget_get_style_class_name: * @actor: a #StWidget * * Get the current style class name * * Returns: the class name string. The string is owned by the #StWidget and * should not be modified or freed. */ const gchar* st_widget_get_style_class_name (StWidget *actor) { g_return_val_if_fail (ST_IS_WIDGET (actor), NULL); return actor->priv->style_class; } /** * st_widget_has_style_class_name: * @actor: a #StWidget * @style_class: a style class string * * Tests if @actor's style class list includes @style_class. * * Returns: whether or not @actor's style class list includes * @style_class. */ gboolean st_widget_has_style_class_name (StWidget *actor, const gchar *style_class) { g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE); return find_class_name (actor->priv->style_class, style_class) != NULL; } /** * st_widget_get_style_pseudo_class: * @actor: a #StWidget * * Get the current style pseudo class list. * * Note that an actor can have multiple pseudo classes; if you just * want to test for the presence of a specific pseudo class, use * st_widget_has_style_pseudo_class(). * * Returns: the pseudo class list string. The string is owned by the * #StWidget and should not be modified or freed. */ const gchar* st_widget_get_style_pseudo_class (StWidget *actor) { g_return_val_if_fail (ST_IS_WIDGET (actor), NULL); return actor->priv->pseudo_class; } /** * st_widget_has_style_pseudo_class: * @actor: a #StWidget * @pseudo_class: a pseudo class string * * Tests if @actor's pseudo class list includes @pseudo_class. * * Returns: whether or not @actor's pseudo class list includes * @pseudo_class. */ gboolean st_widget_has_style_pseudo_class (StWidget *actor, const gchar *pseudo_class) { g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE); return find_class_name (actor->priv->pseudo_class, pseudo_class) != NULL; } /** * st_widget_set_style_pseudo_class: * @actor: a #StWidget * @pseudo_class_list: (allow-none): a new pseudo class list string * * Set the style pseudo class list. @pseudo_class_list can either be * %NULL, for no classes, or a space-separated list of pseudo class * names. See also st_widget_add_style_pseudo_class() and * st_widget_remove_style_pseudo_class(). */ void st_widget_set_style_pseudo_class (StWidget *actor, const gchar *pseudo_class_list) { g_return_if_fail (ST_IS_WIDGET (actor)); if (set_class_list (&actor->priv->pseudo_class, pseudo_class_list)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "pseudo-class"); } } /** * st_widget_change_style_pseudo_class: * @actor: a #StWidget * @pseudo_class: a pseudo class string * @add: whether to add or remove pseudo class * * Adds @pseudo_class to @actor's pseudo class list if @add is true, * removes if @add is false. */ void st_widget_change_style_pseudo_class (StWidget *actor, const gchar *pseudo_class, gboolean add) { g_return_if_fail (ST_IS_WIDGET (actor)); g_return_if_fail (pseudo_class != NULL); if (add) st_widget_add_style_pseudo_class(actor, pseudo_class); else st_widget_remove_style_pseudo_class(actor, pseudo_class); } /** * st_widget_add_style_pseudo_class: * @actor: a #StWidget * @pseudo_class: a pseudo class string * * Adds @pseudo_class to @actor's pseudo class list, if it is not * already present. */ void st_widget_add_style_pseudo_class (StWidget *actor, const gchar *pseudo_class) { g_return_if_fail (ST_IS_WIDGET (actor)); g_return_if_fail (pseudo_class != NULL); if (add_class_name (&actor->priv->pseudo_class, pseudo_class)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "pseudo-class"); } } /** * st_widget_remove_style_pseudo_class: * @actor: a #StWidget * @pseudo_class: a pseudo class string * * Removes @pseudo_class from @actor's pseudo class, if it is present. */ void st_widget_remove_style_pseudo_class (StWidget *actor, const gchar *pseudo_class) { g_return_if_fail (ST_IS_WIDGET (actor)); g_return_if_fail (pseudo_class != NULL); if (remove_class_name (&actor->priv->pseudo_class, pseudo_class)) { st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "pseudo-class"); } } /** * st_widget_set_style: * @actor: a #StWidget * @style: (allow-none): a inline style string, or %NULL * * Set the inline style string for this widget. The inline style string is an * optional ';'-separated list of CSS properties that override the style as * determined from the stylesheets of the current theme. */ void st_widget_set_style (StWidget *actor, const gchar *style) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (actor)); priv = actor->priv; if (g_strcmp0 (style, priv->inline_style)) { g_free (priv->inline_style); priv->inline_style = g_strdup (style); st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "style"); } } /** * st_widget_get_style: * @actor: a #StWidget * * Get the current inline style string. See st_widget_set_style(). * * Returns: The inline style string, or %NULL. The string is owned by the * #StWidget and should not be modified or freed. */ const gchar* st_widget_get_style (StWidget *actor) { g_return_val_if_fail (ST_IS_WIDGET (actor), NULL); return actor->priv->inline_style; } /** * st_widget_set_important: * @actor: a #StWidget * @important: whether the actor is to be considered important. * * When an actor is set to important, and the active theme does not * account for it, a fallback lookup is made to the default cinnamon theme * which (presumably) will always have support for all stock elements * of the desktop. * * This property is inherited by the actor's children. */ void st_widget_set_important (StWidget *actor, gboolean important) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (actor)); priv = actor->priv; if (important != priv->important) { priv->important = important; st_widget_style_changed (actor); g_object_notify (G_OBJECT (actor), "important"); } } /** * st_widget_get_important: * @actor: a #StWidget * * Returns if the @actor is flagged set as important */ gboolean st_widget_get_important (StWidget *actor) { g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE); return actor->priv->important; } static void st_widget_name_notify (StWidget *widget, GParamSpec *pspec, gpointer data) { st_widget_style_changed (widget); } static void st_widget_init (StWidget *actor) { StWidgetPrivate *priv; actor->priv = priv = ST_WIDGET_GET_PRIVATE (actor); priv->is_stylable = TRUE; priv->transition_animation = NULL; priv->local_state_set = atk_state_set_new (); /* connect style changed */ g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL); } static void on_transition_completed (StThemeNodeTransition *transition, StWidget *widget) { st_widget_remove_transition (widget); } static void st_widget_recompute_style (StWidget *widget, StThemeNode *old_theme_node) { StThemeNode *new_theme_node = st_widget_get_theme_node (widget); int transition_duration; gboolean paint_equal; if (new_theme_node == old_theme_node) { widget->priv->is_style_dirty = FALSE; return; } if (!old_theme_node || !st_theme_node_geometry_equal (old_theme_node, new_theme_node)) clutter_actor_queue_relayout ((ClutterActor *) widget); transition_duration = st_theme_node_get_transition_duration (new_theme_node); paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node); if (paint_equal) st_theme_node_copy_cached_paint_state (new_theme_node, old_theme_node); if (transition_duration > 0) { if (widget->priv->transition_animation != NULL) { st_theme_node_transition_update (widget->priv->transition_animation, new_theme_node); } else if (old_theme_node && !paint_equal) { /* Since our transitions are only of the painting done by StThemeNode, we * only want to start a transition when what is painted changes; if * other visual aspects like the foreground color of a label change, * we can't animate that anyways. */ widget->priv->transition_animation = st_theme_node_transition_new (old_theme_node, new_theme_node, transition_duration); g_signal_connect (widget->priv->transition_animation, "completed", G_CALLBACK (on_transition_completed), widget); g_signal_connect_swapped (widget->priv->transition_animation, "new-frame", G_CALLBACK (clutter_actor_queue_redraw), widget); } } else if (widget->priv->transition_animation) { st_widget_remove_transition (widget); } g_signal_emit (widget, signals[STYLE_CHANGED], 0); widget->priv->is_style_dirty = FALSE; } /** * st_widget_ensure_style: * @widget: A #StWidget * * Ensures that @widget has read its style information. * */ void st_widget_ensure_style (StWidget *widget) { g_return_if_fail (ST_IS_WIDGET (widget)); if (widget->priv->is_style_dirty) st_widget_recompute_style (widget, NULL); } static StTextDirection default_direction = ST_TEXT_DIRECTION_LTR; StTextDirection st_widget_get_default_direction (void) { return default_direction; } void st_widget_set_default_direction (StTextDirection dir) { g_return_if_fail (dir != ST_TEXT_DIRECTION_NONE); default_direction = dir; } StTextDirection st_widget_get_direction (StWidget *self) { g_return_val_if_fail (ST_IS_WIDGET (self), ST_TEXT_DIRECTION_LTR); if (self->priv->direction != ST_TEXT_DIRECTION_NONE) return self->priv->direction; else return default_direction; } void st_widget_set_direction (StWidget *self, StTextDirection dir) { StTextDirection old_direction; g_return_if_fail (ST_IS_WIDGET (self)); old_direction = st_widget_get_direction (self); self->priv->direction = dir; if (old_direction != st_widget_get_direction (self)) st_widget_style_changed (self); } /** * st_widget_set_track_hover: * @widget: A #StWidget * @track_hover: %TRUE if the widget should track the pointer hover state * * Enables hover tracking on the #StWidget. * * If hover tracking is enabled, and the widget is visible and * reactive, then @widget's #StWidget:hover property will be updated * automatically to reflect whether the pointer is in @widget (or one * of its children), and @widget's #StWidget:pseudo-class will have * the "hover" class added and removed from it accordingly. * * Note that currently it is not possible to correctly track the hover * state when another actor has a pointer grab. You can use * st_widget_sync_hover() to update the property manually in this * case. */ void st_widget_set_track_hover (StWidget *widget, gboolean track_hover) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (widget)); priv = widget->priv; if (priv->track_hover != track_hover) { priv->track_hover = track_hover; g_object_notify (G_OBJECT (widget), "track-hover"); if (priv->track_hover) st_widget_sync_hover (widget); } } /** * st_widget_get_track_hover: * @widget: A #StWidget * * Returns the current value of the track-hover property. See * st_widget_set_track_hover() for more information. * * Returns: current value of track-hover on @widget */ gboolean st_widget_get_track_hover (StWidget *widget) { g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE); return widget->priv->track_hover; } /** * st_widget_set_hover: * @widget: A #StWidget * @hover: whether the pointer is hovering over the widget * * Sets @widget's hover property and adds or removes "hover" from its * pseudo class accordingly * * If you have set #StWidget:track-hover, you should not need to call * this directly. You can call st_widget_sync_hover() if the hover * state might be out of sync due to another actor's pointer grab. */ void st_widget_set_hover (StWidget *widget, gboolean hover) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (widget)); priv = widget->priv; if (priv->hover != hover) { priv->hover = hover; if (priv->hover) st_widget_add_style_pseudo_class (widget, "hover"); else st_widget_remove_style_pseudo_class (widget, "hover"); g_object_notify (G_OBJECT (widget), "hover"); } } /** * st_widget_sync_hover: * @widget: A #StWidget * * Sets @widget's hover state according to the current pointer * position. This can be used to ensure that it is correct after * (or during) a pointer grab. */ void st_widget_sync_hover (StWidget *widget) { ClutterDeviceManager *device_manager; ClutterInputDevice *pointer; ClutterActor *pointer_actor; if (widget->priv->track_hover) { device_manager = clutter_device_manager_get_default (); pointer = clutter_device_manager_get_core_device (device_manager, CLUTTER_POINTER_DEVICE); pointer_actor = clutter_input_device_get_pointer_actor (pointer); if (pointer_actor) st_widget_set_hover (widget, clutter_actor_contains (CLUTTER_ACTOR (widget), pointer_actor)); else st_widget_set_hover (widget, FALSE); } } /** * st_widget_get_hover: * @widget: A #StWidget * * If #StWidget:track-hover is set, this returns whether the pointer * is currently over the widget. * * Returns: current value of hover on @widget */ gboolean st_widget_get_hover (StWidget *widget) { g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE); return widget->priv->hover; } /** * st_widget_set_can_focus: * @widget: A #StWidget * @can_focus: %TRUE if the widget can receive keyboard focus * via keyboard navigation * * Marks @widget as being able to receive keyboard focus via * keyboard navigation. */ void st_widget_set_can_focus (StWidget *widget, gboolean can_focus) { StWidgetPrivate *priv; g_return_if_fail (ST_IS_WIDGET (widget)); priv = widget->priv; if (priv->can_focus != can_focus) { priv->can_focus = can_focus; g_object_notify (G_OBJECT (widget), "can-focus"); } } /** * st_widget_get_can_focus: * @widget: A #StWidget * * Returns the current value of the can-focus property. See * st_widget_set_can_focus() for more information. * * Returns: current value of can-focus on @widget */ gboolean st_widget_get_can_focus (StWidget *widget) { g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE); return widget->priv->can_focus; } static gboolean st_widget_real_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction) { if (widget->priv->can_focus && CLUTTER_ACTOR (widget) != from) { clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget)); return TRUE; } return FALSE; } /** * st_widget_navigate_focus: * @widget: the "top level" container * @from: (allow-none): the actor that the focus is coming from * @direction: the direction focus is moving in * @wrap_around: whether focus should wrap around * * Tries to update the keyboard focus within @widget in response to a * keyboard event. * * If @from is a descendant of @widget, this attempts to move the * keyboard focus to the next descendant of @widget (in the order * implied by @direction) that has the #StWidget:can-focus property * set. If @from is %NULL, or outside of @widget, this attempts to * focus either @widget itself, or its first descendant in the order * implied by @direction. * * If a container type is marked #StWidget:can-focus, the expected * behavior is that it will only take up a single slot on the focus * chain as a whole, rather than allowing navigation between its child * actors (or having a distinction between itself being focused and * one of its children being focused). * * Some widget classes might have slightly different behavior from the * above, where that would make more sense. * * If @wrap_around is %TRUE and @from is a child of @widget, but the * widget has no further children that can accept the focus in the * given direction, then st_widget_navigate_focus() will try a second * time, using a %NULL @from, which should cause it to reset the focus * to the first available widget in the given direction. * * Return value: %TRUE if clutter_actor_grab_key_focus() has been * called on an actor. %FALSE if not. */ gboolean st_widget_navigate_focus (StWidget *widget, ClutterActor *from, GtkDirectionType direction, gboolean wrap_around) { g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE); if (ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, from, direction)) return TRUE; if (wrap_around && from && clutter_actor_contains (CLUTTER_ACTOR (widget), from)) return ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, NULL, direction); return FALSE; } static gboolean append_actor_text (GString *desc, ClutterActor *actor) { if (CLUTTER_IS_TEXT (actor)) { g_string_append_printf (desc, " (\"%s\")", clutter_text_get_text (CLUTTER_TEXT (actor))); return TRUE; } else if (ST_IS_LABEL (actor)) { g_string_append_printf (desc, " (\"%s\")", st_label_get_text (ST_LABEL (actor))); return TRUE; } else return FALSE; } /** * st_describe_actor: * @actor: a #ClutterActor * * Creates a string describing @actor, for use in debugging. This * includes the class name and actor name (if any), plus if @actor * is an #StWidget, its style class and pseudo class names. * * Return value: the debug name. */ char * st_describe_actor (ClutterActor *actor) { GString *desc; const char *name; int i; if (!actor) return g_strdup ("[null]"); desc = g_string_new (NULL); g_string_append_printf (desc, "[%p %s", actor, G_OBJECT_TYPE_NAME (actor)); if (ST_IS_WIDGET (actor)) { const char *style_class = st_widget_get_style_class_name (ST_WIDGET (actor)); const char *pseudo_class = st_widget_get_style_pseudo_class (ST_WIDGET (actor)); char **classes; if (style_class) { classes = g_strsplit (style_class, ",", -1); for (i = 0; classes[i]; i++) { g_strchug (classes[i]); g_string_append_printf (desc, ".%s", classes[i]); } g_strfreev (classes); } if (pseudo_class) { classes = g_strsplit (pseudo_class, ",", -1); for (i = 0; classes[i]; i++) { g_strchug (classes[i]); g_string_append_printf (desc, ":%s", classes[i]); } g_strfreev (classes); } } name = clutter_actor_get_name (actor); if (name) g_string_append_printf (desc, " \"%s\"", name); if (!append_actor_text (desc, actor)) { GList *children, *l; /* Do a limited search of @actor's children looking for a label */ children = clutter_actor_get_children (actor); for (l = children, i = 0; l && i < 20; l = l->next, i++) { if (append_actor_text (desc, l->data)) break; children = g_list_concat (children, clutter_actor_get_children (l->data)); } g_list_free (children); } g_string_append_c (desc, ']'); return g_string_free (desc, FALSE); } /** * st_set_slow_down_factor: * @factor: new slow-down factor * * Set a global factor applied to all animation durations */ void st_set_slow_down_factor (gfloat factor) { st_slow_down_factor = factor; } /** * st_get_slow_down_factor: * * Returns: the global factor applied to all animation durations */ gfloat st_get_slow_down_factor () { return st_slow_down_factor; } /** * st_widget_get_label_actor: * @widget: a #StWidget * * Gets the label that identifies @widget if it is defined * * Return value: (transfer none): the label that identifies the widget */ ClutterActor * st_widget_get_label_actor (StWidget *widget) { g_return_val_if_fail (ST_IS_WIDGET (widget), NULL); return widget->priv->label_actor; } /** * st_widget_set_label_actor: * @widget: a #StWidget * @label: a #ClutterActor * * Sets @label as the #ClutterActor that identifies (labels) * @widget. @label can be %NULL to indicate that @widget is not * labelled any more */ void st_widget_set_label_actor (StWidget *widget, ClutterActor *label) { g_return_if_fail (ST_IS_WIDGET (widget)); if (widget->priv->label_actor != label) { if (widget->priv->label_actor) g_object_unref (widget->priv->label_actor); if (label != NULL) widget->priv->label_actor = g_object_ref (label); else widget->priv->label_actor = NULL; g_object_notify (G_OBJECT (widget), "label-actor"); } } /** * st_widget_set_accessible_name: * @widget: widget to set the accessible name for * @name: (allow-none): a character string to be set as the accessible name * * This method sets @name as the accessible name for @widget. * * Usually you will have no need to set the accessible name for an * object, as usually there is a label for most of the interface * elements. So in general it is better to just use * @st_widget_set_label_actor. This method is only required when you * need to set an accessible name and there is no available label * object. * */ void st_widget_set_accessible_name (StWidget *widget, const gchar *name) { g_return_if_fail (ST_IS_WIDGET (widget)); if (widget->priv->accessible_name != NULL) g_free (widget->priv->accessible_name); widget->priv->accessible_name = g_strdup (name); g_object_notify (G_OBJECT (widget), "accessible-name"); } /** * st_widget_get_accessible_name: * @widget: widget to get the accessible name for * * Gets the accessible name for this widget. See * st_widget_set_accessible_name() for more information. * * Return value: a character string representing the accessible name * of the widget. */ const gchar * st_widget_get_accessible_name (StWidget *widget) { g_return_val_if_fail (ST_IS_WIDGET (widget), NULL); return widget->priv->accessible_name; } /** * st_widget_set_accessible_role: * @widget: widget to set the accessible role for * @role: The role to use * * This method sets @role as the accessible role for @widget. This * role describes what kind of user interface element @widget is and * is provided so that assistive technologies know how to present * @widget to the user. * * Usually you will have no need to set the accessible role for an * object, as this information is extracted from the context of the * object (ie: a #StButton has by default a push button role). This * method is only required when you need to redefine the role * currently associated with the widget, for instance if it is being * used in an unusual way (ie: a #StButton used as a togglebutton), or * if a generic object is used directly (ie: a container as a menu * item). * * If @role is #ATK_ROLE_INVALID, the role will not be changed * and the accessible's default role will be used instead. */ void st_widget_set_accessible_role (StWidget *widget, AtkRole role) { g_return_if_fail (ST_IS_WIDGET (widget)); widget->priv->accessible_role = role; g_object_notify (G_OBJECT (widget), "accessible-role"); } /** * st_widget_get_accessible_role: * @widget: widget to get the accessible role for * * Gets the #AtkRole for this widget. See * st_widget_set_accessible_role() for more information. * * Return value: accessible #AtkRole for this widget */ AtkRole st_widget_get_accessible_role (StWidget *widget) { AtkObject *accessible = NULL; AtkRole role = ATK_ROLE_INVALID; g_return_val_if_fail (ST_IS_WIDGET (widget), ATK_ROLE_INVALID); if (widget->priv->accessible_role != ATK_ROLE_INVALID) role = widget->priv->accessible_role; else if (widget->priv->accessible != NULL) role = atk_object_get_role (accessible); return role; } static void notify_accessible_state_change (StWidget *widget, AtkStateType state, gboolean value) { if (widget->priv->accessible != NULL) atk_object_notify_state_change (widget->priv->accessible, state, value); } /** * st_widget_add_accessible_state: * @widget: A #StWidget * @state: #AtkStateType state to add * * This method adds @state as one of the accessible states for * @widget. The list of states of a widget describes the current state * of user interface element @widget and is provided so that assistive * technologies know how to present @widget to the user. * * Usually you will have no need to add accessible states for an * object, as the accessible object can extract most of the states * from the object itself (ie: a #StButton knows when it is pressed). * This method is only required when one cannot extract the * information automatically from the object itself (i.e.: a generic * container used as a toggle menu item will not automatically include * the toggled state). * */ void st_widget_add_accessible_state (StWidget *widget, AtkStateType state) { g_return_if_fail (ST_IS_WIDGET (widget)); if (atk_state_set_add_state (widget->priv->local_state_set, state)) notify_accessible_state_change (widget, state, TRUE); } /** * st_widget_remove_accessible_state: * @widget: A #StWidget * @state: #AtkState state to remove * * This method removes @state as on of the accessible states for * @widget. See st_widget_add_accessible_state() for more information. * */ void st_widget_remove_accessible_state (StWidget *widget, AtkStateType state) { g_return_if_fail (ST_IS_WIDGET (widget)); if (atk_state_set_remove_state (widget->priv->local_state_set, state)) notify_accessible_state_change (widget, state, FALSE); } /******************************************************************************/ /*************************** ACCESSIBILITY SUPPORT ****************************/ /******************************************************************************/ /* GObject */ static void st_widget_accessible_class_init (StWidgetAccessibleClass *klass); static void st_widget_accessible_init (StWidgetAccessible *widget); static void st_widget_accessible_dispose (GObject *gobject); /* AtkObject */ static AtkStateSet *st_widget_accessible_ref_state_set (AtkObject *obj); static void st_widget_accessible_initialize (AtkObject *obj, gpointer data); static AtkRole st_widget_accessible_get_role (AtkObject *obj); /* Private methods */ static void on_pseudo_class_notify (GObject *gobject, GParamSpec *pspec, gpointer data); static void on_can_focus_notify (GObject *gobject, GParamSpec *pspec, gpointer data); static void on_label_notify (GObject *gobject, GParamSpec *pspec, gpointer data); static void check_pseudo_class (StWidgetAccessible *self, StWidget *widget); static void check_labels (StWidgetAccessible *self, StWidget *widget); G_DEFINE_TYPE (StWidgetAccessible, st_widget_accessible, CALLY_TYPE_ACTOR) #define ST_WIDGET_ACCESSIBLE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET_ACCESSIBLE, \ StWidgetAccessiblePrivate)) struct _StWidgetAccessiblePrivate { /* Cached values (used to avoid extra notifications) */ gboolean selected; gboolean checked; /* The current_label. Right now there are the proper atk * relationships between this object and the label */ AtkObject *current_label; }; static AtkObject * st_widget_get_accessible (ClutterActor *actor) { StWidget *widget = NULL; g_return_val_if_fail (ST_IS_WIDGET (actor), NULL); widget = ST_WIDGET (actor); if (widget->priv->accessible == NULL) { widget->priv->accessible = g_object_new (ST_WIDGET_GET_CLASS (widget)->get_accessible_type (), NULL); atk_object_initialize (widget->priv->accessible, actor); } return widget->priv->accessible; } static const gchar * st_widget_accessible_get_name (AtkObject *obj) { const gchar* name = NULL; g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), NULL); name = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_name (obj); if (name == NULL) { StWidget *widget = NULL; widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (widget == NULL) name = NULL; else name = widget->priv->accessible_name; } return name; } static void st_widget_accessible_class_init (StWidgetAccessibleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); gobject_class->dispose = st_widget_accessible_dispose; atk_class->ref_state_set = st_widget_accessible_ref_state_set; atk_class->initialize = st_widget_accessible_initialize; atk_class->get_role = st_widget_accessible_get_role; atk_class->get_name = st_widget_accessible_get_name; g_type_class_add_private (gobject_class, sizeof (StWidgetAccessiblePrivate)); } static void st_widget_accessible_init (StWidgetAccessible *self) { StWidgetAccessiblePrivate *priv = ST_WIDGET_ACCESSIBLE_GET_PRIVATE (self); self->priv = priv; } static void st_widget_accessible_dispose (GObject *gobject) { StWidgetAccessible *self = ST_WIDGET_ACCESSIBLE (gobject); if (self->priv->current_label) { g_object_unref (self->priv->current_label); self->priv->current_label = NULL; } G_OBJECT_CLASS (st_widget_accessible_parent_class)->dispose (gobject); } static void on_accessible_name_notify (GObject *gobject, GParamSpec *pspec, AtkObject *accessible) { g_object_notify (G_OBJECT (accessible), "accessible-name"); } static void st_widget_accessible_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->initialize (obj, data); g_signal_connect (data, "notify::pseudo-class", G_CALLBACK (on_pseudo_class_notify), obj); g_signal_connect (data, "notify::can-focus", G_CALLBACK (on_can_focus_notify), obj); g_signal_connect (data, "notify::label-actor", G_CALLBACK (on_label_notify), obj); g_signal_connect (data, "notify::accessible-name", G_CALLBACK (on_accessible_name_notify), obj); /* Check the cached selected state and notify the first selection. * Ie: it is required to ensure a first notification when Alt+Tab * popup appears */ check_pseudo_class (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data)); check_labels (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data)); } static AtkStateSet * st_widget_accessible_ref_state_set (AtkObject *obj) { AtkStateSet *result = NULL; AtkStateSet *aux_set = NULL; ClutterActor *actor = NULL; StWidget *widget = NULL; StWidgetAccessible *self = NULL; result = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->ref_state_set (obj); actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (actor == NULL) /* State is defunct */ return result; widget = ST_WIDGET (actor); self = ST_WIDGET_ACCESSIBLE (obj); /* priv->selected should be properly updated on the * ATK_STATE_SELECTED notification callbacks */ if (self->priv->selected) atk_state_set_add_state (result, ATK_STATE_SELECTED); if (self->priv->checked) atk_state_set_add_state (result, ATK_STATE_CHECKED); /* On clutter there isn't any tip to know if a actor is focusable or * not, anyone can receive the key_focus. For this reason * cally_actor sets any actor as FOCUSABLE. This is not the case on * St, where we have can_focus. But this means that we need to * remove the state FOCUSABLE if it is not focusable */ if (st_widget_get_can_focus (widget)) atk_state_set_add_state (result, ATK_STATE_FOCUSABLE); else atk_state_set_remove_state (result, ATK_STATE_FOCUSABLE); /* We add the states added externally if required */ if (!atk_state_set_is_empty (widget->priv->local_state_set)) { aux_set = atk_state_set_or_sets (result, widget->priv->local_state_set); g_object_unref (result); /* previous result will not be used */ result = aux_set; } return result; } static AtkRole st_widget_accessible_get_role (AtkObject *obj) { StWidget *widget = NULL; g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), ATK_ROLE_INVALID); widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj))); if (widget == NULL) return ATK_ROLE_INVALID; if (widget->priv->accessible_role != ATK_ROLE_INVALID) return widget->priv->accessible_role; return ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_role (obj); } static void on_pseudo_class_notify (GObject *gobject, GParamSpec *pspec, gpointer data) { check_pseudo_class (ST_WIDGET_ACCESSIBLE (data), ST_WIDGET (gobject)); } /* * In some cases the only way to check some states are checking the * pseudo-class. Like if the object is selected (see bug 637830) or if * the object is toggled. This method also notifies a state change if * the value is different to the one cached. * * We also assume that if the object uses that pseudo-class, it makes * sense to notify that state change. It would be possible to refine * that behaviour checking the role (ie: notify CHECKED changes only * for CHECK_BUTTON roles). * * In a ideal world we would have a more standard way to get the * state, like the widget-context (as in the case of * gtktreeview-cells), or something like the property "can-focus". But * for the moment this is enough, and we can update that in the future * if required. */ static void check_pseudo_class (StWidgetAccessible *self, StWidget *widget) { gboolean found = FALSE; found = st_widget_has_style_pseudo_class (widget, "selected"); if (found != self->priv->selected) { self->priv->selected = found; atk_object_notify_state_change (ATK_OBJECT (self), ATK_STATE_SELECTED, found); } found = st_widget_has_style_pseudo_class (widget, "checked"); if (found != self->priv->checked) { self->priv->checked = found; atk_object_notify_state_change (ATK_OBJECT (self), ATK_STATE_CHECKED, found); } } static void on_can_focus_notify (GObject *gobject, GParamSpec *pspec, gpointer data) { gboolean can_focus = st_widget_get_can_focus (ST_WIDGET (gobject)); atk_object_notify_state_change (ATK_OBJECT (data), ATK_STATE_FOCUSABLE, can_focus); } static void on_label_notify (GObject *gobject, GParamSpec *pspec, gpointer data) { check_labels (ST_WIDGET_ACCESSIBLE (data), ST_WIDGET (gobject)); } static void check_labels (StWidgetAccessible *widget_accessible, StWidget *widget) { ClutterActor *label = NULL; AtkObject *label_accessible = NULL; /* We only call this method at startup, and when the label changes, * so it is fine to remove the previous relationships if we have the * current_label by default */ if (widget_accessible->priv->current_label != NULL) { AtkObject *previous_label = widget_accessible->priv->current_label; atk_object_remove_relationship (ATK_OBJECT (widget_accessible), ATK_RELATION_LABELLED_BY, previous_label); atk_object_remove_relationship (previous_label, ATK_RELATION_LABEL_FOR, ATK_OBJECT (widget_accessible)); g_object_unref (previous_label); } label = st_widget_get_label_actor (widget); if (label == NULL) { widget_accessible->priv->current_label = NULL; } else { label_accessible = clutter_actor_get_accessible (label); widget_accessible->priv->current_label = g_object_ref (label_accessible); atk_object_add_relationship (ATK_OBJECT (widget_accessible), ATK_RELATION_LABELLED_BY, label_accessible); atk_object_add_relationship (label_accessible, ATK_RELATION_LABEL_FOR, ATK_OBJECT (widget_accessible)); } } Cinnamon-2.8.6/src/st/st-scroll-view-fade.h0000664000175000017500000000265412625666430017413 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-scroll-view-fade.h: Edge fade effect for StScrollView * * Copyright 2010 Intel Corporation. * Copyright 2011 Adel Gadllah * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_SCROLL_VIEW_FADE_H__ #define __ST_SCROLL_VIEW_FADE_H__ #include G_BEGIN_DECLS #define ST_TYPE_SCROLL_VIEW_FADE (st_scroll_view_fade_get_type ()) #define ST_SCROLL_VIEW_FADE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFade)) #define ST_IS_SCROLL_VIEW_FADE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW_FADE)) typedef struct _StScrollViewFade StScrollViewFade; GType st_scroll_view_fade_get_type (void) G_GNUC_CONST; ClutterEffect *st_scroll_view_fade_new (void); G_END_DECLS #endif /* __ST_SCROLL_VIEW_FADE_H__ */ Cinnamon-2.8.6/src/st/st-icon.h0000664000175000017500000000525412625666430015177 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-icon.h: icon widget * * Copyright 2009, 2010 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Written by: Thomas Wood * */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef _ST_ICON #define _ST_ICON #include #include #include #include G_BEGIN_DECLS #define ST_TYPE_ICON st_icon_get_type() #define ST_ICON(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ICON, StIcon)) #define ST_ICON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ICON, StIconClass)) #define ST_IS_ICON(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ICON)) #define ST_IS_ICON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ICON)) #define ST_ICON_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ICON, StIconClass)) typedef struct _StIconPrivate StIconPrivate; /** * StIcon: * * The contents of this structure are private and should only be accessed * through the public API. */ typedef struct { /*< private >*/ StWidget parent; StIconPrivate *priv; } StIcon; typedef struct { StWidgetClass parent_class; /* padding for future expansion */ void (*_padding_0) (void); void (*_padding_1) (void); void (*_padding_2) (void); void (*_padding_3) (void); void (*_padding_4) (void); } StIconClass; GType st_icon_get_type (void); ClutterActor* st_icon_new (void); const gchar *st_icon_get_icon_name (StIcon *icon); void st_icon_set_icon_name (StIcon *icon, const gchar *icon_name); void st_icon_set_icon_type (StIcon *icon, StIconType icon_type); StIconType st_icon_get_icon_type (StIcon *icon); gint st_icon_get_icon_size (StIcon *icon); void st_icon_set_icon_size (StIcon *icon, gint size); void st_icon_set_gicon (StIcon *icon, GIcon *gicon); GIcon *st_icon_get_gicon (StIcon *icon); G_END_DECLS #endif /* _ST_ICON */ Cinnamon-2.8.6/src/st/st-icon-colors.h0000664000175000017500000000201112625666430016462 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __ST_ICON_COLORS__ #define __ST_ICON_COLORS__ #include G_BEGIN_DECLS #define ST_TYPE_ICON_COLORS (st_icon_colors_get_type ()) typedef struct _StIconColors StIconColors; /** * StIconColors: * @foreground: foreground color * @warning: color indicating a warning state * @error: color indicating an error state * @success: color indicating a successful operation * * The #StIconColors structure encapsulates colors for colorizing a symbolic * icon. */ struct _StIconColors { volatile guint ref_count; ClutterColor foreground; ClutterColor warning; ClutterColor error; ClutterColor success; }; GType st_icon_colors_get_type (void) G_GNUC_CONST; StIconColors *st_icon_colors_new (void); StIconColors *st_icon_colors_ref (StIconColors *colors); void st_icon_colors_unref (StIconColors *colors); StIconColors *st_icon_colors_copy (StIconColors *colors); G_END_DECLS #endif /* __ST_ICON_COLORS__ */ Cinnamon-2.8.6/src/st/st-icon.c0000664000175000017500000005062012625666430015167 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-icon.c: icon widget * * Copyright 2009, 2010 Intel Corporation. * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-icon * @short_description: a simple styled icon actor * * #StIcon is a simple styled texture actor that displays an image from * a stylesheet. */ #include "st-enum-types.h" #include "st-icon.h" #include "st-texture-cache.h" #include "st-theme-context.h" #include "st-private.h" enum { PROP_0, PROP_GICON, PROP_ICON_NAME, PROP_ICON_TYPE, PROP_ICON_SIZE }; G_DEFINE_TYPE (StIcon, st_icon, ST_TYPE_WIDGET) #define ST_ICON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_ICON, StIconPrivate)) struct _StIconPrivate { ClutterActor *icon_texture; ClutterActor *pending_texture; guint opacity_handler_id; GIcon *gicon; gchar *icon_name; StIconType icon_type; gint prop_icon_size; /* icon size set as property */ gint theme_icon_size; /* icon size from theme node */ gint icon_size; /* icon size we are using */ gint icon_scale; CoglHandle shadow_material; float shadow_width; float shadow_height; StShadow *shadow_spec; }; static void st_icon_update (StIcon *icon); static gboolean st_icon_update_icon_size (StIcon *icon); #define DEFAULT_ICON_SIZE 48 #define DEFAULT_ICON_TYPE ST_ICON_SYMBOLIC static void st_icon_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StIcon *icon = ST_ICON (gobject); switch (prop_id) { case PROP_GICON: st_icon_set_gicon (icon, g_value_get_object (value)); break; case PROP_ICON_NAME: st_icon_set_icon_name (icon, g_value_get_string (value)); break; case PROP_ICON_TYPE: st_icon_set_icon_type (icon, g_value_get_enum (value)); break; case PROP_ICON_SIZE: st_icon_set_icon_size (icon, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_icon_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StIcon *icon = ST_ICON (gobject); switch (prop_id) { case PROP_GICON: g_value_set_object (value, icon->priv->gicon); break; case PROP_ICON_NAME: g_value_set_string (value, st_icon_get_icon_name (icon)); break; case PROP_ICON_TYPE: g_value_set_enum (value, st_icon_get_icon_type (icon)); break; case PROP_ICON_SIZE: g_value_set_int (value, st_icon_get_icon_size (icon)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_icon_dispose (GObject *gobject) { StIconPrivate *priv = ST_ICON (gobject)->priv; if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } if (priv->pending_texture) { clutter_actor_destroy (priv->pending_texture); g_object_unref (priv->pending_texture); priv->pending_texture = NULL; } if (priv->gicon) { g_object_unref (priv->gicon); priv->gicon = NULL; } if (priv->shadow_material) { cogl_handle_unref (priv->shadow_material); priv->shadow_material = COGL_INVALID_HANDLE; } if (priv->shadow_spec) { st_shadow_unref (priv->shadow_spec); priv->shadow_spec = NULL; } G_OBJECT_CLASS (st_icon_parent_class)->dispose (gobject); } static void st_icon_finalize (GObject *gobject) { StIconPrivate *priv = ST_ICON (gobject)->priv; if (priv->icon_name) { g_free (priv->icon_name); priv->icon_name = NULL; } G_OBJECT_CLASS (st_icon_parent_class)->finalize (gobject); } static void st_icon_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { StIconPrivate *priv = ST_ICON (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); if (min_height_p) *min_height_p = priv->icon_size * priv->icon_scale; if (nat_height_p) *nat_height_p = priv->icon_size * priv->icon_scale; st_theme_node_adjust_preferred_height (theme_node, min_height_p, nat_height_p); } static void st_icon_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { StIconPrivate *priv = ST_ICON (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); if (min_width_p) *min_width_p = priv->icon_size * priv->icon_scale; if (nat_width_p) *nat_width_p = priv->icon_size * priv->icon_scale; st_theme_node_adjust_preferred_width (theme_node, min_width_p, nat_width_p); } static void st_icon_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StIconPrivate *priv = ST_ICON (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); CLUTTER_ACTOR_CLASS (st_icon_parent_class)->allocate (actor, box, flags); if (priv->icon_texture) { ClutterActorBox content_box; st_theme_node_get_content_box (theme_node, box, &content_box); /* Center the texture in the allocation; scaling up the icon from the size * we loaded it at is just a bad idea and probably accidental. Main downside * of doing this is that it may not be obvious that they have to turn off * fill to align the icon non-centered in the parent container. * * We don't use _st_allocate_fill() for a bit of efficiency and because we * expect to get rid of the child actor in favor of a CoglTexture in the * future. */ content_box.x1 = (int)(0.5 + content_box.x1 + (content_box.x2 - content_box.x1 - (priv->icon_size * priv->icon_scale)) / 2.); content_box.x2 = content_box.x1 + (priv->icon_size * priv->icon_scale); content_box.y1 = (int)(0.5 + content_box.y1 + (content_box.y2 - content_box.y1 - (priv->icon_size * priv->icon_scale)) / 2.); content_box.y2 = content_box.y1 + (priv->icon_size * priv->icon_scale); clutter_actor_allocate (priv->icon_texture, &content_box, flags); } } static void st_icon_paint (ClutterActor *actor) { StIconPrivate *priv = ST_ICON (actor)->priv; /* Chain up to paint background */ CLUTTER_ACTOR_CLASS (st_icon_parent_class)->paint (actor); if (priv->icon_texture) { if (priv->shadow_material) { ClutterActorBox allocation; float width, height; clutter_actor_get_allocation_box (priv->icon_texture, &allocation); clutter_actor_box_get_size (&allocation, &width, &height); allocation.x1 = (width - priv->shadow_width) / 2; allocation.y1 = (height - priv->shadow_height) / 2; allocation.x2 = allocation.x1 + priv->shadow_width; allocation.y2 = allocation.y1 + priv->shadow_height; _st_paint_shadow_with_opacity (priv->shadow_spec, priv->shadow_material, &allocation, clutter_actor_get_paint_opacity (priv->icon_texture)); } clutter_actor_paint (priv->icon_texture); } } static void st_icon_style_changed (StWidget *widget) { StIcon *self = ST_ICON (widget); StThemeNode *theme_node = st_widget_get_theme_node (widget); StIconPrivate *priv = self->priv; if (priv->shadow_spec) { st_shadow_unref (priv->shadow_spec); priv->shadow_spec = NULL; } if (priv->shadow_material) { cogl_handle_unref (priv->shadow_material); priv->shadow_material = COGL_INVALID_HANDLE; } priv->shadow_spec = st_theme_node_get_shadow (theme_node, "icon-shadow"); if (priv->shadow_spec && priv->shadow_spec->inset) { g_warning ("The icon-shadow property does not support inset shadows"); st_shadow_unref (priv->shadow_spec); priv->shadow_spec = NULL; } priv->theme_icon_size = (int)(0.5 + st_theme_node_get_length (theme_node, "icon-size")); st_icon_update_icon_size (self); st_icon_update (self); } static void st_icon_class_init (StIconClass *klass) { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); g_type_class_add_private (klass, sizeof (StIconPrivate)); object_class->get_property = st_icon_get_property; object_class->set_property = st_icon_set_property; object_class->dispose = st_icon_dispose; object_class->finalize = st_icon_finalize; actor_class->get_preferred_height = st_icon_get_preferred_height; actor_class->get_preferred_width = st_icon_get_preferred_width; actor_class->allocate = st_icon_allocate; actor_class->paint = st_icon_paint; widget_class->style_changed = st_icon_style_changed; pspec = g_param_spec_object ("gicon", "GIcon", "A GIcon to override :icon-name", G_TYPE_ICON, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_GICON, pspec); pspec = g_param_spec_string ("icon-name", "Icon name", "An icon name", NULL, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_NAME, pspec); pspec = g_param_spec_enum ("icon-type", "Icon type", "The type of icon that should be used", ST_TYPE_ICON_TYPE, DEFAULT_ICON_TYPE, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_TYPE, pspec); pspec = g_param_spec_int ("icon-size", "Icon size", "The size if the icon, if positive. Otherwise the size will be derived from the current style", -1, G_MAXINT, -1, ST_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ICON_SIZE, pspec); } static void st_icon_init (StIcon *self) { self->priv = ST_ICON_GET_PRIVATE (self); self->priv->icon_size = DEFAULT_ICON_SIZE; self->priv->prop_icon_size = -1; self->priv->icon_type = DEFAULT_ICON_TYPE; self->priv->shadow_material = COGL_INVALID_HANDLE; self->priv->shadow_width = -1; self->priv->shadow_height = -1; self->priv->icon_scale = 1; } static void st_icon_update_shadow_material (StIcon *icon) { StIconPrivate *priv = icon->priv; if (priv->shadow_material) { cogl_handle_unref (priv->shadow_material); priv->shadow_material = COGL_INVALID_HANDLE; } if (priv->shadow_spec) { CoglHandle material; gint width, height; clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->icon_texture), &width, &height); material = _st_create_shadow_material_from_actor (priv->shadow_spec, priv->icon_texture); priv->shadow_material = material; priv->shadow_width = width; priv->shadow_height = height; } } static void on_pixbuf_changed (ClutterTexture *texture, StIcon *icon) { st_icon_update_shadow_material (icon); } static void st_icon_finish_update (StIcon *icon) { StIconPrivate *priv = icon->priv; if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } if (priv->pending_texture) { priv->icon_texture = priv->pending_texture; priv->pending_texture = NULL; clutter_actor_add_child (CLUTTER_ACTOR (icon), priv->icon_texture); /* Remove the temporary ref we added */ g_object_unref (priv->icon_texture); st_icon_update_shadow_material (icon); /* "pixbuf-change" is actually a misnomer for "texture-changed" */ g_signal_connect (priv->icon_texture, "pixbuf-change", G_CALLBACK (on_pixbuf_changed), icon); } } static void opacity_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { StIcon *icon = user_data; StIconPrivate *priv = icon->priv; g_signal_handler_disconnect (priv->pending_texture, priv->opacity_handler_id); priv->opacity_handler_id = 0; st_icon_finish_update (icon); } static void st_icon_update (StIcon *icon) { StIconPrivate *priv = icon->priv; StThemeNode *theme_node; StTextureCache *cache; gint scale; ClutterActor *stage; StThemeContext *context; if (priv->pending_texture) { clutter_actor_destroy (priv->pending_texture); g_object_unref (priv->pending_texture); priv->pending_texture = NULL; priv->opacity_handler_id = 0; } theme_node = st_widget_peek_theme_node (ST_WIDGET (icon)); if (theme_node == NULL) return; stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon)); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); g_object_get (context, "scale-factor", &scale, NULL); priv->icon_scale = scale; cache = st_texture_cache_get_default (); if (priv->gicon) { priv->pending_texture = st_texture_cache_load_gicon (cache, (priv->icon_type != ST_ICON_APPLICATION && priv->icon_type != ST_ICON_DOCUMENT) ? theme_node : NULL, priv->gicon, priv->icon_size); } else if (priv->icon_name) { priv->pending_texture = st_texture_cache_load_icon_name (cache, theme_node, priv->icon_name, priv->icon_type, priv->icon_size); } if (priv->pending_texture) { g_object_ref_sink (priv->pending_texture); if (clutter_actor_get_opacity (priv->pending_texture) != 0 || priv->icon_texture == NULL) { /* This icon is ready for showing, or nothing else is already showing */ st_icon_finish_update (icon); } else { /* Will be shown when fully loaded */ priv->opacity_handler_id = g_signal_connect (priv->pending_texture, "notify::opacity", G_CALLBACK (opacity_changed_cb), icon); } } else if (priv->icon_texture) { clutter_actor_destroy (priv->icon_texture); priv->icon_texture = NULL; } } static gboolean st_icon_update_icon_size (StIcon *icon) { StIconPrivate *priv = icon->priv; int new_size; if (priv->prop_icon_size > 0) new_size = priv->prop_icon_size; else if (priv->theme_icon_size > 0) { gint scale; ClutterActor *stage; StThemeContext *context; /* The theme will give us an already-scaled size, so we * undo it here, as priv->icon_size is in unscaled pixels. */ stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon)); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); g_object_get (context, "scale-factor", &scale, NULL); new_size = (gint) (priv->theme_icon_size / scale); } else new_size = DEFAULT_ICON_SIZE; if (new_size != priv->icon_size) { clutter_actor_queue_relayout (CLUTTER_ACTOR (icon)); priv->icon_size = new_size; return TRUE; } else return FALSE; } /** * st_icon_new: * * Create a newly allocated #StIcon * * Returns: A newly allocated #StIcon */ ClutterActor * st_icon_new (void) { return g_object_new (ST_TYPE_ICON, NULL); } const gchar * st_icon_get_icon_name (StIcon *icon) { g_return_val_if_fail (ST_IS_ICON (icon), NULL); return icon->priv->icon_name; } void st_icon_set_icon_name (StIcon *icon, const gchar *icon_name) { StIconPrivate *priv; g_return_if_fail (ST_IS_ICON (icon)); priv = icon->priv; /* Check if there's no change */ if (g_strcmp0 (priv->icon_name, icon_name) == 0) return; g_free (priv->icon_name); priv->icon_name = g_strdup (icon_name); if (priv->gicon) { g_object_unref (priv->gicon); priv->gicon = NULL; g_object_notify (G_OBJECT (icon), "gicon"); } g_object_notify (G_OBJECT (icon), "icon-name"); st_icon_update (icon); } /** * st_icon_get_icon_type: * @icon: a #StIcon * * Gets the type of icon we'll look up to display in the actor. * See st_icon_set_icon_type(). * * Return value: the icon type. */ StIconType st_icon_get_icon_type (StIcon *icon) { g_return_val_if_fail (ST_IS_ICON (icon), DEFAULT_ICON_TYPE); return icon->priv->icon_type; } /** * st_icon_set_icon_type: * @icon: a #StIcon * @icon_type: the type of icon to use * * Sets the type of icon we'll look up to display in the actor. * The icon type determines whether we use a symbolic icon or * a full color icon and also is used for specific handling for * application and document icons. */ void st_icon_set_icon_type (StIcon *icon, StIconType icon_type) { StIconPrivate *priv; g_return_if_fail (ST_IS_ICON (icon)); priv = icon->priv; if (icon_type == priv->icon_type) return; priv->icon_type = icon_type; st_icon_update (icon); g_object_notify (G_OBJECT (icon), "icon-type"); } /** * st_icon_get_gicon: * @icon: an icon * * Return value: (transfer none): the override GIcon, if set, or NULL */ GIcon * st_icon_get_gicon (StIcon *icon) { g_return_val_if_fail (ST_IS_ICON (icon), NULL); return icon->priv->gicon; } /** * st_icon_set_gicon: * @icon: an icon * @gicon: (allow-none): a #GIcon to override :icon-name */ void st_icon_set_gicon (StIcon *icon, GIcon *gicon) { g_return_if_fail (ST_IS_ICON (icon)); g_return_if_fail (G_IS_ICON (gicon)); if (icon->priv->gicon == gicon) /* do nothing */ return; if (icon->priv->gicon) { g_object_unref (icon->priv->gicon); icon->priv->gicon = NULL; } if (gicon) icon->priv->gicon = g_object_ref (gicon); if (icon->priv->icon_name) { g_free (icon->priv->icon_name); icon->priv->icon_name = NULL; g_object_notify (G_OBJECT (icon), "icon-name"); } g_object_notify (G_OBJECT (icon), "gicon"); st_icon_update (icon); } /** * st_icon_get_icon_size: * @icon: an icon * * Gets the size explicit size on the icon. This is not necesariily * the size that the icon will actually be displayed at. * * Return value: the size explicitly set, or -1 if no size has been set */ gint st_icon_get_icon_size (StIcon *icon) { g_return_val_if_fail (ST_IS_ICON (icon), -1); return icon->priv->prop_icon_size; } /** * st_icon_set_icon_size: * @icon: an icon * @size: if positive, the new size, otherwise the size will be * derived from the current style * * Sets an explicit size for the icon. */ void st_icon_set_icon_size (StIcon *icon, gint size) { StIconPrivate *priv; g_return_if_fail (ST_IS_ICON (icon)); priv = icon->priv; if (priv->prop_icon_size != size) { priv->prop_icon_size = size; if (st_icon_update_icon_size (icon)) st_icon_update (icon); g_object_notify (G_OBJECT (icon), "icon-size"); } } Cinnamon-2.8.6/src/st/st-texture-cache.c0000664000175000017500000017144112625666430017005 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-texture-cache.h: Object for loading and caching images as textures * * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2010, Maxim Ermilov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "config.h" #include "st-texture-cache.h" #include "st-private.h" #include "st-cogl-wrapper.h" #include #include #include #define CACHE_PREFIX_ICON "icon:" #define CACHE_PREFIX_URI "uri:" #define CACHE_PREFIX_URI_FOR_CAIRO "uri-for-cairo:" #define CACHE_PREFIX_RAW_CHECKSUM "raw-checksum:" #define CACHE_PREFIX_COMPRESSED_CHECKSUM "compressed-checksum:" static int active_scale = 1; struct _StTextureCachePrivate { GtkIconTheme *icon_theme; /* Things that were loaded with a cache policy != NONE */ GHashTable *keyed_cache; /* char * -> CoglTexture* */ /* Presently this is used to de-duplicate requests for GIcons and async URIs. */ GHashTable *outstanding_requests; /* char * -> AsyncTextureLoadData * */ /* File monitors to evict cache data on changes */ GHashTable *file_monitors; /* char * -> GFileMonitor * */ GSettings *settings; double scale; }; static void st_texture_cache_dispose (GObject *object); static void st_texture_cache_finalize (GObject *object); enum { ICON_THEME_CHANGED, TEXTURE_FILE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE(StTextureCache, st_texture_cache, G_TYPE_OBJECT); /* We want to preserve the aspect ratio by default, also the default * pipeline for an empty texture is full opacity white, which we * definitely don't want. Skip that by setting 0 opacity. */ static ClutterTexture * create_default_texture (void) { ClutterTexture * texture = CLUTTER_TEXTURE (clutter_texture_new ()); g_object_set (texture, "keep-aspect-ratio", TRUE, "opacity", 0, NULL); return texture; } /* Reverse the opacity we added while loading */ static void set_texture_cogl_texture (ClutterTexture *clutter_texture, CoglTexture *cogl_texture) { clutter_texture_set_cogl_texture (clutter_texture, cogl_texture); g_object_set (clutter_texture, "opacity", 255, NULL); } static void st_texture_cache_class_init (StTextureCacheClass *klass) { GObjectClass *gobject_class = (GObjectClass *)klass; gobject_class->dispose = st_texture_cache_dispose; gobject_class->finalize = st_texture_cache_finalize; signals[ICON_THEME_CHANGED] = g_signal_new ("icon-theme-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, /* no default handler slot */ NULL, NULL, NULL, G_TYPE_NONE, 0); signals[TEXTURE_FILE_CHANGED] = g_signal_new ("texture-file-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, /* no default handler slot */ NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } /* Evicts all cached textures for named icons */ static void st_texture_cache_evict_icons (StTextureCache *cache) { GHashTableIter iter; gpointer key; gpointer value; g_hash_table_iter_init (&iter, cache->priv->keyed_cache); while (g_hash_table_iter_next (&iter, &key, &value)) { const char *cache_key = key; /* This is too conservative - it takes out all cached textures * for GIcons even when they aren't named icons, but it's not * worth the complexity of parsing the key and calling * g_icon_new_for_string(); icon theme changes aren't normal */ if (g_str_has_prefix (cache_key, CACHE_PREFIX_ICON)) g_hash_table_iter_remove (&iter); } } static void on_icon_theme_changed (GtkIconTheme *icon_theme, StTextureCache *cache) { st_texture_cache_evict_icons (cache); g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0); } static void update_scale_factor (GSettings *settings, gchar *key, gpointer data) { StTextureCache *cache = ST_TEXTURE_CACHE (data); cache->priv->scale = g_settings_get_int (settings, key); active_scale = cache->priv->scale; on_icon_theme_changed (cache->priv->icon_theme, cache); } static void st_texture_cache_init (StTextureCache *self) { self->priv = g_new0 (StTextureCachePrivate, 1); self->priv->icon_theme = gtk_icon_theme_get_default (); g_signal_connect (self->priv->icon_theme, "changed", G_CALLBACK (on_icon_theme_changed), self); self->priv->keyed_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cogl_object_unref); self->priv->outstanding_requests = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->priv->file_monitors = g_hash_table_new_full (g_str_hash, g_str_equal, g_object_unref, g_object_unref); self->priv->settings = g_settings_new ("org.cinnamon"); g_signal_connect (self->priv->settings, "changed::active-display-scale", G_CALLBACK (update_scale_factor), self); update_scale_factor (self->priv->settings, "active-display-scale", self); } static void st_texture_cache_dispose (GObject *object) { StTextureCache *self = (StTextureCache*)object; if (self->priv->icon_theme) { g_signal_handlers_disconnect_by_func (self->priv->icon_theme, (gpointer) on_icon_theme_changed, self); self->priv->icon_theme = NULL; } if (self->priv->settings) { g_signal_handlers_disconnect_by_func (self->priv->settings, (gpointer) update_scale_factor, self); g_object_unref (self->priv->settings); self->priv->settings = NULL; } g_clear_pointer (&self->priv->keyed_cache, g_hash_table_destroy); g_clear_pointer (&self->priv->outstanding_requests, g_hash_table_destroy); g_clear_pointer (&self->priv->file_monitors, g_hash_table_destroy); G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object); } static void st_texture_cache_finalize (GObject *object) { G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object); } static gboolean compute_pixbuf_scale (gint width, gint height, gint available_width, gint available_height, gint *new_width, gint *new_height) { int scaled_width, scaled_height; if (width == 0 || height == 0) return FALSE; if (available_width >= 0 && available_height >= 0) { /* This should keep the aspect ratio of the image intact, because if * available_width < (available_height * width) / height * then * (available_width * height) / width < available_height * So we are guaranteed to either scale the image to have an available_width * for width and height scaled accordingly OR have the available_height * for height and width scaled accordingly, whichever scaling results * in the image that can fit both available dimensions. */ scaled_width = MIN (available_width, (available_height * width) / height); scaled_height = MIN (available_height, (available_width * height) / width); } else if (available_width >= 0) { scaled_width = available_width; scaled_height = (available_width * height) / width; } else if (available_height >= 0) { scaled_width = (available_height * width) / height; scaled_height = available_height; } else { scaled_width = scaled_height = 0; } /* Scale the image only if that will not increase its original dimensions. */ if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height) { *new_width = scaled_width; *new_height = scaled_height; return TRUE; } return FALSE; } static void rgba_from_clutter (GdkRGBA *rgba, ClutterColor *color) { rgba->red = color->red / 255.; rgba->green = color->green / 255.; rgba->blue = color->blue / 255.; rgba->alpha = color->alpha / 255.; } /* A private structure for keeping width and height. */ typedef struct { int width; int height; } Dimensions; /* This struct corresponds to a request for an texture. * It's creasted when something needs a new texture, * and destroyed when the texture data is loaded. */ typedef struct { StTextureCache *cache; StTextureCachePolicy policy; char *key; gboolean enforced_square; guint width; guint height; GSList *textures; GtkIconInfo *icon_info; StIconColors *colors; char *uri; } AsyncTextureLoadData; static void texture_load_data_free (gpointer p) { AsyncTextureLoadData *data = p; if (data->icon_info) { g_object_unref (data->icon_info); if (data->colors) st_icon_colors_unref (data->colors); } else if (data->uri) g_free (data->uri); if (data->key) g_free (data->key); if (data->textures) g_slist_free_full (data->textures, (GDestroyNotify) g_object_unref); g_free (data); } /** * on_image_size_prepared: * @pixbuf_loader: #GdkPixbufLoader loading the image * @width: the original width of the image * @height: the original height of the image * @data: pointer to the #Dimensions sructure containing available width and height for the image, * available width or height can be -1 if the dimension is not limited * * Private function. * * Sets the size of the image being loaded to fit the available width and height dimensions, * but never scales up the image beyond its actual size. * Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal. */ static void on_image_size_prepared (GdkPixbufLoader *pixbuf_loader, gint width, gint height, gpointer data) { Dimensions *available_dimensions = data; int available_width = available_dimensions->width; int available_height = available_dimensions->height; int scaled_width; int scaled_height; int final_width = width; int final_height = height; if (compute_pixbuf_scale (width, height, available_width, available_height, &scaled_width, &scaled_height)) { final_width = scaled_width; final_height = scaled_height; } final_width = (int)((double) final_width * active_scale); final_height = (int)((double) final_height * active_scale); gdk_pixbuf_loader_set_size (pixbuf_loader, final_width, final_height); } static GdkPixbuf * impl_load_pixbuf_data (const guchar *data, gsize size, int available_width, int available_height, GError **error) { GdkPixbufLoader *pixbuf_loader = NULL; GdkPixbuf *rotated_pixbuf = NULL; GdkPixbuf *pixbuf; gboolean success; Dimensions available_dimensions; int width_before_rotation, width_after_rotation; pixbuf_loader = gdk_pixbuf_loader_new (); available_dimensions.width = available_width; available_dimensions.height = available_height; g_signal_connect (pixbuf_loader, "size-prepared", G_CALLBACK (on_image_size_prepared), &available_dimensions); success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error); if (!success) goto out; success = gdk_pixbuf_loader_close (pixbuf_loader, error); if (!success) goto out; pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader); width_before_rotation = gdk_pixbuf_get_width (pixbuf); rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf); /* There is currently no way to tell if the pixbuf will need to be rotated before it is loaded, * so we only check that once it is loaded, and reload it again if it needs to be rotated in order * to use the available width and height correctly. * See http://bugzilla.gnome.org/show_bug.cgi?id=579003 */ if (width_before_rotation != width_after_rotation) { g_object_unref (pixbuf_loader); g_object_unref (rotated_pixbuf); rotated_pixbuf = NULL; pixbuf_loader = gdk_pixbuf_loader_new (); /* We know that the image will later be rotated, so we reverse the available dimensions. */ available_dimensions.width = available_height; available_dimensions.height = available_width; g_signal_connect (pixbuf_loader, "size-prepared", G_CALLBACK (on_image_size_prepared), &available_dimensions); success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error); if (!success) goto out; success = gdk_pixbuf_loader_close (pixbuf_loader, error); if (!success) goto out; pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader); rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf); } out: if (pixbuf_loader) g_object_unref (pixbuf_loader); return rotated_pixbuf; } static GdkPixbuf* decode_image (const char *val) { int i; GError *error = NULL; GdkPixbuf *res = NULL; struct { const char *prefix; const char *mime_type; } formats[] = { { "data:image/x-icon;base64,", "image/x-icon" }, { "data:image/png;base64,", "image/png" } }; g_return_val_if_fail (val, NULL); for (i = 0; i < G_N_ELEMENTS (formats); i++) { if (g_str_has_prefix (val, formats[i].prefix)) { gsize len; guchar *data = NULL; char *unescaped; unescaped = g_uri_unescape_string (val + strlen (formats[i].prefix), NULL); if (unescaped) { data = g_base64_decode (unescaped, &len); g_free (unescaped); } if (data) { GdkPixbufLoader *loader; loader = gdk_pixbuf_loader_new_with_mime_type (formats[i].mime_type, &error); if (loader && gdk_pixbuf_loader_write (loader, data, len, &error) && gdk_pixbuf_loader_close (loader, &error)) { res = gdk_pixbuf_loader_get_pixbuf (loader); g_object_ref (res); } g_object_unref (loader); g_free (data); } } } if (!res) { if (error) { g_warning ("%s\n", error->message); g_error_free (error); } else g_warning ("incorrect data uri"); } return res; } static GdkPixbuf * impl_load_pixbuf_file (const char *uri, int available_width, int available_height, GError **error) { GdkPixbuf *pixbuf = NULL; GFile *file; char *contents = NULL; gsize size; if (g_str_has_prefix (uri, "data:")) return decode_image (uri); file = g_file_new_for_uri (uri); if (g_file_load_contents (file, NULL, &contents, &size, NULL, error)) { pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size, available_width, available_height, error); } g_object_unref (file); g_free (contents); return pixbuf; } static void load_pixbuf_thread (GSimpleAsyncResult *result, GObject *object, GCancellable *cancellable) { GdkPixbuf *pixbuf; AsyncTextureLoadData *data; GError *error = NULL; data = g_async_result_get_user_data (G_ASYNC_RESULT (result)); g_assert (data != NULL); g_assert (data->uri != NULL); pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error); if (error != NULL) { g_simple_async_result_set_from_error (result, error); return; } if (pixbuf) g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf), g_object_unref); } static GdkPixbuf * load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; return g_simple_async_result_get_op_res_gpointer (simple); } static CoglTexture * data_to_cogl_texture (const guchar *data, gboolean has_alpha, int width, int height, int rowstride, gboolean add_padding) { CoglHandle texture, offscreen = NULL; CoglColor clear_color; guint size; GError *error; size = MAX (width, height); if (!add_padding || width == height) return st_cogl_texture_new_from_data_wrapper (width, height, COGL_TEXTURE_NONE, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, data); texture = st_cogl_texture_new_with_size_wrapper (size, size, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); if (texture) offscreen = cogl_offscreen_new_to_texture (texture); error = NULL; if (!texture || !offscreen || !cogl_framebuffer_allocate (offscreen, &error)) { if (!texture) g_warning("Failed to allocate texture (sized %d)", size); else if (!offscreen) { g_warning("Failed to allocate offscreen for texture (sized %d)", size); cogl_object_unref (texture); } else { g_warning ("Failed to allocate FBO (sized %d): %s", size, error->message); cogl_object_unref (texture); cogl_object_unref (offscreen); g_clear_error (&error); } return st_cogl_texture_new_from_data_wrapper (width, height, COGL_TEXTURE_NONE, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, data); } cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0); cogl_push_framebuffer (offscreen); cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); cogl_pop_framebuffer (); cogl_handle_unref (offscreen); cogl_texture_set_region (texture, 0, 0, (size - width) / 2, (size - height) / 2, width, height, width, height, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, rowstride, data); return texture; } static CoglTexture * pixbuf_to_cogl_texture (GdkPixbuf *pixbuf, gboolean add_padding) { return data_to_cogl_texture (gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf), gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), gdk_pixbuf_get_rowstride (pixbuf), add_padding); } static cairo_surface_t * pixbuf_to_cairo_surface (GdkPixbuf *pixbuf) { cairo_surface_t *dummy_surface; cairo_pattern_t *pattern; cairo_surface_t *surface; cairo_t *cr; dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); cr = cairo_create (dummy_surface); gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); pattern = cairo_get_source (cr); cairo_pattern_get_surface (pattern, &surface); cairo_surface_reference (surface); cairo_destroy (cr); cairo_surface_destroy (dummy_surface); return surface; } static void finish_texture_load (AsyncTextureLoadData *data, GdkPixbuf *pixbuf) { GSList *iter; StTextureCache *cache; CoglTexture *texdata = NULL; cache = data->cache; g_hash_table_remove (cache->priv->outstanding_requests, data->key); if (pixbuf == NULL) goto out; texdata = pixbuf_to_cogl_texture (pixbuf, data->enforced_square); if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE) { gpointer orig_key, value; if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { cogl_object_ref (texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key), texdata); } } for (iter = data->textures; iter; iter = iter->next) { ClutterTexture *texture = iter->data; set_texture_cogl_texture (texture, texdata); } out: if (texdata) cogl_object_unref (texdata); texture_load_data_free (data); } static void on_symbolic_icon_loaded (GObject *source, GAsyncResult *result, gpointer user_data) { GdkPixbuf *pixbuf; pixbuf = gtk_icon_info_load_symbolic_finish (GTK_ICON_INFO (source), result, NULL, NULL); finish_texture_load (user_data, pixbuf); g_clear_object (&pixbuf); } static void on_icon_loaded (GObject *source, GAsyncResult *result, gpointer user_data) { GdkPixbuf *pixbuf; pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (source), result, NULL); finish_texture_load (user_data, pixbuf); g_clear_object (&pixbuf); } static void on_pixbuf_loaded (GObject *source, GAsyncResult *result, gpointer user_data) { GdkPixbuf *pixbuf; pixbuf = load_pixbuf_async_finish (ST_TEXTURE_CACHE (source), result, NULL); finish_texture_load (user_data, pixbuf); g_clear_object (&pixbuf); } static void load_texture_async (StTextureCache *cache, AsyncTextureLoadData *data) { if (data->uri) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (cache), on_pixbuf_loaded, data, load_texture_async); g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); } else if (data->icon_info) { StIconColors *colors = data->colors; if (colors) { GdkRGBA foreground_color; GdkRGBA success_color; GdkRGBA warning_color; GdkRGBA error_color; rgba_from_clutter (&foreground_color, &colors->foreground); rgba_from_clutter (&success_color, &colors->success); rgba_from_clutter (&warning_color, &colors->warning); rgba_from_clutter (&error_color, &colors->error); gtk_icon_info_load_symbolic_async (data->icon_info, &foreground_color, &success_color, &warning_color, &error_color, NULL, on_symbolic_icon_loaded, data); } else { gtk_icon_info_load_icon_async (data->icon_info, NULL, on_icon_loaded, data); } } else g_assert_not_reached (); } typedef struct { StTextureCache *cache; ClutterTexture *texture; GObject *source; guint notify_signal_id; gboolean weakref_active; } StTextureCachePropertyBind; static void st_texture_cache_reset_texture (StTextureCachePropertyBind *bind, const char *propname) { GdkPixbuf *pixbuf; CoglTexture *texdata; g_object_get (bind->source, propname, &pixbuf, NULL); g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf)); if (pixbuf != NULL) { texdata = pixbuf_to_cogl_texture (pixbuf, FALSE); g_object_unref (pixbuf); clutter_texture_set_cogl_texture (bind->texture, texdata); cogl_object_unref (texdata); clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 255); } else clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 0); } static void st_texture_cache_on_pixbuf_notify (GObject *object, GParamSpec *paramspec, gpointer data) { StTextureCachePropertyBind *bind = data; st_texture_cache_reset_texture (bind, paramspec->name); } static void st_texture_cache_bind_weak_notify (gpointer data, GObject *source_location) { StTextureCachePropertyBind *bind = data; bind->weakref_active = FALSE; g_signal_handler_disconnect (bind->source, bind->notify_signal_id); } static void st_texture_cache_free_bind (gpointer data) { StTextureCachePropertyBind *bind = data; if (bind->weakref_active) g_object_weak_unref (G_OBJECT(bind->texture), st_texture_cache_bind_weak_notify, bind); g_free (bind); } /** * st_texture_cache_bind_pixbuf_property: * @cache: * @object: A #GObject with a property @property_name of type #GdkPixbuf * @property_name: Name of a property * * Create a #ClutterTexture which tracks the #GdkPixbuf value of a GObject property * named by @property_name. Unlike other methods in StTextureCache, the underlying * #CoglTexture is not shared by default with other invocations to this method. * * If the source object is destroyed, the texture will continue to show the last * value of the property. * * Return value: (transfer none): A new #ClutterActor */ ClutterActor * st_texture_cache_bind_pixbuf_property (StTextureCache *cache, GObject *object, const char *property_name) { ClutterTexture *texture; gchar *notify_key; StTextureCachePropertyBind *bind; texture = CLUTTER_TEXTURE (clutter_texture_new ()); bind = g_new0 (StTextureCachePropertyBind, 1); bind->cache = cache; bind->texture = texture; bind->source = object; g_object_weak_ref (G_OBJECT (texture), st_texture_cache_bind_weak_notify, bind); bind->weakref_active = TRUE; st_texture_cache_reset_texture (bind, property_name); notify_key = g_strdup_printf ("notify::%s", property_name); bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(st_texture_cache_on_pixbuf_notify), bind, (GClosureNotify)st_texture_cache_free_bind, 0); g_free (notify_key); return CLUTTER_ACTOR(texture); } /** * st_texture_cache_load: (skip) * @cache: A #StTextureCache * @key: Arbitrary string used to refer to item * @policy: Caching policy * @load: Function to create the texture, if not already cached * @data: User data passed to @load * @error: A #GError * * Load an arbitrary texture, caching it. The string chosen for @key * should be of the form "type-prefix:type-uuid". For example, * "url:file:///usr/share/icons/hicolor/48x48/apps/firefox.png", or * "stock-icon:gtk-ok". * * Returns: (transfer full): A newly-referenced handle to the texture */ CoglTexture * st_texture_cache_load (StTextureCache *cache, const char *key, StTextureCachePolicy policy, StTextureCacheLoader load, void *data, GError **error) { CoglTexture *texture; texture = g_hash_table_lookup (cache->priv->keyed_cache, key); if (!texture) { texture = load (cache, key, data, error); if (texture) g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texture); else return NULL; } cogl_object_ref (texture); return texture; } /** * ensure_request: * @cache: * @key: A cache key * @policy: Cache policy * @request: (out): If no request is outstanding, one will be created and returned here * @texture: A texture to be added to the request * * Check for any outstanding load for the data represented by @key. If there * is already a request pending, append it to that request to avoid loading * the data multiple times. * * Returns: %TRUE iff there is already a request pending */ static gboolean ensure_request (StTextureCache *cache, const char *key, StTextureCachePolicy policy, AsyncTextureLoadData **request, ClutterActor *texture) { CoglTexture *texdata; AsyncTextureLoadData *pending; gboolean had_pending; texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata != NULL) { /* We had this cached already, just set the texture and we're done. */ set_texture_cogl_texture (CLUTTER_TEXTURE (texture), texdata); return TRUE; } pending = g_hash_table_lookup (cache->priv->outstanding_requests, key); had_pending = pending != NULL; if (pending == NULL) { /* Not cached and no pending request, create it */ *request = g_new0 (AsyncTextureLoadData, 1); if (policy != ST_TEXTURE_CACHE_POLICY_NONE) g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); } else *request = pending; /* Regardless of whether there was a pending request, prepend our texture here. */ (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (texture)); return had_pending; } static ClutterActor * load_gicon_with_colors (StTextureCache *cache, GIcon *icon, gint size, gint scale, StIconColors *colors) { AsyncTextureLoadData *request; ClutterActor *texture; char *gicon_string; char *key; GtkIconTheme *theme; GtkIconInfo *info; StTextureCachePolicy policy; /* Do theme lookups in the main thread to avoid thread-unsafety */ theme = cache->priv->icon_theme; info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, size, scale, GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE); if (info == NULL) { texture = CLUTTER_ACTOR (create_default_texture ()); clutter_actor_set_size (texture, size * scale, size * scale); return texture; } gicon_string = g_icon_to_string (icon); /* A return value of NULL indicates that the icon can not be serialized, * so don't have a unique identifier for it as a cache key, and thus can't * be cached. If it is cachable, we hardcode a policy of FOREVER here for * now; we should actually blow this away on icon theme changes probably */ policy = gicon_string != NULL ? ST_TEXTURE_CACHE_POLICY_FOREVER : ST_TEXTURE_CACHE_POLICY_NONE; if (colors) { /* This raises some doubts about the practice of using string keys */ key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,colors=%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x", gicon_string, size, scale, colors->foreground.red, colors->foreground.blue, colors->foreground.green, colors->foreground.alpha, colors->warning.red, colors->warning.blue, colors->warning.green, colors->warning.alpha, colors->error.red, colors->error.blue, colors->error.green, colors->error.alpha, colors->success.red, colors->success.blue, colors->success.green, colors->success.alpha); } else { key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d", gicon_string, size, scale); } g_free (gicon_string); texture = (ClutterActor *) create_default_texture (); clutter_actor_set_size (texture, size * scale, size * scale); if (ensure_request (cache, key, policy, &request, texture)) { /* If there's an outstanding request, we've just added ourselves to it */ g_object_unref (info); g_free (key); } else { /* Else, make a new request */ request->cache = cache; /* Transfer ownership of key */ request->key = key; request->policy = policy; request->colors = colors ? st_icon_colors_ref (colors) : NULL; request->icon_info = info; request->width = request->height = size * scale; request->enforced_square = TRUE; load_texture_async (cache, request); } return CLUTTER_ACTOR (texture); } /** * st_texture_cache_load_gicon: * @cache: The texture cache instance * @theme_node: (allow-none): The #StThemeNode to use for colors, or NULL * if the icon must not be recolored * @icon: the #GIcon to load * @size: Size of themed * * This method returns a new #ClutterActor for a given #GIcon. If the * icon isn't loaded already, the texture will be filled * asynchronously. * * Return Value: (transfer none): A new #ClutterActor for the icon, or an empty ClutterActor * if none was found. */ ClutterActor * st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size) { return load_gicon_with_colors (cache, icon, size, cache->priv->scale, theme_node ? st_theme_node_get_icon_colors (theme_node) : NULL); } static ClutterActor * load_from_pixbuf (GdkPixbuf *pixbuf) { ClutterTexture *texture; CoglTexture *texdata; int width = gdk_pixbuf_get_width (pixbuf); int height = gdk_pixbuf_get_height (pixbuf); texture = create_default_texture (); clutter_actor_set_size (CLUTTER_ACTOR (texture), width, height); texdata = pixbuf_to_cogl_texture (pixbuf, FALSE); set_texture_cogl_texture (texture, texdata); cogl_object_unref (texdata); return CLUTTER_ACTOR (texture); } static void file_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other, GFileMonitorEvent event_type, gpointer user_data) { StTextureCache *cache = user_data; char *uri, *key; if (event_type != G_FILE_MONITOR_EVENT_CHANGED) return; uri = g_file_get_uri (file); key = g_strconcat (CACHE_PREFIX_URI, uri, NULL); g_hash_table_remove (cache->priv->keyed_cache, key); g_free (key); key = g_strconcat (CACHE_PREFIX_URI_FOR_CAIRO, uri, NULL); g_hash_table_remove (cache->priv->keyed_cache, key); g_free (key); g_signal_emit (cache, signals[TEXTURE_FILE_CHANGED], 0, uri); g_free (uri); } static void ensure_monitor_for_uri (StTextureCache *cache, const gchar *uri) { /* Don't monitor changes at all. * We're keeping this function for now, even if it doesn't do anything * In case special cases come up in the future, where monitors are needed for particular uris. */ // StTextureCachePrivate *priv = cache->priv; // GFile *file = g_file_new_for_uri (uri); // /* No point in trying to monitor files that are part of a // * GResource, since it does not support file monitoring. // */ // if (!g_file_has_uri_scheme (file, "resource")) { // if (g_hash_table_lookup (priv->file_monitors, uri) == NULL) // { // GFileMonitor *monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, // NULL, NULL); // g_signal_connect (monitor, "changed", // G_CALLBACK (file_changed_cb), cache); // g_hash_table_insert (priv->file_monitors, g_strdup (uri), monitor); // } // } // g_object_unref (file); } typedef struct { gchar *path; gint grid_width, grid_height; ClutterActor *actor; GFunc load_callback; gpointer load_callback_data; } AsyncImageData; static void on_data_destroy (gpointer data) { AsyncImageData *d = (AsyncImageData *)data; g_free (d->path); g_object_unref (d->actor); g_free (d); } static void on_sliced_image_loaded (GObject *source_object, GAsyncResult *res, gpointer user_data) { GObject *cache = source_object; AsyncImageData *data = (AsyncImageData *)user_data; GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); GList *list; if (g_simple_async_result_propagate_error (simple, NULL)) return; for (list = g_simple_async_result_get_op_res_gpointer (simple); list; list = g_list_next (list)) { ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data)); clutter_actor_hide (actor); clutter_actor_add_child (data->actor, actor); } if (data->load_callback != NULL) data->load_callback (cache, data->load_callback_data); } static void free_glist_unref_gobjects (gpointer p) { GList *list = p; GList *iter; for (iter = list; iter; iter = iter->next) g_object_unref (iter->data); g_list_free (list); } static void load_sliced_image (GSimpleAsyncResult *result, GObject *object, GCancellable *cancellable) { AsyncImageData *data; GList *res = NULL; GdkPixbuf *pix; gint width, height, y, x; g_assert (!cancellable); data = g_object_get_data (G_OBJECT (result), "load_sliced_image"); g_assert (data); if (!(pix = gdk_pixbuf_new_from_file (data->path, NULL))) return; width = gdk_pixbuf_get_width (pix); height = gdk_pixbuf_get_height (pix); for (y = 0; y < height; y += data->grid_height) { for (x = 0; x < width; x += data->grid_width) { GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, data->grid_width, data->grid_height); g_assert (pixbuf != NULL); res = g_list_append (res, pixbuf); } } /* We don't need the original pixbuf anymore, though the subpixbufs will hold a reference. */ g_object_unref (pix); g_simple_async_result_set_op_res_gpointer (result, res, free_glist_unref_gobjects); } /** * st_texture_cache_load_sliced_image: * @cache: A #StTextureCache * @path: Path to a filename * @grid_width: Width in pixels * @grid_height: Height in pixels * @load_callback: (scope async) (allow-none): Function called when the image is loaded, or %NULL * @user_data: Data to pass to the load callback * * This function reads a single image file which contains multiple images internally. * The image file will be divided using @grid_width and @grid_height; * note that the dimensions of the image loaded from @path * should be a multiple of the specified grid dimensions. * * Returns: (transfer none): A new #ClutterActor */ ClutterActor * st_texture_cache_load_sliced_image (StTextureCache *cache, const gchar *path, gint grid_width, gint grid_height, GFunc load_callback, gpointer user_data) { AsyncImageData *data; GSimpleAsyncResult *result; ClutterActor *actor = clutter_actor_new (); data = g_new0 (AsyncImageData, 1); data->grid_width = grid_width; data->grid_height = grid_height; data->path = g_strdup (path); data->actor = actor; data->load_callback = load_callback; data->load_callback_data = user_data; g_object_ref (G_OBJECT (actor)); result = g_simple_async_result_new (G_OBJECT (cache), on_sliced_image_loaded, data, st_texture_cache_load_sliced_image); g_object_set_data_full (G_OBJECT (result), "load_sliced_image", data, on_data_destroy); g_simple_async_result_run_in_thread (result, load_sliced_image, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); return actor; } /** * StIconType: * @ST_ICON_SYMBOLIC: a symbolic (ie, mostly monochrome) icon * @ST_ICON_FULLCOLOR: a full-color icon * @ST_ICON_APPLICATION: a full-color icon, which is expected * to be an application icon * @ST_ICON_DOCUMENT: a full-color icon, which is expected * to be a document (MIME type) icon * * Describes what style of icon is desired in a call to * st_texture_cache_load_icon_name() or st_texture_cache_load_gicon(). * Use %ST_ICON_SYMBOLIC for symbolic icons (eg, for the panel and * much of the rest of Cinnamon chrome) or %ST_ICON_FULLCOLOR for a * full-color icon. * * If you know that the requested icon is either an application icon * or a document type icon, you should use %ST_ICON_APPLICATION or * %ST_ICON_DOCUMENT, which may do a better job of selecting the * correct theme icon for those types. If you are unsure what kind of * icon you are loading, use %ST_ICON_FULLCOLOR. */ /* generates names like g_themed_icon_new_with_default_fallbacks(), * but *only* symbolic names */ static char ** symbolic_names_for_icon (const char *name) { char **parts, **names; int i, numnames; parts = g_strsplit (name, "-", -1); numnames = g_strv_length (parts); names = g_new (char *, numnames + 1); for (i = 0; parts[i]; i++) { if (i == 0) { names[i] = g_strdup_printf ("%s-symbolic", parts[i]); } else { names[i] = g_strdup_printf ("%.*s-%s-symbolic", (int) (strlen (names[i - 1]) - strlen ("-symbolic")), names[i - 1], parts[i]); } } names[i] = NULL; g_strfreev (parts); /* need to reverse here, because longest (most specific) name has to come first */ for (i = 0; i < (numnames / 2); i++) { char *tmp = names[i]; names[i] = names[numnames - i - 1]; names[numnames - i - 1] = tmp; } return names; } typedef struct { char *name; int size; int scale; } CreateFadedIconData; static CoglTexture * create_faded_icon_cpu (StTextureCache *cache, const char *key, void *datap, GError **error) { CreateFadedIconData *data = datap; char *name; GdkPixbuf *pixbuf; int size; CoglTexture *texture; gint width, height, rowstride; guint8 n_channels; gboolean have_alpha; gint fade_start; gint fade_range; guint i, j; guint pixbuf_byte_size; guint8 *orig_pixels; guint8 *pixels; GIcon *icon; GtkIconInfo *info; gint scale; name = data->name; size = data->size; scale = data->scale; info = NULL; icon = g_themed_icon_new_with_default_fallbacks (name); if (icon != NULL) { info = gtk_icon_theme_lookup_by_gicon_for_scale (gtk_icon_theme_get_default (), icon, size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); } if (info == NULL) { icon = g_themed_icon_new ("application-x-executable"); info = gtk_icon_theme_lookup_by_gicon_for_scale (gtk_icon_theme_get_default (), icon, size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); g_object_unref (icon); } if (info == NULL) return NULL; pixbuf = gtk_icon_info_load_icon (info, NULL); g_object_unref (info); if (pixbuf == NULL) return NULL; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); orig_pixels = gdk_pixbuf_get_pixels (pixbuf); have_alpha = gdk_pixbuf_get_has_alpha (pixbuf); pixbuf_byte_size = (height - 1) * rowstride + + width * ((n_channels * gdk_pixbuf_get_bits_per_sample (pixbuf) + 7) / 8); pixels = g_malloc0 (rowstride * height); memcpy (pixels, orig_pixels, pixbuf_byte_size); fade_start = width / 2; fade_range = width - fade_start; for (i = fade_start; i < width; i++) { for (j = 0; j < height; j++) { guchar *pixel = &pixels[j * rowstride + i * n_channels]; float fade = 1.0 - ((float) i - fade_start) / fade_range; pixel[0] = 0.5 + pixel[0] * fade; pixel[1] = 0.5 + pixel[1] * fade; pixel[2] = 0.5 + pixel[2] * fade; if (have_alpha) pixel[3] = 0.5 + pixel[3] * fade; } } texture = st_cogl_texture_new_from_data_wrapper (width, height, COGL_TEXTURE_NONE, have_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, pixels); g_free (pixels); g_object_unref (pixbuf); return texture; } /** * st_texture_cache_load_icon_name: * @cache: The texture cache instance * @theme_node: (allow-none): a #StThemeNode * @name: Name of a themed icon * @icon_type: the type of icon to load * @size: Size of themed * * Load a themed icon into a texture. See the #StIconType documentation * for an explanation of how @icon_type affects the returned icon. The * colors used for symbolic icons are derived from @theme_node. * * Return Value: (transfer none): A new #ClutterTexture for the icon */ ClutterActor * st_texture_cache_load_icon_name (StTextureCache *cache, StThemeNode *theme_node, const char *name, StIconType icon_type, gint size) { ClutterActor *texture; CoglTexture *cogltexture; GIcon *themed; char **names; char *cache_key; CreateFadedIconData data; g_return_val_if_fail (!(icon_type == ST_ICON_SYMBOLIC && theme_node == NULL), NULL); switch (icon_type) { case ST_ICON_APPLICATION: themed = g_themed_icon_new (name); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); if (texture == NULL) { themed = g_themed_icon_new ("application-x-executable"); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); } return CLUTTER_ACTOR (texture); break; case ST_ICON_DOCUMENT: themed = g_themed_icon_new (name); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); if (texture == NULL) { themed = g_themed_icon_new ("x-office-document"); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); } return CLUTTER_ACTOR (texture); break; case ST_ICON_SYMBOLIC: names = symbolic_names_for_icon (name); themed = g_themed_icon_new_from_names (names, -1); g_strfreev (names); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, st_theme_node_get_icon_colors (theme_node)); g_object_unref (themed); return CLUTTER_ACTOR (texture); break; case ST_ICON_FULLCOLOR: themed = g_themed_icon_new_with_default_fallbacks (name); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); if (texture == NULL) { themed = g_themed_icon_new ("image-missing"); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); } return CLUTTER_ACTOR (texture); break; case ST_ICON_FADED: themed = g_themed_icon_new_with_default_fallbacks (name); cache_key = g_strdup_printf ("faded-icon:%s,size=%d,scale=%f", name, size, cache->priv->scale); data.name = g_strdup (name); data.size = size; data.scale = cache->priv->scale; cogltexture = st_texture_cache_load (st_texture_cache_get_default (), cache_key, ST_TEXTURE_CACHE_POLICY_FOREVER, create_faded_icon_cpu, &data, NULL); g_free (data.name); g_free (cache_key); if (cogltexture != NULL) { texture = clutter_texture_new (); clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture), cogltexture); } else { texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); if (texture == NULL) { themed = g_themed_icon_new ("image-missing"); texture = load_gicon_with_colors (cache, themed, size, cache->priv->scale, NULL); g_object_unref (themed); } } return CLUTTER_ACTOR (texture); break; default: g_assert_not_reached (); } } /** * st_texture_cache_load_uri_async: * @cache: The texture cache instance * @uri: uri of the image file from which to create a pixbuf * @available_width: available width for the image, can be -1 if not limited * @available_height: available height for the image, can be -1 if not limited * * Asynchronously load an image. Initially, the returned texture will have a natural * size of zero. At some later point, either the image will be loaded successfully * and at that point size will be negotiated, or upon an error, no image will be set. * * Return value: (transfer none): A new #ClutterActor with no image loaded initially. */ ClutterActor * st_texture_cache_load_uri_async (StTextureCache *cache, const gchar *uri, int available_width, int available_height) { ClutterActor *texture; AsyncTextureLoadData *request; StTextureCachePolicy policy; gchar *key; int width = available_width == -1 ? -1 : available_width * cache->priv->scale; int height = available_height == -1 ? -1 : available_height * cache->priv->scale; key = g_strconcat (CACHE_PREFIX_URI, uri, NULL); policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */ texture = (ClutterActor *) create_default_texture (); if (ensure_request (cache, key, policy, &request, texture)) { /* If there's an outstanding request, we've just added ourselves to it */ g_free (key); } else { /* Else, make a new request */ request->cache = cache; /* Transfer ownership of key */ request->key = key; request->uri = g_strdup (uri); request->policy = policy; request->width = width; request->height = height; load_texture_async (cache, request); } ensure_monitor_for_uri (cache, uri); return CLUTTER_ACTOR (texture); } static CoglTexture * st_texture_cache_load_uri_sync_to_cogl_texture (StTextureCache *cache, StTextureCachePolicy policy, const gchar *uri, int available_width, int available_height, GError **error) { CoglTexture *texdata; GdkPixbuf *pixbuf; char *key; int width = available_width == -1 ? -1 : available_width * cache->priv->scale; int height = available_height == -1 ? -1 : available_height * cache->priv->scale; key = g_strconcat (CACHE_PREFIX_URI, uri, NULL); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata == NULL) { pixbuf = impl_load_pixbuf_file (uri, width, height, error); if (!pixbuf) goto out; texdata = pixbuf_to_cogl_texture (pixbuf, FALSE); g_object_unref (pixbuf); if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER) { cogl_object_ref (texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); } } else cogl_object_ref (texdata); ensure_monitor_for_uri (cache, uri); out: g_free (key); return texdata; } static cairo_surface_t * st_texture_cache_load_uri_sync_to_cairo_surface (StTextureCache *cache, StTextureCachePolicy policy, const gchar *uri, int available_width, int available_height, GError **error) { cairo_surface_t *surface; GdkPixbuf *pixbuf; char *key; int width = available_width == -1 ? -1 : available_width * cache->priv->scale; int height = available_height == -1 ? -1 : available_height * cache->priv->scale; key = g_strconcat (CACHE_PREFIX_URI_FOR_CAIRO, uri, NULL); surface = g_hash_table_lookup (cache->priv->keyed_cache, key); if (surface == NULL) { pixbuf = impl_load_pixbuf_file (uri, width, height, error); if (!pixbuf) goto out; surface = pixbuf_to_cairo_surface (pixbuf); g_object_unref (pixbuf); if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER) { cairo_surface_reference (surface); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), surface); } } else cairo_surface_reference (surface); ensure_monitor_for_uri (cache, uri); out: g_free (key); return surface; } /** * st_texture_cache_load_uri_sync: * @cache: The texture cache instance * @policy: Requested lifecycle of cached data * @uri: uri of the image file from which to create a pixbuf * @available_width: available width for the image, can be -1 if not limited * @available_height: available height for the image, can be -1 if not limited * @error: Return location for error * * Synchronously load an image from a uri. The image is scaled down to fit the * available width and height imensions, but the image is never scaled up beyond * its actual size. The pixbuf is rotated according to the associated orientation * setting. * * Return value: (transfer none): A new #ClutterActor with the image file loaded if it was * generated succesfully, %NULL otherwise */ ClutterActor * st_texture_cache_load_uri_sync (StTextureCache *cache, StTextureCachePolicy policy, const gchar *uri, int available_width, int available_height, GError **error) { CoglTexture *texdata; ClutterTexture *texture; texdata = st_texture_cache_load_uri_sync_to_cogl_texture (cache, policy, uri, available_width, available_height, error); if (texdata == NULL) return NULL; texture = create_default_texture (); set_texture_cogl_texture (texture, texdata); cogl_object_unref (texdata); return CLUTTER_ACTOR (texture); } /** * st_texture_cache_load_file_to_cogl_texture: (skip) * @cache: A #StTextureCache * @file_path: Path to a file in supported image format * * This function synchronously loads the given file path * into a COGL texture. On error, a warning is emitted * and %NULL is returned. * * Returns: (transfer full): a new #CoglTexture */ CoglTexture * st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, const gchar *file_path) { CoglTexture *texture; GFile *file; char *uri; GError *error = NULL; file = g_file_new_for_path (file_path); uri = g_file_get_uri (file); texture = st_texture_cache_load_uri_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, uri, -1, -1, &error); g_object_unref (file); g_free (uri); if (texture == NULL) { g_warning ("Failed to load %s: %s", file_path, error->message); g_clear_error (&error); return NULL; } return texture; } /** * st_texture_cache_load_file_to_cairo_surface: * @cache: A #StTextureCache * @file_path: Path to a file in supported image format * * This function synchronously loads the given file path * into a cairo surface. On error, a warning is emitted * and %NULL is returned. * * Returns: (transfer full): a new #cairo_surface_t */ cairo_surface_t * st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, const gchar *file_path) { cairo_surface_t *surface; GFile *file; char *uri; GError *error = NULL; file = g_file_new_for_path (file_path); uri = g_file_get_uri (file); surface = st_texture_cache_load_uri_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, uri, -1, -1, &error); g_object_unref (file); g_free (uri); if (surface == NULL) { g_warning ("Failed to load %s: %s", file_path, error->message); g_clear_error (&error); return NULL; } return surface; } /** * st_texture_cache_load_from_raw: * @cache: a #StTextureCache * @data: (array length=len): raw pixel data * @len: the length of @data * @has_alpha: whether @data includes an alpha channel * @width: width in pixels of @data * @height: width in pixels of @data * @rowstride: rowstride of @data * @size: size of icon to return * * Creates (or retrieves from cache) an icon based on raw pixel data. * * Return value: (transfer none): a new #ClutterActor displaying a * pixbuf created from @data and the other parameters. **/ ClutterActor * st_texture_cache_load_from_raw (StTextureCache *cache, const guchar *data, gsize len, gboolean has_alpha, int width, int height, int rowstride, int size, GError **error) { ClutterTexture *texture; CoglTexture *texdata; char *key; char *checksum; texture = create_default_texture (); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); /* In theory, two images of with different width and height could have the same * pixel data and thus hash the same. (Say, a 16x16 and a 8x32 blank image.) * We ignore this for now. If anybody hits this problem they should use * GChecksum directly to compute a checksum including the width and height. */ checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); key = g_strdup_printf (CACHE_PREFIX_RAW_CHECKSUM "checksum=%s", checksum); g_free (checksum); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata == NULL) { texdata = data_to_cogl_texture (data, has_alpha, width, height, rowstride, TRUE); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); } g_free (key); set_texture_cogl_texture (texture, texdata); return CLUTTER_ACTOR (texture); } static StTextureCache *instance = NULL; /** * st_texture_cache_load_file_simple: * @cache: A #StTextureCache * @file_path: Filesystem path * * Synchronously load an image into a texture. The texture will be cached * indefinitely. On error, this function returns an empty texture and prints a warning. * * Returns: (transfer none): A new #ClutterTexture */ ClutterActor * st_texture_cache_load_file_simple (StTextureCache *cache, const gchar *file_path) { GFile *file; char *uri; ClutterActor *texture; GError *error = NULL; file = g_file_new_for_path (file_path); uri = g_file_get_uri (file); texture = st_texture_cache_load_uri_sync (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, uri, -1, -1, &error); if (texture == NULL) { g_warning ("Failed to load %s: %s", file_path, error->message); g_clear_error (&error); texture = clutter_texture_new (); } return texture; } /** * st_texture_cache_get_default: * * Return value: (transfer none): The global texture cache */ StTextureCache* st_texture_cache_get_default (void) { if (instance == NULL) instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL); return instance; } Cinnamon-2.8.6/src/st/st-entry.h0000664000175000017500000000623212625666430015405 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-entry.h: Plain entry actor * * Copyright 2008, 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_ENTRY_H__ #define __ST_ENTRY_H__ G_BEGIN_DECLS #include #define ST_TYPE_ENTRY (st_entry_get_type ()) #define ST_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ENTRY, StEntry)) #define ST_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ENTRY)) #define ST_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ENTRY, StEntryClass)) #define ST_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ENTRY)) #define ST_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ENTRY, StEntryClass)) typedef struct _StEntry StEntry; typedef struct _StEntryPrivate StEntryPrivate; typedef struct _StEntryClass StEntryClass; /** * StEntry: * * The contents of this structure is private and should only be accessed using * the provided API. */ struct _StEntry { /*< private >*/ StWidget parent_instance; StEntryPrivate *priv; }; struct _StEntryClass { StWidgetClass parent_class; /* signals */ void (*primary_icon_clicked) (StEntry *entry); void (*secondary_icon_clicked) (StEntry *entry); }; GType st_entry_get_type (void) G_GNUC_CONST; StWidget * st_entry_new (const gchar *text); const gchar * st_entry_get_text (StEntry *entry); void st_entry_set_text (StEntry *entry, const gchar *text); ClutterActor* st_entry_get_clutter_text (StEntry *entry); void st_entry_set_hint_text (StEntry *entry, const gchar *text); const gchar * st_entry_get_hint_text (StEntry *entry); void st_entry_set_primary_icon (StEntry *entry, ClutterActor *icon); void st_entry_set_secondary_icon (StEntry *entry, ClutterActor *icon); void st_entry_set_primary_icon_from_file (StEntry *entry, const gchar *filename); void st_entry_set_secondary_icon_from_file (StEntry *entry, const gchar *filename); G_END_DECLS #endif /* __ST_ENTRY_H__ */ Cinnamon-2.8.6/src/st/st-enum-types.h.in0000664000175000017500000000115612625666430016757 0ustar fabiofabio/*** BEGIN file-header ***/ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #ifndef __ST_ENUM_TYPES_H__ #define __ST_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__ST_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define ST_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ Cinnamon-2.8.6/src/st/st-border-image.c0000664000175000017500000000710412625666430016573 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-border-image.c: store information about an image with borders * * Copyright 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "st-border-image.h" struct _StBorderImage { GObject parent; char *filename; int border_top; int border_right; int border_bottom; int border_left; }; struct _StBorderImageClass { GObjectClass parent_class; }; G_DEFINE_TYPE (StBorderImage, st_border_image, G_TYPE_OBJECT) static void st_border_image_finalize (GObject *object) { StBorderImage *image = ST_BORDER_IMAGE (object); g_free (image->filename); G_OBJECT_CLASS (st_border_image_parent_class)->finalize (object); } static void st_border_image_class_init (StBorderImageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = st_border_image_finalize; } static void st_border_image_init (StBorderImage *image) { } StBorderImage * st_border_image_new (const char *filename, int border_top, int border_right, int border_bottom, int border_left) { StBorderImage *image; image = g_object_new (ST_TYPE_BORDER_IMAGE, NULL); image->filename = g_strdup (filename); image->border_top = border_top; image->border_right = border_right; image->border_bottom = border_bottom; image->border_left = border_left; return image; } const char * st_border_image_get_filename (StBorderImage *image) { g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), NULL); return image->filename; } void st_border_image_get_borders (StBorderImage *image, int *border_top, int *border_right, int *border_bottom, int *border_left) { g_return_if_fail (ST_IS_BORDER_IMAGE (image)); if (border_top) *border_top = image->border_top; if (border_right) *border_right = image->border_right; if (border_bottom) *border_bottom = image->border_bottom; if (border_left) *border_left = image->border_left; } /** * st_border_image_equal: * @image: a #StBorder_Image * @other: a different #StBorder_Image * * Check if two border_image objects are identical. * * Return value: %TRUE if the two border image objects are identical */ gboolean st_border_image_equal (StBorderImage *image, StBorderImage *other) { g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), FALSE); g_return_val_if_fail (ST_IS_BORDER_IMAGE (other), FALSE); return (image->border_top == other->border_top && image->border_right == other->border_right && image->border_bottom == other->border_bottom && image->border_left == other->border_left && strcmp (image->filename, other->filename) == 0); } Cinnamon-2.8.6/src/st/st-theme-node-transition.h0000664000175000017500000000615212625666430020462 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node-transition.h: Theme node transitions for StWidget. * * Copyright 2010 Florian Müllner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_NODE_TRANSITION_H__ #define __ST_THEME_NODE_TRANSITION_H__ #include #include "st-widget.h" #include "st-theme-node.h" G_BEGIN_DECLS #define ST_TYPE_THEME_NODE_TRANSITION (st_theme_node_transition_get_type ()) #define ST_THEME_NODE_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransition)) #define ST_IS_THEME_NODE_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_THEME_NODE_TRANSITION)) #define ST_THEME_NODE_TRANSITION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionClass)) #define ST_IS_THEME_NODE_TRANSITION_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), ST_TYPE_THEME_NODE_TRANSITION)) #define ST_THEME_NODE_TRANSITION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ST_THEME_NODE_TRANSITION, StThemeNodeTransitionClass)) typedef struct _StThemeNodeTransition StThemeNodeTransition; typedef struct _StThemeNodeTransitionClass StThemeNodeTransitionClass; typedef struct _StThemeNodeTransitionPrivate StThemeNodeTransitionPrivate; struct _StThemeNodeTransition { GObject parent; StThemeNodeTransitionPrivate *priv; }; struct _StThemeNodeTransitionClass { GObjectClass parent_class; void (*completed) (StThemeNodeTransition *transition); void (*new_frame) (StThemeNodeTransition *transition); }; GType st_theme_node_transition_get_type (void) G_GNUC_CONST; StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node, StThemeNode *to_node, guint duration); void st_theme_node_transition_update (StThemeNodeTransition *transition, StThemeNode *new_node); void st_theme_node_transition_paint (StThemeNodeTransition *transition, ClutterActorBox *allocation, guint8 paint_opacity); void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, const ClutterActorBox *allocation, ClutterActorBox *paint_box); G_END_DECLS #endif Cinnamon-2.8.6/src/st/st-group.c0000664000175000017500000001771512625666430015403 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-group.c: A fixed layout container based on ClutterGroup * * Copyright 2010 Florian Müllner * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * SECTION:st-group * SECTION:clutter-group * @short_description: A fixed layout container * * A #StGroup is an Actor which contains multiple child actors positioned * relative to the #StGroup position. Other operations such as scaling, * rotating and clipping of the group will apply to the child actors. * * A #StGroup's size is defined by the size and position of its children; * it will be the smallest non-negative size that covers the right and bottom * edges of all of its children. * * Setting the size on a Group using #ClutterActor methods like * clutter_actor_set_size() will override the natural size of the Group, * however this will not affect the size of the children and they may still * be painted outside of the allocation of the group. One way to constrain * the visible area of a #StGroup to a specified allocation is to * explicitly set the size of the #StGroup and then use the * #ClutterActor:clip-to-allocation property. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-group.h" #include "st-enum-types.h" #include "st-private.h" G_DEFINE_TYPE (StGroup, st_group, ST_TYPE_CONTAINER); static void st_group_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (st_group_parent_class)->paint (actor); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void st_group_pick (ClutterActor *actor, const ClutterColor *pick) { /* Chain up so we get a bounding box painted (if we are reactive) */ CLUTTER_ACTOR_CLASS (st_group_parent_class)->pick (actor, pick); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void st_group_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble min_width, natural_width; gint css_width, css_min_width, css_max_width; GList *l, *children; css_width = st_theme_node_get_width (node); css_min_width = st_theme_node_get_min_width (node); css_max_width = st_theme_node_get_max_width (node); /* We will always be at least 0 sized (ie, if all of the actors are * to the left of the origin we won't return a negative size) */ min_width = 0; natural_width = 0; children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_x, child_min, child_nat; child_x = clutter_actor_get_x (child); /* for_height is irrelevant to the fixed layout, so it's not used */ _st_actor_get_preferred_width (child, -1, FALSE, &child_min, &child_nat); /* Track the rightmost edge */ if (child_x + child_min > min_width) min_width = child_x + child_min; if (child_x + child_nat > natural_width) natural_width = child_x + child_nat; } /* The size is defined as the distance from the origin to the right-hand * edge of the rightmost actor, unless overridden with min-width */ if (min_width_p) { if (css_min_width != -1) *min_width_p = css_min_width; else *min_width_p = min_width; } if (natural_width_p) { if (css_width != -1) natural_width = css_width; *natural_width_p = MIN (natural_width, css_max_width); } } static void st_group_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble min_height, natural_height; gint css_height, css_min_height, css_max_height; GList *l, *children; css_height = st_theme_node_get_height (node); css_min_height = st_theme_node_get_min_height (node); css_max_height = st_theme_node_get_max_height (node); /* We will always be at least 0 sized (ie, if all of the actors are * above of the origin we won't return a negative size) */ min_height = 0; natural_height = 0; children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_y, child_min, child_nat; child_y = clutter_actor_get_y (child); /* for_width is irrelevant to the fixed layout, so it's not used */ _st_actor_get_preferred_height (child, -1, FALSE, &child_min, &child_nat); /* Track the bottommost edge */ if (child_y + child_min > min_height) min_height = child_y + child_min; if (child_y + child_nat > natural_height) natural_height = child_y + child_nat; } /* The size is defined as the distance from the origin to the right-hand * edge of the rightmost actor, unless overridden with min-height */ if (min_height_p) { if (css_min_height != -1) *min_height_p = css_min_height; else *min_height_p = min_height; } if (natural_height_p) { if (css_height != -1) natural_height = css_height; *natural_height_p = MIN (natural_height, css_max_height); } } static void st_group_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *l, *children; CLUTTER_ACTOR_CLASS (st_group_parent_class)->allocate (actor, box, flags); children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; clutter_actor_allocate_preferred_size (child, flags); } } static void st_group_show_all (ClutterActor *actor) { clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_show), NULL); clutter_actor_show (actor); } static void st_group_hide_all (ClutterActor *actor) { clutter_actor_hide (actor); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_hide), NULL); } static void st_group_class_init (StGroupClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_width = st_group_get_preferred_width; actor_class->get_preferred_height = st_group_get_preferred_height; actor_class->allocate = st_group_allocate; actor_class->paint = st_group_paint; actor_class->pick = st_group_pick; actor_class->show_all = st_group_show_all; actor_class->hide_all = st_group_hide_all; } static void st_group_init (StGroup *self) { } /** * st_group_new: * * Create a new #StGroup. * * Return value: the newly created #StGroup actor */ StWidget * st_group_new (void) { return g_object_new (ST_TYPE_GROUP, NULL); } Cinnamon-2.8.6/src/st/st-theme-node-transition.c0000664000175000017500000003716412625666430020464 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-node-transition.c: Theme node transitions for StWidget. * * Copyright 2010 Florian Müllner * Copyright 2010 Adel Gadllah * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "st-theme-node-transition.h" #include "st-cogl-wrapper.h" enum { COMPLETED, NEW_FRAME, LAST_SIGNAL }; #define ST_THEME_NODE_TRANSITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionPrivate)) struct _StThemeNodeTransitionPrivate { StThemeNode *old_theme_node; StThemeNode *new_theme_node; CoglHandle old_texture; CoglHandle new_texture; CoglHandle old_offscreen; CoglHandle new_offscreen; CoglHandle material; ClutterAlpha *alpha; ClutterTimeline *timeline; guint timeline_completed_id; guint timeline_new_frame_id; ClutterActorBox last_allocation; ClutterActorBox offscreen_box; gboolean needs_setup; }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (StThemeNodeTransition, st_theme_node_transition, G_TYPE_OBJECT); static void on_timeline_completed (ClutterTimeline *timeline, StThemeNodeTransition *transition) { g_signal_emit (transition, signals[COMPLETED], 0); } static void on_timeline_new_frame (ClutterTimeline *timeline, gint frame_num, StThemeNodeTransition *transition) { g_signal_emit (transition, signals[NEW_FRAME], 0); } StThemeNodeTransition * st_theme_node_transition_new (StThemeNode *from_node, StThemeNode *to_node, guint duration) { StThemeNodeTransition *transition; g_return_val_if_fail (ST_IS_THEME_NODE (from_node), NULL); g_return_val_if_fail (ST_IS_THEME_NODE (to_node), NULL); duration = st_theme_node_get_transition_duration (to_node); transition = g_object_new (ST_TYPE_THEME_NODE_TRANSITION, NULL); transition->priv->old_theme_node = g_object_ref (from_node); transition->priv->new_theme_node = g_object_ref (to_node); transition->priv->alpha = clutter_alpha_new (); transition->priv->timeline = clutter_timeline_new (duration); transition->priv->timeline_completed_id = g_signal_connect (transition->priv->timeline, "completed", G_CALLBACK (on_timeline_completed), transition); transition->priv->timeline_new_frame_id = g_signal_connect (transition->priv->timeline, "new-frame", G_CALLBACK (on_timeline_new_frame), transition); clutter_alpha_set_mode (transition->priv->alpha, CLUTTER_EASE_IN_OUT_QUAD); clutter_alpha_set_timeline (transition->priv->alpha, transition->priv->timeline); clutter_timeline_start (transition->priv->timeline); return transition; } void st_theme_node_transition_update (StThemeNodeTransition *transition, StThemeNode *new_node) { StThemeNodeTransitionPrivate *priv = transition->priv; StThemeNode *old_node; ClutterTimelineDirection direction; g_return_if_fail (ST_IS_THEME_NODE_TRANSITION (transition)); g_return_if_fail (ST_IS_THEME_NODE (new_node)); direction = clutter_timeline_get_direction (priv->timeline); old_node = (direction == CLUTTER_TIMELINE_FORWARD) ? priv->old_theme_node : priv->new_theme_node; /* If the update is the reversal of the current transition, * we reverse the timeline. * Otherwise, we should initiate a new transition from the * current state to the new one; this is hard to do if the * transition is in an intermediate state, so we just cancel * the ongoing transition in that case. * Note that reversing a timeline before any time elapsed * results in the timeline's time position being set to the * full duration - this is not what we want, so we cancel the * transition as well in that case. */ if (st_theme_node_equal (new_node, old_node)) { if (clutter_timeline_get_elapsed_time (priv->timeline) > 0) { if (direction == CLUTTER_TIMELINE_FORWARD) clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_BACKWARD); else clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_FORWARD); } else { clutter_timeline_stop (priv->timeline); g_signal_emit (transition, signals[COMPLETED], 0); } } else { if (clutter_timeline_get_elapsed_time (priv->timeline) > 0) { clutter_timeline_stop (priv->timeline); g_signal_emit (transition, signals[COMPLETED], 0); } else { guint new_duration = st_theme_node_get_transition_duration (new_node); clutter_timeline_set_duration (priv->timeline, new_duration); /* If the change doesn't affect painting, we don't need to redraw, * but we still need to replace the node so that we properly share * caching with the painting that happens after the transition finishes. */ if (!st_theme_node_paint_equal (priv->new_theme_node, new_node)) priv->needs_setup = TRUE; g_object_unref (priv->new_theme_node); priv->new_theme_node = g_object_ref (new_node); } } } static void calculate_offscreen_box (StThemeNodeTransition *transition, const ClutterActorBox *allocation) { ClutterActorBox paint_box; st_theme_node_transition_get_paint_box (transition, allocation, &paint_box); transition->priv->offscreen_box.x1 = paint_box.x1 - allocation->x1; transition->priv->offscreen_box.y1 = paint_box.y1 - allocation->y1; transition->priv->offscreen_box.x2 = paint_box.x2 - allocation->x1; transition->priv->offscreen_box.y2 = paint_box.y2 - allocation->y1; } void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, const ClutterActorBox *allocation, ClutterActorBox *paint_box) { StThemeNodeTransitionPrivate *priv = transition->priv; ClutterActorBox old_node_box, new_node_box; st_theme_node_get_paint_box (priv->old_theme_node, allocation, &old_node_box); st_theme_node_get_paint_box (priv->new_theme_node, allocation, &new_node_box); paint_box->x1 = MIN (old_node_box.x1, new_node_box.x1); paint_box->y1 = MIN (old_node_box.y1, new_node_box.y1); paint_box->x2 = MAX (old_node_box.x2, new_node_box.x2); paint_box->y2 = MAX (old_node_box.y2, new_node_box.y2); } static gboolean setup_framebuffers (StThemeNodeTransition *transition, const ClutterActorBox *allocation) { StThemeNodeTransitionPrivate *priv = transition->priv; CoglColor clear_color = { 0, 0, 0, 0 }; guint width, height; /* template material to avoid unnecessary shader compilation */ static CoglHandle material_template = COGL_INVALID_HANDLE; width = priv->offscreen_box.x2 - priv->offscreen_box.x1; height = priv->offscreen_box.y2 - priv->offscreen_box.y1; g_return_val_if_fail (width > 0, FALSE); g_return_val_if_fail (height > 0, FALSE); if (priv->old_texture) cogl_handle_unref (priv->old_texture); priv->old_texture = st_cogl_texture_new_with_size_wrapper (width, height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); if (priv->new_texture) cogl_handle_unref (priv->new_texture); priv->new_texture = st_cogl_texture_new_with_size_wrapper (width, height, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); g_return_val_if_fail (priv->old_texture != COGL_INVALID_HANDLE, FALSE); g_return_val_if_fail (priv->new_texture != COGL_INVALID_HANDLE, FALSE); if (priv->old_offscreen) cogl_handle_unref (priv->old_offscreen); priv->old_offscreen = cogl_offscreen_new_to_texture (priv->old_texture); if (priv->new_offscreen) cogl_handle_unref (priv->new_offscreen); priv->new_offscreen = cogl_offscreen_new_to_texture (priv->new_texture); g_return_val_if_fail (priv->old_offscreen != COGL_INVALID_HANDLE, FALSE); g_return_val_if_fail (priv->new_offscreen != COGL_INVALID_HANDLE, FALSE); if (priv->material == NULL) { if (G_UNLIKELY (material_template == COGL_INVALID_HANDLE)) { material_template = cogl_material_new (); cogl_material_set_layer_combine (material_template, 0, "RGBA = REPLACE (TEXTURE)", NULL); cogl_material_set_layer_combine (material_template, 1, "RGBA = INTERPOLATE (PREVIOUS, " "TEXTURE, " "CONSTANT[A])", NULL); cogl_material_set_layer_combine (material_template, 2, "RGBA = MODULATE (PREVIOUS, " "PRIMARY)", NULL); } priv->material = cogl_material_copy (material_template); } cogl_material_set_layer (priv->material, 0, priv->new_texture); cogl_material_set_layer (priv->material, 1, priv->old_texture); cogl_push_framebuffer (priv->old_offscreen); cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2, priv->offscreen_box.y2, priv->offscreen_box.y1, 0.0, 1.0); st_theme_node_paint (priv->old_theme_node, allocation, 255); cogl_pop_framebuffer (); cogl_push_framebuffer (priv->new_offscreen); cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR); cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2, priv->offscreen_box.y2, priv->offscreen_box.y1, 0.0, 1.0); st_theme_node_paint (priv->new_theme_node, allocation, 255); cogl_pop_framebuffer (); return TRUE; } void st_theme_node_transition_paint (StThemeNodeTransition *transition, ClutterActorBox *allocation, guint8 paint_opacity) { StThemeNodeTransitionPrivate *priv = transition->priv; CoglColor constant; float tex_coords[] = { 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, }; g_return_if_fail (ST_IS_THEME_NODE (priv->old_theme_node)); g_return_if_fail (ST_IS_THEME_NODE (priv->new_theme_node)); if (!clutter_actor_box_equal (allocation, &priv->last_allocation)) priv->needs_setup = TRUE; if (priv->needs_setup) { priv->last_allocation = *allocation; calculate_offscreen_box (transition, allocation); priv->needs_setup = !setup_framebuffers (transition, allocation); if (priv->needs_setup) /* setting up framebuffers failed */ return; } cogl_color_set_from_4f (&constant, 0., 0., 0., clutter_alpha_get_alpha (priv->alpha)); cogl_material_set_layer_combine_constant (priv->material, 1, &constant); cogl_material_set_color4ub (priv->material, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_set_source (priv->material); cogl_rectangle_with_multitexture_coords (priv->offscreen_box.x1, priv->offscreen_box.y1, priv->offscreen_box.x2, priv->offscreen_box.y2, tex_coords, 8); } static void st_theme_node_transition_dispose (GObject *object) { StThemeNodeTransitionPrivate *priv = ST_THEME_NODE_TRANSITION (object)->priv; if (priv->old_theme_node) { g_object_unref (priv->old_theme_node); priv->old_theme_node = NULL; } if (priv->new_theme_node) { g_object_unref (priv->new_theme_node); priv->new_theme_node = NULL; } if (priv->old_texture) { cogl_handle_unref (priv->old_texture); priv->old_texture = NULL; } if (priv->new_texture) { cogl_handle_unref (priv->new_texture); priv->new_texture = NULL; } if (priv->old_offscreen) { cogl_handle_unref (priv->old_offscreen); priv->old_offscreen = NULL; } if (priv->new_offscreen) { cogl_handle_unref (priv->new_offscreen); priv->new_offscreen = NULL; } if (priv->material) { cogl_handle_unref (priv->material); priv->material = NULL; } if (priv->timeline) { if (priv->timeline_completed_id != 0) g_signal_handler_disconnect (priv->timeline, priv->timeline_completed_id); if (priv->timeline_new_frame_id != 0) g_signal_handler_disconnect (priv->timeline, priv->timeline_new_frame_id); g_object_unref (priv->timeline); priv->timeline = NULL; } priv->timeline_completed_id = 0; priv->timeline_new_frame_id = 0; if (priv->alpha) { g_object_unref (priv->alpha); priv->alpha = NULL; } G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object); } static void st_theme_node_transition_init (StThemeNodeTransition *transition) { transition->priv = ST_THEME_NODE_TRANSITION_GET_PRIVATE (transition); transition->priv->old_theme_node = NULL; transition->priv->new_theme_node = NULL; transition->priv->old_texture = NULL; transition->priv->new_texture = NULL; transition->priv->old_offscreen = NULL; transition->priv->new_offscreen = NULL; transition->priv->needs_setup = TRUE; transition->priv->alpha = NULL; } static void st_theme_node_transition_class_init (StThemeNodeTransitionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (StThemeNodeTransitionPrivate)); object_class->dispose = st_theme_node_transition_dispose; signals[COMPLETED] = g_signal_new ("completed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StThemeNodeTransitionClass, completed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[NEW_FRAME] = g_signal_new ("new-frame", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (StThemeNodeTransitionClass, new_frame), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } Cinnamon-2.8.6/src/st/st-types.h0000664000175000017500000000273412625666430015413 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_TYPES_H__ #define __ST_TYPES_H__ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif #include #include #include G_BEGIN_DECLS /** * SECTION:st-types * @short_description: type definitions used throughout St * * Common types for StWidgets. */ typedef enum { ST_ALIGN_START, ST_ALIGN_MIDDLE, ST_ALIGN_END } StAlign; typedef enum { ST_ICON_SYMBOLIC, ST_ICON_FULLCOLOR, ST_ICON_APPLICATION, ST_ICON_DOCUMENT, ST_ICON_FADED } StIconType; typedef enum { ST_BACKGROUND_SIZE_AUTO, ST_BACKGROUND_SIZE_CONTAIN, ST_BACKGROUND_SIZE_COVER, ST_BACKGROUND_SIZE_FIXED } StBackgroundSize; G_END_DECLS #endif /* __ST_TYPES_H__ */ Cinnamon-2.8.6/src/st/st-theme-private.h0000664000175000017500000000305412625666430017015 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-theme-private.h: Private StThemeMethods * * Copyright 2008, 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef __ST_THEME_PRIVATE_H__ #define __ST_THEME_PRIVATE_H__ #include #include "st-theme.h" G_BEGIN_DECLS GPtrArray *_st_theme_get_matched_properties (StTheme *theme, StThemeNode *node); GPtrArray *_st_theme_get_matched_properties_fallback (StTheme *theme, StThemeNode *node); /* Resolve an URL from the stylesheet to a filename */ char *_st_theme_resolve_url (StTheme *theme, CRStyleSheet *base_stylesheet, const char *url); CRDeclaration *_st_theme_parse_declaration_list (const char *str); G_END_DECLS #endif /* __ST_THEME_PRIVATE_H__ */ Cinnamon-2.8.6/src/cinnamon-xfixes-cursor.h0000664000175000017500000000350612625666430017614 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_XFIXES_CURSOR_H__ #define __CINNAMON_XFIXES_CURSOR_H__ #include #include G_BEGIN_DECLS typedef struct _CinnamonXFixesCursor CinnamonXFixesCursor; typedef struct _CinnamonXFixesCursorClass CinnamonXFixesCursorClass; #define CINNAMON_TYPE_XFIXES_CURSOR (cinnamon_xfixes_cursor_get_type ()) #define CINNAMON_XFIXES_CURSOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_XFIXES_CURSOR, CinnamonXFixesCursor)) #define CINNAMON_XFIXES_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_XFIXES_CURSOR, CinnamonXFixesCursorClass)) #define CINNAMON_IS_XFIXES_CURSOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_XFIXES_CURSOR)) #define CINNAMON_IS_XFIXES_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_XFIXES_CURSOR)) #define CINNAMON_XFIXES_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_XFIXES_CURSOR, CinnamonXFixesCursorClass)) GType cinnamon_xfixes_cursor_get_type (void) G_GNUC_CONST; CinnamonXFixesCursor *cinnamon_xfixes_cursor_get_for_stage (ClutterStage *stage); void cinnamon_xfixes_cursor_show (CinnamonXFixesCursor *xfixes_cursor); void cinnamon_xfixes_cursor_hide (CinnamonXFixesCursor *xfixes_cursor); int cinnamon_xfixes_cursor_get_hot_x (CinnamonXFixesCursor *xfixes_cursor); int cinnamon_xfixes_cursor_get_hot_y (CinnamonXFixesCursor *xfixes_cursor); void cinnamon_xfixes_cursor_update_texture_image (CinnamonXFixesCursor *xfixes_cursor, ClutterTexture *texture); G_END_DECLS #endif /* __CINNAMON_XFIXES_CURSOR_H__ */ Cinnamon-2.8.6/src/hotplug-sniffer/0000775000175000017500000000000012625666430016132 5ustar fabiofabioCinnamon-2.8.6/src/hotplug-sniffer/hotplug-mimetypes.h0000664000175000017500000000571412625666430022006 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __HOTPLUG_MIMETYPES_H__ #define __HOTPLUG_MIMETYPES_H__ #include G_GNUC_UNUSED static const gchar *docs_mimetypes[] = { "application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.presentation", "application/vnd.oasis.opendocument.spreadsheet", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/rtf", "application/pdf", "application/x-bzpdf", "application/x-gzpdf", "application/x-xzpdf", "application/postscript", "application/x-bzpostscript", "application/x-gzpostscript", "image/x-eps", "image/x-bzeps", "image/x-gzeps", "application/x-dvi", "application/x-bzdvi", "application/x-gzdvi", "image/vnd.djvu", "application/x-cbr", "application/x-cbz", "application/x-cb7", "application/x-cbt", NULL }; G_GNUC_UNUSED static const gchar *video_mimetypes[] = { "application/mxf", "application/ogg", "application/ram", "application/sdp", "application/vnd.ms-wpl", "application/vnd.rn-realmedia", "application/x-extension-m4a", "application/x-extension-mp4", "application/x-flash-video", "application/x-matroska", "application/x-netshow-channel", "application/x-ogg", "application/x-quicktimeplayer", "application/x-shorten", "image/vnd.rn-realpix", "image/x-pict", "misc/ultravox", "text/x-google-video-pointer", "video/3gpp", "video/dv", "video/fli", "video/flv", "video/mp2t", "video/mp4", "video/mp4v-es", "video/mpeg", "video/msvideo", "video/ogg", "video/quicktime", "video/vivo", "video/vnd.divx", "video/vnd.rn-realvideo", "video/vnd.vivo", "video/webm", "video/x-anim", "video/x-avi", "video/x-flc", "video/x-fli", "video/x-flic", "video/x-flv", "video/x-m4v", "video/x-matroska", "video/x-mpeg", "video/x-ms-asf", "video/x-ms-asx", "video/x-msvideo", "video/x-ms-wm", "video/x-ms-wmv", "video/x-ms-wmx", "video/x-ms-wvx", "video/x-nsv", "video/x-ogm+ogg", "video/x-theora+ogg", "video/x-totem-stream", NULL }; G_GNUC_UNUSED static const gchar *audio_mimetypes[] = { "audio/3gpp", "audio/ac3", "audio/AMR", "audio/AMR-WB", "audio/basic", "audio/flac", "audio/midi", "audio/mp2", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/prs.sid", "audio/vnd.rn-realaudio", "audio/x-aiff", "audio/x-ape", "audio/x-flac", "audio/x-gsm", "audio/x-it", "audio/x-m4a", "audio/x-matroska", "audio/x-mod", "audio/x-mp3", "audio/x-mpeg", "audio/x-ms-asf", "audio/x-ms-asx", "audio/x-ms-wax", "audio/x-ms-wma", "audio/x-musepack", "audio/x-pn-aiff", "audio/x-pn-au", "audio/x-pn-wav", "audio/x-pn-windows-acm", "audio/x-realaudio", "audio/x-real-audio", "audio/x-sbc", "audio/x-speex", "audio/x-tta", "audio/x-wav", "audio/x-wavpack", "audio/x-vorbis", "audio/x-vorbis+ogg", "audio/x-xm", NULL }; #endif /* __HOTPLUG_MIMETYPES_H__ */ Cinnamon-2.8.6/src/hotplug-sniffer/hotplug-sniffer.c0000664000175000017500000001720612625666430021420 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Authors: David Zeuthen * Cosimo Cecchi * */ #include "cinnamon-mime-sniffer.h" #include "hotplug-mimetypes.h" /* Set the environment variable HOTPLUG_SNIFFER_DEBUG to show debug */ static void print_debug (const gchar *str, ...); #define BUS_NAME "org.Cinnamon.HotplugSniffer" #define AUTOQUIT_TIMEOUT 5 static const gchar introspection_xml[] = "" " " " " " " " " " " " " ""; static GDBusNodeInfo *introspection_data = NULL; static GMainLoop *loop = NULL; static guint autoquit_id = 0; static gboolean autoquit_timeout_cb (gpointer _unused) { print_debug ("Timeout reached, quitting..."); autoquit_id = 0; g_main_loop_quit (loop); return FALSE; } static void ensure_autoquit_off (void) { if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL) return; if (autoquit_id != 0) { g_source_remove (autoquit_id); autoquit_id = 0; } } static void ensure_autoquit_on (void) { if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL) return; autoquit_id = g_timeout_add_seconds (AUTOQUIT_TIMEOUT, autoquit_timeout_cb, NULL); } typedef struct { GVariant *parameters; GDBusMethodInvocation *invocation; } InvocationData; static InvocationData * invocation_data_new (GVariant *params, GDBusMethodInvocation *invocation) { InvocationData *ret; ret = g_slice_new0 (InvocationData); ret->parameters = g_variant_ref (params); ret->invocation = g_object_ref (invocation); return ret; } static void invocation_data_free (InvocationData *data) { g_variant_unref (data->parameters); g_clear_object (&data->invocation); g_slice_free (InvocationData, data); } static void sniff_async_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { InvocationData *data = user_data; gchar **types; GError *error = NULL; types = cinnamon_mime_sniffer_sniff_finish (CINNAMON_MIME_SNIFFER (source), res, &error); if (error != NULL) { g_dbus_method_invocation_return_gerror (data->invocation, error); g_error_free (error); goto out; } g_dbus_method_invocation_return_value (data->invocation, g_variant_new ("(^as)", types)); g_strfreev (types); out: invocation_data_free (data); ensure_autoquit_on (); } static void handle_sniff_uri (InvocationData *data) { CinnamonMimeSniffer *sniffer; const gchar *uri; GFile *file; ensure_autoquit_off (); g_variant_get (data->parameters, "(&s)", &uri, NULL); file = g_file_new_for_uri (uri); print_debug ("Initiating sniff for uri %s", uri); sniffer = cinnamon_mime_sniffer_new (file); cinnamon_mime_sniffer_sniff_async (sniffer, sniff_async_ready_cb, data); g_object_unref (sniffer); g_object_unref (file); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { InvocationData *data; data = invocation_data_new (parameters, invocation); if (g_strcmp0 (method_name, "SniffURI") == 0) handle_sniff_uri (data); else g_assert_not_reached (); } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, /* get_property */ NULL, /* set_property */ }; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error = NULL; print_debug ("Connected to the session bus: %s", name); g_dbus_connection_register_object (connection, "/org/Cinnamon/HotplugSniffer", introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &error); if (error != NULL) { g_printerr ("Error exporting object on the session bus: %s", error->message); g_error_free (error); _exit(1); } print_debug ("Object exported on the session bus"); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { print_debug ("Lost bus name: %s, exiting", name); g_main_loop_quit (loop); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { print_debug ("Acquired bus name: %s", name); } int main (int argc, char **argv) { guint name_owner_id; introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); ensure_autoquit_on (); loop = g_main_loop_new (NULL, FALSE); name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, BUS_NAME, 0, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); g_main_loop_run (loop); if (name_owner_id != 0) g_bus_unown_name (name_owner_id); if (loop != NULL) g_main_loop_unref (loop); return 0; } /* ---------------------------------------------------------------------------------------------------- */ static void print_debug (const gchar *format, ...) { gchar *s; va_list ap; gchar timebuf[64]; GTimeVal now; time_t now_t; struct tm broken_down; static volatile gsize once_init_value = 0; static gboolean show_debug = FALSE; static guint pid = 0; if (g_once_init_enter (&once_init_value)) { show_debug = (g_getenv ("HOTPLUG_SNIFFER_DEBUG") != NULL); pid = getpid (); g_once_init_leave (&once_init_value, 1); } if (!show_debug) goto out; g_get_current_time (&now); now_t = now.tv_sec; localtime_r (&now_t, &broken_down); strftime (timebuf, sizeof timebuf, "%H:%M:%S", &broken_down); va_start (ap, format); s = g_strdup_vprintf (format, ap); va_end (ap); g_print ("cinnamon-hotplug-sniffer[%d]: %s.%03d: %s\n", pid, timebuf, (gint) (now.tv_usec / 1000), s); g_free (s); out: ; } Cinnamon-2.8.6/src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in0000664000175000017500000000013712625666430025410 0ustar fabiofabio[D-BUS Service] Name=org.Cinnamon.HotplugSniffer Exec=@pkglibexecdir@/cinnamon-hotplug-sniffer Cinnamon-2.8.6/src/hotplug-sniffer/cinnamon-mime-sniffer.h0000664000175000017500000000516612625666430022474 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Author: Cosimo Cecchi * */ #ifndef __CINNAMON_MIME_SNIFFER_H__ #define __CINNAMON_MIME_SNIFFER_H__ #include #include G_BEGIN_DECLS #define CINNAMON_TYPE_MIME_SNIFFER (cinnamon_mime_sniffer_get_type ()) #define CINNAMON_MIME_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_MIME_SNIFFER, CinnamonMimeSniffer)) #define CINNAMON_IS_MIME_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_MIME_SNIFFER)) #define CINNAMON_MIME_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_MIME_SNIFFER, CinnamonMimeSnifferClass)) #define CINNAMON_IS_MIME_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_MIME_SNIFFER)) #define CINNAMON_MIME_SNIFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_MIME_SNIFFER, CinnamonMimeSnifferClass)) typedef struct _CinnamonMimeSniffer CinnamonMimeSniffer; typedef struct _CinnamonMimeSnifferPrivate CinnamonMimeSnifferPrivate; typedef struct _CinnamonMimeSnifferClass CinnamonMimeSnifferClass; struct _CinnamonMimeSniffer { GObject parent_instance; CinnamonMimeSnifferPrivate *priv; }; struct _CinnamonMimeSnifferClass { GObjectClass parent_class; }; GType cinnamon_mime_sniffer_get_type (void) G_GNUC_CONST; CinnamonMimeSniffer *cinnamon_mime_sniffer_new (GFile *file); void cinnamon_mime_sniffer_sniff_async (CinnamonMimeSniffer *self, GAsyncReadyCallback callback, gpointer user_data); gchar ** cinnamon_mime_sniffer_sniff_finish (CinnamonMimeSniffer *self, GAsyncResult *res, GError **error); G_END_DECLS #endif /* __CINNAMON_MIME_SNIFFER_H__ */ Cinnamon-2.8.6/src/hotplug-sniffer/cinnamon-mime-sniffer.c0000664000175000017500000004111012625666430022454 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 1999, 2000, 2001 Eazel, Inc. * Copyright (C) 2011 Red Hat, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Author: Cosimo Cecchi * * The code for crawling the directory hierarchy is based on * nautilus/libnautilus-private/nautilus-directory-async.c, with * the following copyright and author: * * Copyright (C) 1999, 2000, 2001 Eazel, Inc. * Author: Darin Adler * */ #include "cinnamon-mime-sniffer.h" #include "hotplug-mimetypes.h" #include #include #define LOADER_ATTRS \ G_FILE_ATTRIBUTE_STANDARD_TYPE "," \ G_FILE_ATTRIBUTE_STANDARD_NAME "," \ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," #define WATCHDOG_TIMEOUT 1500 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100 #define HIGH_SCORE_RATIO 0.10 G_DEFINE_TYPE (CinnamonMimeSniffer, cinnamon_mime_sniffer, G_TYPE_OBJECT); enum { PROP_FILE = 1, NUM_PROPERTIES }; static GHashTable *image_type_table = NULL; static GHashTable *audio_type_table = NULL; static GHashTable *video_type_table = NULL; static GHashTable *docs_type_table = NULL; static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; typedef struct { CinnamonMimeSniffer *self; GFile *file; GFileEnumerator *enumerator; GList *deep_count_subdirectories; gint audio_count; gint image_count; gint document_count; gint video_count; gint total_items; } DeepCountState; struct _CinnamonMimeSnifferPrivate { GFile *file; GCancellable *cancellable; guint watchdog_id; GSimpleAsyncResult *async_result; gchar **sniffed_mime; }; static void deep_count_load (DeepCountState *state, GFile *file); static void init_mimetypes (void) { static gsize once_init = 0; if (g_once_init_enter (&once_init)) { GSList *formats, *l; GdkPixbufFormat *format; gchar **types; gint idx; image_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); video_type_table = g_hash_table_new (g_str_hash, g_str_equal); audio_type_table = g_hash_table_new (g_str_hash, g_str_equal); docs_type_table = g_hash_table_new (g_str_hash, g_str_equal); formats = gdk_pixbuf_get_formats (); for (l = formats; l != NULL; l = l->next) { format = l->data; types = gdk_pixbuf_format_get_mime_types (format); for (idx = 0; types[idx] != NULL; idx++) g_hash_table_insert (image_type_table, g_strdup (types[idx]), GINT_TO_POINTER (1)); g_strfreev (types); } g_slist_free (formats); for (idx = 0; audio_mimetypes[idx] != NULL; idx++) g_hash_table_insert (audio_type_table, (gpointer) audio_mimetypes[idx], GINT_TO_POINTER (1)); for (idx = 0; video_mimetypes[idx] != NULL; idx++) g_hash_table_insert (video_type_table, (gpointer) video_mimetypes[idx], GINT_TO_POINTER (1)); for (idx = 0; docs_mimetypes[idx] != NULL; idx++) g_hash_table_insert (docs_type_table, (gpointer) docs_mimetypes[idx], GINT_TO_POINTER (1)); g_once_init_leave (&once_init, 1); } } static void add_content_type_to_cache (DeepCountState *state, const gchar *content_type) { gboolean matched = TRUE; if (g_hash_table_lookup (image_type_table, content_type)) state->image_count++; else if (g_hash_table_lookup (video_type_table, content_type)) state->video_count++; else if (g_hash_table_lookup (docs_type_table, content_type)) state->document_count++; else if (g_hash_table_lookup (audio_type_table, content_type)) state->audio_count++; else matched = FALSE; if (matched) state->total_items++; } typedef struct { const gchar *type; gdouble ratio; } SniffedResult; static gint results_cmp_func (gconstpointer a, gconstpointer b) { const SniffedResult *sniffed_a = a; const SniffedResult *sniffed_b = b; if (sniffed_a->ratio < sniffed_b->ratio) return 1; if (sniffed_a->ratio > sniffed_b->ratio) return -1; return 0; } static void prepare_async_result (DeepCountState *state) { CinnamonMimeSniffer *self = state->self; GArray *results; GPtrArray *sniffed_mime; SniffedResult result; sniffed_mime = g_ptr_array_new (); results = g_array_new (TRUE, TRUE, sizeof (SniffedResult)); if (state->total_items == 0) goto out; result.type = "x-content/video"; result.ratio = (gdouble) state->video_count / (gdouble) state->total_items; g_array_append_val (results, result); result.type = "x-content/audio"; result.ratio = (gdouble) state->audio_count / (gdouble) state->total_items; g_array_append_val (results, result); result.type = "x-content/pictures"; result.ratio = (gdouble) state->image_count / (gdouble) state->total_items; g_array_append_val (results, result); result.type = "x-content/documents"; result.ratio = (gdouble) state->document_count / (gdouble) state->total_items; g_array_append_val (results, result); g_array_sort (results, results_cmp_func); result = g_array_index (results, SniffedResult, 0); g_ptr_array_add (sniffed_mime, g_strdup (result.type)); /* if other types score high in ratio, add them, up to three */ result = g_array_index (results, SniffedResult, 1); if (result.ratio < HIGH_SCORE_RATIO) goto out; g_ptr_array_add (sniffed_mime, g_strdup (result.type)); result = g_array_index (results, SniffedResult, 2); if (result.ratio < HIGH_SCORE_RATIO) goto out; g_ptr_array_add (sniffed_mime, g_strdup (result.type)); out: g_ptr_array_add (sniffed_mime, NULL); self->priv->sniffed_mime = (gchar **) g_ptr_array_free (sniffed_mime, FALSE); g_array_free (results, TRUE); g_simple_async_result_complete_in_idle (self->priv->async_result); } /* adapted from nautilus/libnautilus-private/nautilus-directory-async.c */ static void deep_count_one (DeepCountState *state, GFileInfo *info) { GFile *subdir; const char *content_type; if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { /* record the fact that we have to descend into this directory */ subdir = g_file_get_child (state->file, g_file_info_get_name (info)); state->deep_count_subdirectories = g_list_append (state->deep_count_subdirectories, subdir); } else { content_type = g_file_info_get_content_type (info); add_content_type_to_cache (state, content_type); } } static void deep_count_finish (DeepCountState *state) { prepare_async_result (state); if (state->enumerator) { if (!g_file_enumerator_is_closed (state->enumerator)) g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL); g_object_unref (state->enumerator); } g_cancellable_reset (state->self->priv->cancellable); g_clear_object (&state->file); g_list_free_full (state->deep_count_subdirectories, g_object_unref); g_free (state); } static void deep_count_next_dir (DeepCountState *state) { GFile *new_file; g_clear_object (&state->file); if (state->deep_count_subdirectories != NULL) { /* Work on a new directory. */ new_file = state->deep_count_subdirectories->data; state->deep_count_subdirectories = g_list_remove (state->deep_count_subdirectories, new_file); deep_count_load (state, new_file); g_object_unref (new_file); } else { deep_count_finish (state); } } static void deep_count_more_files_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { DeepCountState *state; GList *files, *l; GFileInfo *info; state = user_data; if (g_cancellable_is_cancelled (state->self->priv->cancellable)) { deep_count_finish (state); return; } files = g_file_enumerator_next_files_finish (state->enumerator, res, NULL); for (l = files; l != NULL; l = l->next) { info = l->data; deep_count_one (state, info); g_object_unref (info); } if (files == NULL) { g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL); g_object_unref (state->enumerator); state->enumerator = NULL; deep_count_next_dir (state); } else { g_file_enumerator_next_files_async (state->enumerator, DIRECTORY_LOAD_ITEMS_PER_CALLBACK, G_PRIORITY_LOW, state->self->priv->cancellable, deep_count_more_files_callback, state); } g_list_free (files); } static void deep_count_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { DeepCountState *state; GFileEnumerator *enumerator; state = user_data; if (g_cancellable_is_cancelled (state->self->priv->cancellable)) { deep_count_finish (state); return; } enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL); if (enumerator == NULL) { deep_count_next_dir (state); } else { state->enumerator = enumerator; g_file_enumerator_next_files_async (state->enumerator, DIRECTORY_LOAD_ITEMS_PER_CALLBACK, G_PRIORITY_LOW, state->self->priv->cancellable, deep_count_more_files_callback, state); } } static void deep_count_load (DeepCountState *state, GFile *file) { state->file = g_object_ref (file); g_file_enumerate_children_async (state->file, LOADER_ATTRS, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */ G_PRIORITY_LOW, /* prio */ state->self->priv->cancellable, deep_count_callback, state); } static void deep_count_start (CinnamonMimeSniffer *self) { DeepCountState *state; state = g_new0 (DeepCountState, 1); state->self = self; deep_count_load (state, self->priv->file); } static void query_info_async_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GFileInfo *info; GError *error = NULL; CinnamonMimeSniffer *self = user_data; info = g_file_query_info_finish (G_FILE (source), res, &error); if (error != NULL) { g_simple_async_result_take_error (self->priv->async_result, error); g_simple_async_result_complete_in_idle (self->priv->async_result); return; } if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) { g_simple_async_result_set_error (self->priv->async_result, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, "Not a directory"); g_simple_async_result_complete_in_idle (self->priv->async_result); return; } deep_count_start (self); } static gboolean watchdog_timeout_reached_cb (gpointer user_data) { CinnamonMimeSniffer *self = user_data; self->priv->watchdog_id = 0; g_cancellable_cancel (self->priv->cancellable); return FALSE; } static void start_loading_file (CinnamonMimeSniffer *self) { g_file_query_info_async (self->priv->file, LOADER_ATTRS, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, self->priv->cancellable, query_info_async_ready_cb, self); } static void cinnamon_mime_sniffer_set_file (CinnamonMimeSniffer *self, GFile *file) { g_clear_object (&self->priv->file); self->priv->file = g_object_ref (file); } static void cinnamon_mime_sniffer_dispose (GObject *object) { CinnamonMimeSniffer *self = CINNAMON_MIME_SNIFFER (object); g_clear_object (&self->priv->file); g_clear_object (&self->priv->cancellable); g_clear_object (&self->priv->async_result); if (self->priv->watchdog_id != 0) { g_source_remove (self->priv->watchdog_id); self->priv->watchdog_id = 0; } G_OBJECT_CLASS (cinnamon_mime_sniffer_parent_class)->dispose (object); } static void cinnamon_mime_sniffer_finalize (GObject *object) { CinnamonMimeSniffer *self = CINNAMON_MIME_SNIFFER (object); g_strfreev (self->priv->sniffed_mime); G_OBJECT_CLASS (cinnamon_mime_sniffer_parent_class)->finalize (object); } static void cinnamon_mime_sniffer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CinnamonMimeSniffer *self = CINNAMON_MIME_SNIFFER (object); switch (prop_id) { case PROP_FILE: g_value_set_object (value, self->priv->file); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_mime_sniffer_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CinnamonMimeSniffer *self = CINNAMON_MIME_SNIFFER (object); switch (prop_id) { case PROP_FILE: cinnamon_mime_sniffer_set_file (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cinnamon_mime_sniffer_class_init (CinnamonMimeSnifferClass *klass) { GObjectClass *oclass; oclass = G_OBJECT_CLASS (klass); oclass->dispose = cinnamon_mime_sniffer_dispose; oclass->finalize = cinnamon_mime_sniffer_finalize; oclass->get_property = cinnamon_mime_sniffer_get_property; oclass->set_property = cinnamon_mime_sniffer_set_property; properties[PROP_FILE] = g_param_spec_object ("file", "File", "The loaded file", G_TYPE_FILE, G_PARAM_READWRITE); g_type_class_add_private (klass, sizeof (CinnamonMimeSnifferPrivate)); g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); } static void cinnamon_mime_sniffer_init (CinnamonMimeSniffer *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CINNAMON_TYPE_MIME_SNIFFER, CinnamonMimeSnifferPrivate); init_mimetypes (); } CinnamonMimeSniffer * cinnamon_mime_sniffer_new (GFile *file) { return g_object_new (CINNAMON_TYPE_MIME_SNIFFER, "file", file, NULL); } void cinnamon_mime_sniffer_sniff_async (CinnamonMimeSniffer *self, GAsyncReadyCallback callback, gpointer user_data) { g_assert (self->priv->watchdog_id == 0); g_assert (self->priv->async_result == NULL); self->priv->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, cinnamon_mime_sniffer_sniff_finish); self->priv->cancellable = g_cancellable_new (); self->priv->watchdog_id = g_timeout_add (WATCHDOG_TIMEOUT, watchdog_timeout_reached_cb, self); start_loading_file (self); } gchar ** cinnamon_mime_sniffer_sniff_finish (CinnamonMimeSniffer *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (self->priv->async_result, error)) return NULL; return g_strdupv (self->priv->sniffed_mime); } Cinnamon-2.8.6/src/Makefile.am0000664000175000017500000002231612625666430015056 0ustar fabiofabioNULL = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = bin_SCRIPTS = pkglibexec_PROGRAMS = noinst_LTLIBRARIES = noinst_PROGRAMS = service_in_files = -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = INTROSPECTION_SCANNER_ARGS = --warn-all --add-include-path=$(srcdir) INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) --includedir=$(MUFFIN_TYPELIB_DIR) typelibdir = $(pkglibdir) typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) servicedir = $(datadir)/dbus-1/services service_DATA = $(service_in_files:.service.in=.service) %.service: %.service.in Makefile $(AM_V_GEN) \ [ -d $(@D) ] || $(MKDIR_P) $(@D) ; \ sed -e "s|\@libexecdir\@|$(libexecdir)|" \ -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@.tmp && mv $@.tmp $@ CLEANFILES += $(service_DATA) CLEANFILES += $(gir_DATA) $(typelib_DATA) bin_SCRIPTS += cinnamon-extension-tool EXTRA_DIST += cinnamon-extension-tool.in bin_PROGRAMS = cinnamon-real if USE_JHBUILD_WRAPPER_SCRIPT cinnamon = cinnamon-jhbuild bin_SCRIPTS += cinnamon-jhbuild else cinnamon = cinnamon-real endif EXTRA_DIST += cinnamon-jhbuild.in noinst_DATA = cinnamon cinnamon: $(cinnamon) Makefile $(AM_V_GEN) cp $< $@.tmp && mv $@.tmp $@ install-exec-hook: mv $(DESTDIR)$(bindir)/$(cinnamon) $(DESTDIR)$(bindir)/cinnamon uninstall-hook: rm -f $(DESTDIR)$(bindir)/cinnamon generated_script_substitutions = \ -e "s|@bindir[@]|$(bindir)|" \ -e "s|@datadir[@]|$(datadir)|" \ -e "s|@libexecdir[@]|$(libexecdir)|" \ -e "s|@libdir[@]|$(libdir)|" \ -e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|" \ -e "s|@pkgdatadir[@]|$(pkgdatadir)|" \ -e "s|@pkglibexecdir[@]|$(pkglibexecdir)|" \ -e "s|@PYTHON[@]|$(PYTHON)|" \ -e "s|@VERSION[@]|$(VERSION)|" \ -e "s|@sysconfdir[@]|$(sysconfdir)|" \ -e "s|@GJS_CONSOLE[@]|$(GJS_CONSOLE)|" cinnamon-jhbuild: cinnamon-jhbuild.in cinnamon-real Makefile $(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@ @# Regenerate libtool wrapper so Alt+F2 restart will work @$(LIBTOOL) --mode=execute test -f cinnamon-real cinnamon-extension-tool: cinnamon-extension-tool.in Makefile $(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@ CLEANFILES += cinnamon $(bin_SCRIPTS) include Makefile-st.am include Makefile-tray.am include Makefile-gvc.am include Makefile-hotplug-sniffer.am cinnamon_cflags = \ $(CINNAMON_CFLAGS) \ -I$(srcdir)/tray \ -DCLUTTER_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_API \ -DVERSION=\"$(VERSION)\" \ -DLOCALEDIR=\"$(datadir)/locale\" \ -DDATADIR=\"$(datadir)\" \ -DCINNAMON_LIBEXECDIR=\"$(libexecdir)\" \ -DCINNAMON_DATADIR=\"$(pkgdatadir)\" \ -DCINNAMON_PKGLIBDIR=\"$(pkglibdir)\" \ -DCINNAMON_PKGLIBEXECDIR=\"$(pkglibexecdir)\" \ -DJSDIR=\"$(pkgdatadir)/js\" privlibdir = $(pkglibdir) privlib_LTLIBRARIES = libcinnamon.la libcinnamon-js.la cinnamon_built_sources = \ cinnamon-marshal.h \ cinnamon-marshal.c \ cinnamon-enum-types.h \ cinnamon-enum-types.c BUILT_SOURCES += $(cinnamon_built_sources) EXTRA_DIST += cinnamon-marshal.list cinnamon_public_headers_h = \ cinnamon-app.h \ cinnamon-app-system.h \ cinnamon-doc-system.h \ cinnamon-embedded-window.h \ cinnamon-generic-container.h \ cinnamon-gtk-embed.h \ cinnamon-global.h \ cinnamon-mount-operation.h \ cinnamon-perf-log.h \ cinnamon-screenshot.h \ cinnamon-screen-grabber.h \ cinnamon-slicer.h \ cinnamon-stack.h \ cinnamon-tray-icon.h \ cinnamon-tray-manager.h \ cinnamon-util.h \ cinnamon-window-tracker.h \ cinnamon-wm.h \ cinnamon-xfixes-cursor.h \ cinnamon-js.h libcinnamon_la_SOURCES = \ $(cinnamon_built_sources) \ $(cinnamon_public_headers_h) \ cinnamon-app-private.h \ cinnamon-app-system-private.h \ cinnamon-embedded-window-private.h \ cinnamon-global-private.h \ cinnamon-window-tracker-private.h \ cinnamon-wm-private.h \ cinnamon-plugin.c \ cinnamon-app.c \ cinnamon-app-system.c \ cinnamon-doc-system.c \ cinnamon-embedded-window.c \ cinnamon-generic-container.c \ cinnamon-gtk-embed.c \ cinnamon-global.c \ cinnamon-mount-operation.c \ cinnamon-perf-log.c \ cinnamon-screenshot.c \ cinnamon-screen-grabber.c \ cinnamon-slicer.c \ cinnamon-stack.c \ cinnamon-tray-icon.c \ cinnamon-tray-manager.c \ cinnamon-util.c \ cinnamon-window-tracker.c \ cinnamon-wm.c \ cinnamon-xfixes-cursor.c \ cinnamon-js.cpp libcinnamon_la_gir_sources = \ $(filter-out %-private.h $(cinnamon_recorder_non_gir_sources), $(cinnamon_public_headers_h) $(libcinnamon_la_SOURCES)) cinnamon_real_SOURCES = \ main.c cinnamon_real_CPPFLAGS = $(cinnamon_cflags) cinnamon_real_LDADD = libcinnamon.la libcinnamon-js.la $(libcinnamon_la_LIBADD) cinnamon_real_DEPENDENCIES = libcinnamon.la ######################################## libcinnamon_js_la_SOURCES = \ cinnamon-js.h \ cinnamon-js.cpp \ $(NULL) libcinnamon_js_la_LIBADD = \ $(CINNAMON_JS_LIBS) \ $(NULL) libcinnamon_js_la_LDFLAGS = \ -avoid-version libcinnamon_js_la_CPPFLAGS = \ $(CINNAMON_JS_CFLAGS) ######################################## cinnamon_recorder_sources = \ cinnamon-recorder.c \ cinnamon-recorder.h # Custom element is an internal detail cinnamon_recorder_non_gir_sources = \ cinnamon-recorder-src.c \ cinnamon-recorder-src.h if BUILD_RECORDER libcinnamon_la_SOURCES += $(cinnamon_recorder_sources) $(cinnamon_recorder_non_gir_sources) noinst_PROGRAMS += test-recorder test_recorder_CPPFLAGS = $(TEST_CINNAMON_RECORDER_CFLAGS) \ $(ST_CFLAGS) test_recorder_LDADD = $(TEST_CINNAMON_RECORDER_LIBS) \ libst-1.0.la test_recorder_SOURCES = \ $(cinnamon_recorder_sources) $(cinnamon_recorder_non_gir_sources) \ test-recorder.c endif BUILD_RECORDER ######################################## pkglibexec_PROGRAMS += cinnamon-perf-helper cinnamon_perf_helper_SOURCES = cinnamon-perf-helper.c cinnamon_perf_helper_CPPFLAGS = $(CINNAMON_PERF_HELPER_CFLAGS) cinnamon_perf_helper_LDADD = $(CINNAMON_PERF_HELPER_LIBS) ######################################## noinst_PROGRAMS += run-js-test run_js_test_CPPFLAGS = $(cinnamon_cflags) run_js_test_LDADD = libcinnamon.la $(CINNAMON_JS_LIBS) $(libcinnamon_la_LIBADD) run_js_test_LDFLAGS = -export-dynamic run_js_test_SOURCES = \ run-js-test.c ######################################## cinnamon-marshal.h: stamp-cinnamon-marshal.h @true stamp-cinnamon-marshal.h: Makefile cinnamon-marshal.list $(AM_V_GEN) $(GLIB_GENMARSHAL) \ --prefix=_cinnamon_marshal \ --header \ $(srcdir)/cinnamon-marshal.list > xgen-smh && \ (cmp -s xgen-smh cinnamon-marshal.h || cp -f xgen-smh cinnamon-marshal.h) && \ rm -f xgen-smh && \ echo timestamp > $(@F) CLEANFILES += stamp-cinnamon-marshal.h cinnamon-marshal.c: Makefile cinnamon-marshal.list $(AM_V_GEN) (echo "#include \"cinnamon-marshal.h\"" ; \ $(GLIB_GENMARSHAL) \ --prefix=_cinnamon_marshal \ --body \ $(srcdir)/cinnamon-marshal.list ) > xgen-smc && \ cp -f xgen-smc cinnamon-marshal.c && \ rm -f xgen-smc cinnamon-enum-types.h: stamp-cinnamon-enum-types.h Makefile @true stamp-cinnamon-enum-types.h: $(srcdir)/cinnamon-enum-types.h.in $(cinnamon_public_headers_h) $(AM_V_GEN) ( \ $(GLIB_MKENUMS) \ --template $< \ $(addprefix $(srcdir)/,$(cinnamon_public_headers_h)) ) > $(@F).tmp && \ (cmp -s $(@F).tmp cinnamon-enum-types.h || mv $(@F).tmp cinnamon-enum-types.h) && \ rm -f $(@F).tmp && \ echo timestamp > $(@F) EXTRA_DIST += cinnamon-enum-types.h.in CLEANFILES += stamp-cinnamon-enum-types.h cinnamon-enum-types.c: $(srcdir)/cinnamon-enum-types.c.in stamp-cinnamon-enum-types.h $(AM_V_GEN) ( \ $(GLIB_MKENUMS) \ --template $< \ $(addprefix $(srcdir)/,$(cinnamon_public_headers_h)) ) > $(@F).tmp && \ mv $(@F).tmp $(@F) && \ rm -f $(@F).tmp EXTRA_DIST += cinnamon-enum-types.c.in libcinnamon_la_LDFLAGS = -avoid-version libcinnamon_la_LIBADD = \ -lm \ $(CINNAMON_LIBS) \ $(BLUETOOTH_LIBS) \ libst-1.0.la \ libtray.la \ libgvc.la \ $(NULL) libcinnamon_la_CPPFLAGS = $(cinnamon_cflags) Cinnamon-0.1.gir: libcinnamon.la St-1.0.gir Cinnamon_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-Muffin.0 Soup-2.4 CMenu-3.0 NetworkManager-1.0 NMClient-1.0 Cinnamon_0_1_gir_CFLAGS = $(libcinnamon_la_CPPFLAGS) -I $(srcdir) Cinnamon_0_1_gir_LIBS = libcinnamon.la Cinnamon_0_1_gir_FILES = $(libcinnamon_la_gir_sources) Cinnamon_0_1_gir_SCANNERFLAGS = --include-uninstalled=$(builddir)/St-1.0.gir \ --add-include-path=$(MUFFIN_GIR_DIR) $(if $(BLUETOOTH_DIR),-L $(BLUETOOTH_DIR),) INTROSPECTION_GIRS += Cinnamon-0.1.gir CLEANFILES += Cinnamon-0.1.gir CinnamonJS-0.1.gir: libcinnamon-js.la CinnamonJS_0_1_gir_CFLAGS = $(libcinnamon_la_CPPFLAGS) -I $(srcdir) CinnamonJS_0_1_gir_LIBS = libcinnamon-js.la CinnamonJS_0_1_gir_FILES = $(libcinnamon_js_la_SOURCES) INTROSPECTION_GIRS += CinnamonJS-0.1.gir CLEANFILES += CinnamonJS-0.1.gir St-1.0.gir: libst-1.0.la St_1_0_gir_INCLUDES = Clutter-1.0 Gtk-3.0 St_1_0_gir_CFLAGS = $(st_cflags) -DST_COMPILATION St_1_0_gir_LIBS = libst-1.0.la St_1_0_gir_FILES = $(filter-out %-private.h $(st_non_gir_sources), $(addprefix $(srcdir)/,$(st_source_h))) \ $(addprefix $(srcdir)/,$(st_source_c)) INTROSPECTION_GIRS += St-1.0.gir CLEANFILES += St-1.0.gir Cinnamon-2.8.6/src/cinnamon-tray-manager.h0000664000175000017500000000371612625666430017365 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_TRAY_MANAGER_H__ #define __CINNAMON_TRAY_MANAGER_H__ #include #include "st.h" G_BEGIN_DECLS #define CINNAMON_TYPE_TRAY_MANAGER (cinnamon_tray_manager_get_type ()) #define CINNAMON_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_TRAY_MANAGER, CinnamonTrayManager)) #define CINNAMON_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_TRAY_MANAGER, CinnamonTrayManagerClass)) #define CINNAMON_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_TRAY_MANAGER)) #define CINNAMON_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_TRAY_MANAGER)) #define CINNAMON_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_TRAY_MANAGER, CinnamonTrayManagerClass)) typedef struct _CinnamonTrayManager CinnamonTrayManager; typedef struct _CinnamonTrayManagerPrivate CinnamonTrayManagerPrivate; typedef struct _CinnamonTrayManagerClass CinnamonTrayManagerClass; struct _CinnamonTrayManager { GObject parent_instance; CinnamonTrayManagerPrivate *priv; }; struct _CinnamonTrayManagerClass { GObjectClass parent_class; void (* tray_icon_added) (CinnamonTrayManager *manager, ClutterActor *icon, const char *lowercase_wm_class); void (* tray_icon_removed) (CinnamonTrayManager *manager, ClutterActor *icon); }; GType cinnamon_tray_manager_get_type (void); CinnamonTrayManager *cinnamon_tray_manager_new (void); void cinnamon_tray_manager_manage_stage (CinnamonTrayManager *manager, ClutterStage *stage, StWidget *theme_widget); void cinnamon_tray_manager_redisplay (CinnamonTrayManager *manager); G_END_DECLS #endif /* __CINNAMON_TRAY_MANAGER_H__ */ Cinnamon-2.8.6/src/cinnamon-recorder-src.h0000664000175000017500000000353612625666430017370 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_RECORDER_SRC_H__ #define __CINNAMON_RECORDER_SRC_H__ #include G_BEGIN_DECLS /** * CinnamonRecorderSrc: * * cinnamonrecordersrc a custom source element is pretty much like a very * simple version of the stander GStreamer 'appsrc' element, without * any of the provisions for seeking, generating data on demand, * etc. In both cases, the application supplies the buffers and the * element pushes them into the pipeline. The main reason for not using * appsrc is that it wasn't a supported element until gstreamer 0.10.22, * and as of 2009-03, many systems still have 0.10.21. */ typedef struct _CinnamonRecorderSrc CinnamonRecorderSrc; typedef struct _CinnamonRecorderSrcClass CinnamonRecorderSrcClass; #define CINNAMON_TYPE_RECORDER_SRC (cinnamon_recorder_src_get_type ()) #define CINNAMON_RECORDER_SRC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_RECORDER_SRC, CinnamonRecorderSrc)) #define CINNAMON_RECORDER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_RECORDER_SRC, CinnamonRecorderSrcClass)) #define CINNAMON_IS_RECORDER_SRC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_RECORDER_SRC)) #define CINNAMON_IS_RECORDER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_RECORDER_SRC)) #define CINNAMON_RECORDER_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_RECORDER_SRC, CinnamonRecorderSrcClass)) GType cinnamon_recorder_src_get_type (void) G_GNUC_CONST; void cinnamon_recorder_src_register (void); void cinnamon_recorder_src_add_buffer (CinnamonRecorderSrc *src, GstBuffer *buffer); void cinnamon_recorder_src_close (CinnamonRecorderSrc *src); G_END_DECLS #endif /* __CINNAMON_RECORDER_SRC_H__ */ Cinnamon-2.8.6/src/cinnamon-stack.h0000664000175000017500000000227112625666430016076 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_STACK_H__ #define __CINNAMON_STACK_H__ #include "st.h" #include #define CINNAMON_TYPE_STACK (cinnamon_stack_get_type ()) #define CINNAMON_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_STACK, CinnamonStack)) #define CINNAMON_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_STACK, CinnamonStackClass)) #define CINNAMON_IS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_STACK)) #define CINNAMON_IS_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_STACK)) #define CINNAMON_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_STACK, CinnamonStackClass)) typedef struct _CinnamonStack CinnamonStack; typedef struct _CinnamonStackClass CinnamonStackClass; typedef struct _CinnamonStackPrivate CinnamonStackPrivate; struct _CinnamonStack { StContainer parent; CinnamonStackPrivate *priv; }; struct _CinnamonStackClass { StContainerClass parent_class; }; GType cinnamon_stack_get_type (void) G_GNUC_CONST; #endif /* __CINNAMON_STACK_H__ */ Cinnamon-2.8.6/src/cinnamon-tray-icon.h0000664000175000017500000000300512625666430016672 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_TRAY_ICON_H__ #define __CINNAMON_TRAY_ICON_H__ #include "cinnamon-gtk-embed.h" #define CINNAMON_TYPE_TRAY_ICON (cinnamon_tray_icon_get_type ()) #define CINNAMON_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_TRAY_ICON, CinnamonTrayIcon)) #define CINNAMON_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_TRAY_ICON, CinnamonTrayIconClass)) #define CINNAMON_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_TRAY_ICON)) #define CINNAMON_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_TRAY_ICON)) #define CINNAMON_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_TRAY_ICON, CinnamonTrayIconClass)) typedef struct _CinnamonTrayIcon CinnamonTrayIcon; typedef struct _CinnamonTrayIconClass CinnamonTrayIconClass; typedef struct _CinnamonTrayIconPrivate CinnamonTrayIconPrivate; struct _CinnamonTrayIcon { CinnamonGtkEmbed parent; CinnamonTrayIconPrivate *priv; }; struct _CinnamonTrayIconClass { CinnamonGtkEmbedClass parent_class; }; GType cinnamon_tray_icon_get_type (void) G_GNUC_CONST; ClutterActor *cinnamon_tray_icon_new (CinnamonEmbeddedWindow *window); void cinnamon_tray_icon_click (CinnamonTrayIcon *icon, ClutterEvent *event); #endif /* __CINNAMON_TRAY_ICON_H__ */ Cinnamon-2.8.6/src/test-recorder.c0000664000175000017500000000517512625666430015754 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #define GST_USE_UNSTABLE_API #include "cinnamon-recorder.h" #include #include /* Very simple test of the CinnamonRecorder class; shows some text strings * moving around and records it. */ static CinnamonRecorder *recorder; static gboolean stop_recording_timeout (gpointer data) { cinnamon_recorder_close (recorder); return FALSE; } static void on_animation_completed (ClutterAnimation *animation) { g_timeout_add (1000, stop_recording_timeout, NULL); } int main (int argc, char **argv) { ClutterActor *stage; ClutterActor *text; ClutterAnimation *animation; ClutterColor red, green, blue; gst_init (&argc, &argv); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; clutter_color_from_string (&red, "red"); clutter_color_from_string (&green, "green"); clutter_color_from_string (&blue, "blue"); stage = clutter_stage_new (); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); text = g_object_new (CLUTTER_TYPE_TEXT, "text", "Red", "font-name", "Sans 40px", "color", &red, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); animation = clutter_actor_animate (text, CLUTTER_EASE_IN_OUT_QUAD, 3000, "x", 320, "y", 240, NULL); g_signal_connect (animation, "completed", G_CALLBACK (on_animation_completed), NULL); text = g_object_new (CLUTTER_TYPE_TEXT, "text", "Blue", "font-name", "Sans 40px", "color", &blue, "x", 640, "y", 0, NULL); clutter_actor_set_anchor_point_from_gravity (text, CLUTTER_GRAVITY_NORTH_EAST); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); animation = clutter_actor_animate (text, CLUTTER_EASE_IN_OUT_QUAD, 3000, "x", 320, "y", 240, NULL); text = g_object_new (CLUTTER_TYPE_TEXT, "text", "Green", "font-name", "Sans 40px", "color", &green, "x", 0, "y", 480, NULL); clutter_actor_set_anchor_point_from_gravity (text, CLUTTER_GRAVITY_SOUTH_WEST); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); animation = clutter_actor_animate (text, CLUTTER_EASE_IN_OUT_QUAD, 3000, "x", 320, "y", 240, NULL); recorder = cinnamon_recorder_new (CLUTTER_STAGE (stage)); cinnamon_recorder_set_filename (recorder, "test-recorder.ogg"); clutter_actor_show (stage); cinnamon_recorder_record (recorder); clutter_main (); return 0; } Cinnamon-2.8.6/src/cinnamon-generic-container.h0000664000175000017500000000442712625666430020372 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_GENERIC_CONTAINER_H__ #define __CINNAMON_GENERIC_CONTAINER_H__ #include "st.h" #define CINNAMON_TYPE_GENERIC_CONTAINER (cinnamon_generic_container_get_type ()) #define CINNAMON_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_GENERIC_CONTAINER, CinnamonGenericContainer)) #define CINNAMON_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_GENERIC_CONTAINER, CinnamonGenericContainerClass)) #define CINNAMON_IS_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_GENERIC_CONTAINER)) #define CINNAMON_IS_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_GENERIC_CONTAINER)) #define CINNAMON_GENERIC_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_GENERIC_CONTAINER, CinnamonGenericContainerClass)) typedef struct { float min_size; float natural_size; /* */ guint _refcount; } CinnamonGenericContainerAllocation; #define CINNAMON_TYPE_GENERIC_CONTAINER_ALLOCATION (cinnamon_generic_container_allocation_get_type ()) GType cinnamon_generic_container_allocation_get_type (void); typedef struct _CinnamonGenericContainer CinnamonGenericContainer; typedef struct _CinnamonGenericContainerClass CinnamonGenericContainerClass; typedef struct _CinnamonGenericContainerPrivate CinnamonGenericContainerPrivate; struct _CinnamonGenericContainer { StContainer parent; CinnamonGenericContainerPrivate *priv; }; struct _CinnamonGenericContainerClass { StContainerClass parent_class; }; GType cinnamon_generic_container_get_type (void) G_GNUC_CONST; guint cinnamon_generic_container_get_n_skip_paint (CinnamonGenericContainer *self); gboolean cinnamon_generic_container_get_skip_paint (CinnamonGenericContainer *self, ClutterActor *child); void cinnamon_generic_container_set_skip_paint (CinnamonGenericContainer *self, ClutterActor *child, gboolean skip); #endif /* __CINNAMON_GENERIC_CONTAINER_H__ */ Cinnamon-2.8.6/src/cinnamon-app.h0000664000175000017500000000573312625666430015557 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_APP_H__ #define __CINNAMON_APP_H__ #include #include #include #define GMENU_I_KNOW_THIS_IS_UNSTABLE #include G_BEGIN_DECLS typedef struct _CinnamonApp CinnamonApp; typedef struct _CinnamonAppClass CinnamonAppClass; typedef struct _CinnamonAppPrivate CinnamonAppPrivate; #define CINNAMON_TYPE_APP (cinnamon_app_get_type ()) #define CINNAMON_APP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_APP, CinnamonApp)) #define CINNAMON_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_APP, CinnamonAppClass)) #define CINNAMON_IS_APP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_APP)) #define CINNAMON_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_APP)) #define CINNAMON_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_APP, CinnamonAppClass)) struct _CinnamonAppClass { GObjectClass parent_class; }; typedef enum { CINNAMON_APP_STATE_STOPPED, CINNAMON_APP_STATE_STARTING, CINNAMON_APP_STATE_RUNNING } CinnamonAppState; GType cinnamon_app_get_type (void) G_GNUC_CONST; const char *cinnamon_app_get_id (CinnamonApp *app); GMenuTreeEntry *cinnamon_app_get_tree_entry (CinnamonApp *app); GDesktopAppInfo *cinnamon_app_get_app_info (CinnamonApp *app); ClutterActor *cinnamon_app_create_icon_texture (CinnamonApp *app, int size); ClutterActor *cinnamon_app_get_faded_icon (CinnamonApp *app, int size); const char *cinnamon_app_get_name (CinnamonApp *app); const char *cinnamon_app_get_description (CinnamonApp *app); gboolean cinnamon_app_is_window_backed (CinnamonApp *app); void cinnamon_app_activate_window (CinnamonApp *app, MetaWindow *window, guint32 timestamp); void cinnamon_app_activate (CinnamonApp *app); void cinnamon_app_activate_full (CinnamonApp *app, int workspace, guint32 timestamp); void cinnamon_app_open_new_window (CinnamonApp *app, int workspace); CinnamonAppState cinnamon_app_get_state (CinnamonApp *app); gboolean cinnamon_app_request_quit (CinnamonApp *app); guint cinnamon_app_get_n_windows (CinnamonApp *app); GSList *cinnamon_app_get_windows (CinnamonApp *app); GSList *cinnamon_app_get_pids (CinnamonApp *app); gboolean cinnamon_app_is_on_workspace (CinnamonApp *app, MetaWorkspace *workspace); gboolean cinnamon_app_launch (CinnamonApp *app, guint timestamp, GList *uris, int workspace, char **startup_id, GError **error); int cinnamon_app_compare_by_name (CinnamonApp *app, CinnamonApp *other); int cinnamon_app_compare (CinnamonApp *app, CinnamonApp *other); G_END_DECLS #endif /* __CINNAMON_APP_H__ */ Cinnamon-2.8.6/src/cinnamon-embedded-window.h0000664000175000017500000000300712625666430020025 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_EMBEDDED_WINDOW_H__ #define __CINNAMON_EMBEDDED_WINDOW_H__ #include #include #define CINNAMON_TYPE_EMBEDDED_WINDOW (cinnamon_embedded_window_get_type ()) #define CINNAMON_EMBEDDED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_EMBEDDED_WINDOW, CinnamonEmbeddedWindow)) #define CINNAMON_EMBEDDED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_EMBEDDED_WINDOW, CinnamonEmbeddedWindowClass)) #define CINNAMON_IS_EMBEDDED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_EMBEDDED_WINDOW)) #define CINNAMON_IS_EMBEDDED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_EMBEDDED_WINDOW)) #define CINNAMON_EMBEDDED_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_EMBEDDED_WINDOW, CinnamonEmbeddedWindowClass)) typedef struct _CinnamonEmbeddedWindow CinnamonEmbeddedWindow; typedef struct _CinnamonEmbeddedWindowClass CinnamonEmbeddedWindowClass; typedef struct _CinnamonEmbeddedWindowPrivate CinnamonEmbeddedWindowPrivate; struct _CinnamonEmbeddedWindow { GtkWindow parent; CinnamonEmbeddedWindowPrivate *priv; }; struct _CinnamonEmbeddedWindowClass { GtkWindowClass parent_class; }; GType cinnamon_embedded_window_get_type (void) G_GNUC_CONST; GtkWidget *cinnamon_embedded_window_new (ClutterStage *stage); #endif /* __CINNAMON_EMBEDDED_WINDOW_H__ */ Cinnamon-2.8.6/src/cinnamon-gtk-embed.h0000664000175000017500000000264212625666430016632 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef __CINNAMON_GTK_EMBED_H__ #define __CINNAMON_GTK_EMBED_H__ #include #include "cinnamon-embedded-window.h" #define CINNAMON_TYPE_GTK_EMBED (cinnamon_gtk_embed_get_type ()) #define CINNAMON_GTK_EMBED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CINNAMON_TYPE_GTK_EMBED, CinnamonGtkEmbed)) #define CINNAMON_GTK_EMBED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_GTK_EMBED, CinnamonGtkEmbedClass)) #define CINNAMON_IS_GTK_EMBED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_TYPE_GTK_EMBED)) #define CINNAMON_IS_GTK_EMBED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_GTK_EMBED)) #define CINNAMON_GTK_EMBED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_GTK_EMBED, CinnamonGtkEmbedClass)) typedef struct _CinnamonGtkEmbed CinnamonGtkEmbed; typedef struct _CinnamonGtkEmbedClass CinnamonGtkEmbedClass; typedef struct _CinnamonGtkEmbedPrivate CinnamonGtkEmbedPrivate; struct _CinnamonGtkEmbed { ClutterX11TexturePixmap parent; CinnamonGtkEmbedPrivate *priv; }; struct _CinnamonGtkEmbedClass { ClutterX11TexturePixmapClass parent_class; }; GType cinnamon_gtk_embed_get_type (void) G_GNUC_CONST; ClutterActor *cinnamon_gtk_embed_new (CinnamonEmbeddedWindow *window); #endif /* __CINNAMON_GTK_EMBED_H__ */ Cinnamon-2.8.6/src/cinnamon-perf-log.c0000664000175000017500000006701612625666430016507 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include "cinnamon-perf-log.h" typedef struct _CinnamonPerfEvent CinnamonPerfEvent; typedef struct _CinnamonPerfStatistic CinnamonPerfStatistic; typedef struct _CinnamonPerfStatisticsClosure CinnamonPerfStatisticsClosure; typedef union _CinnamonPerfStatisticValue CinnamonPerfStatisticValue; typedef struct _CinnamonPerfBlock CinnamonPerfBlock; /** * SECTION:cinnamon-perf-log * @short_description: Event recorder for performance measurement * * CinnamonPerfLog provides a way for different parts of the code to * record information for subsequent analysis and interactive * exploration. Events exist of a timestamp, an event ID, and * arguments to the event. * * Emphasis is placed on storing recorded events in a compact * fashion so log recording disturbs the execution of the program * as little as possible, however events should not be recorded * at too fine a granularity - an event that is recorded once * per frame or once per user action is appropriate, an event that * occurs many times per frame is not. * * Arguments are identified by a D-Bus style signature; at the moment * only a limited number of event signatures are supported to * simplify the code. */ struct _CinnamonPerfLog { GObject parent; GPtrArray *events; GHashTable *events_by_name; GPtrArray *statistics; GHashTable *statistics_by_name; GPtrArray *statistics_closures; GQueue *blocks; gint64 start_time; gint64 last_time; guint statistics_timeout_id; guint enabled : 1; }; struct _CinnamonPerfLogClass { GObjectClass parent_class; }; struct _CinnamonPerfEvent { guint16 id; char *name; char *description; char *signature; }; union _CinnamonPerfStatisticValue { int i; gint64 x; }; struct _CinnamonPerfStatistic { CinnamonPerfEvent *event; CinnamonPerfStatisticValue current_value; CinnamonPerfStatisticValue last_value; guint initialized : 1; guint recorded : 1; }; struct _CinnamonPerfStatisticsClosure { CinnamonPerfStatisticsCallback callback; gpointer user_data; GDestroyNotify notify; }; /* The events in the log are stored in a linked list of fixed size * blocks. * * Note that the power-of-two nature of BLOCK_SIZE here is superficial * since the allocated block has the 'bytes' field and malloc * overhead. The current value is well below the size that will * typically be independently mmapped by the malloc implementation so * it doesn't matter. If we switched to mmapping blocks manually * (perhaps to avoid polluting malloc statistics), we'd want to use a * different value of BLOCK_SIZE. */ #define BLOCK_SIZE 8192 struct _CinnamonPerfBlock { guint32 bytes; guchar buffer[BLOCK_SIZE]; }; /* Number of milliseconds between periodic statistics collection when * events are enabled. Statistics collection can also be explicitly * triggered. */ #define STATISTIC_COLLECTION_INTERVAL_MS 5000 /* Builtin events */ enum { EVENT_SET_TIME, EVENT_STATISTICS_COLLECTED }; G_DEFINE_TYPE(CinnamonPerfLog, cinnamon_perf_log, G_TYPE_OBJECT); static gint64 get_time (void) { GTimeVal timeval; g_get_current_time (&timeval); return timeval.tv_sec * G_GINT64_CONSTANT(1000000) + timeval.tv_usec; } static void cinnamon_perf_log_init (CinnamonPerfLog *perf_log) { perf_log->events = g_ptr_array_new (); perf_log->events_by_name = g_hash_table_new (g_str_hash, g_str_equal); perf_log->statistics = g_ptr_array_new (); perf_log->statistics_by_name = g_hash_table_new (g_str_hash, g_str_equal); perf_log->statistics_closures = g_ptr_array_new (); perf_log->blocks = g_queue_new (); /* This event is used when timestamp deltas are greater than * fits in a gint32. 0xffffffff microseconds is about 70 minutes, so this * is not going to happen in normal usage. It might happen if performance * logging is enabled some time after starting Cinnamon */ cinnamon_perf_log_define_event (perf_log, "perf.setTime", "", "x"); g_assert (perf_log->events->len == EVENT_SET_TIME + 1); /* The purpose of this event is to allow us to optimize out storing * statistics that haven't changed. We want to mark every time we * collect statistics even if we don't record any individual * statistics so that we can distinguish sudden changes from gradual changes. * * The argument is the number of microseconds that statistics collection * took; we record that since statistics collection could start taking * significant time if we do things like grub around in /proc/ */ cinnamon_perf_log_define_event (perf_log, "perf.statisticsCollected", "Finished collecting statistics", "x"); g_assert (perf_log->events->len == EVENT_STATISTICS_COLLECTED + 1); perf_log->start_time = perf_log->last_time = get_time(); } static void cinnamon_perf_log_class_init (CinnamonPerfLogClass *class) { } /** * cinnamon_perf_log_get_default: * * Gets the global singleton performance log. This is initially disabled * and must be explicitly enabled with cinnamon_perf_log_set_enabled(). * * Return value: (transfer none): the global singleton performance log */ CinnamonPerfLog * cinnamon_perf_log_get_default (void) { static CinnamonPerfLog *perf_log; if (perf_log == NULL) perf_log = g_object_new (CINNAMON_TYPE_PERF_LOG, NULL); return perf_log; } static gboolean statistics_timeout (gpointer data) { CinnamonPerfLog *perf_log = data; cinnamon_perf_log_collect_statistics (perf_log); return TRUE; } /** * cinnamon_perf_log_set_enabled: * @perf_log: a #CinnamonPerfLog * @enabled: whether to record events * * Sets whether events are currently being recorded. */ void cinnamon_perf_log_set_enabled (CinnamonPerfLog *perf_log, gboolean enabled) { enabled = enabled != FALSE; if (enabled != perf_log->enabled) { perf_log->enabled = enabled; if (enabled) { perf_log->statistics_timeout_id = g_timeout_add (STATISTIC_COLLECTION_INTERVAL_MS, statistics_timeout, perf_log); } else { if (perf_log->statistics_timeout_id){ g_source_remove (perf_log->statistics_timeout_id); perf_log->statistics_timeout_id = 0; } } } } static CinnamonPerfEvent * define_event (CinnamonPerfLog *perf_log, const char *name, const char *description, const char *signature) { CinnamonPerfEvent *event; if (strcmp (signature, "") != 0 && strcmp (signature, "s") != 0 && strcmp (signature, "i") != 0 && strcmp (signature, "x") != 0) { g_warning ("Only supported event signatures are '', 's', 'i', and 'x'\n"); return NULL; } if (perf_log->events->len == 65536) { g_warning ("Maximum number of events defined\n"); return NULL; } /* We could do stricter validation, but this will break our JSON dumps */ if (strchr (name, '"') != NULL) { g_warning ("Event names can't include '\"'"); return NULL; } if (g_hash_table_lookup (perf_log->events_by_name, name) != NULL) { g_warning ("Duplicate event event for '%s'\n", name); return NULL; } event = g_slice_new (CinnamonPerfEvent); event->id = perf_log->events->len; event->name = g_strdup (name); event->signature = g_strdup (signature); event->description = g_strdup (description); g_ptr_array_add (perf_log->events, event); g_hash_table_insert (perf_log->events_by_name, event->name, event); return event; } /** * cinnamon_perf_log_define_event: * @perf_log: a #CinnamonPerfLog * @name: name of the event. This should of the form * '., for example * 'clutter.stagePaintDone'. * @description: human readable description of the event. * @signature: signature defining the arguments that event takes. * This is a string of type characters, using the same characters * as D-Bus or GVariant. Only a very limited number of signatures * are supported: , '', 's', 'i', and 'x'. This mean respectively: * no arguments, one string, one 32-bit integer, and one 64-bit * integer. * * Defines a performance event for later recording. */ void cinnamon_perf_log_define_event (CinnamonPerfLog *perf_log, const char *name, const char *description, const char *signature) { define_event (perf_log, name, description, signature); } static CinnamonPerfEvent * lookup_event (CinnamonPerfLog *perf_log, const char *name, const char *signature) { CinnamonPerfEvent *event = g_hash_table_lookup (perf_log->events_by_name, name); if (G_UNLIKELY (event == NULL)) { g_warning ("Discarding unknown event '%s'\n", name); return NULL; } if (G_UNLIKELY (strcmp (event->signature, signature) != 0)) { g_warning ("Event '%s'; defined with signature '%s', used with '%s'\n", name, event->signature, signature); return NULL; } return event; } static void record_event (CinnamonPerfLog *perf_log, gint64 event_time, CinnamonPerfEvent *event, const guchar *bytes, size_t bytes_len) { CinnamonPerfBlock *block; size_t total_bytes; guint32 time_delta; guint32 pos; if (!perf_log->enabled) return; total_bytes = sizeof (gint32) + sizeof (gint16) + bytes_len; if (G_UNLIKELY (bytes_len > BLOCK_SIZE || total_bytes > BLOCK_SIZE)) { g_warning ("Discarding oversize event '%s'\n", event->name); return; } if (event_time > perf_log->last_time + G_GINT64_CONSTANT(0xffffffff)) { perf_log->last_time = event_time; record_event (perf_log, event_time, lookup_event (perf_log, "perf.setTime", "x"), (const guchar *)&event_time, sizeof(gint64)); time_delta = 0; } else if (event_time < perf_log->last_time) time_delta = 0; else time_delta = (guint32)(event_time - perf_log->last_time); perf_log->last_time = event_time; if (perf_log->blocks->tail == NULL || total_bytes + ((CinnamonPerfBlock *)perf_log->blocks->tail->data)->bytes > BLOCK_SIZE) { block = g_new (CinnamonPerfBlock, 1); block->bytes = 0; g_queue_push_tail (perf_log->blocks, block); } else { block = (CinnamonPerfBlock *)perf_log->blocks->tail->data; } pos = block->bytes; memcpy (block->buffer + pos, &time_delta, sizeof (guint32)); pos += sizeof (guint32); memcpy (block->buffer + pos, &event->id, sizeof (guint16)); pos += sizeof (guint16); memcpy (block->buffer + pos, bytes, bytes_len); pos += bytes_len; block->bytes = pos; } /** * cinnamon_perf_log_event: * @perf_log: a #CinnamonPerfLog * @name: name of the event * * Records a performance event with no arguments. */ void cinnamon_perf_log_event (CinnamonPerfLog *perf_log, const char *name) { CinnamonPerfEvent *event = lookup_event (perf_log, name, ""); if (G_UNLIKELY (event == NULL)) return; record_event (perf_log, get_time(), event, NULL, 0); } /** * cinnamon_perf_log_event_i: * @perf_log: a #CinnamonPerfLog * @name: name of the event * @arg: the argument * * Records a performance event with one 32-bit integer argument. */ void cinnamon_perf_log_event_i (CinnamonPerfLog *perf_log, const char *name, gint32 arg) { CinnamonPerfEvent *event = lookup_event (perf_log, name, "i"); if (G_UNLIKELY (event == NULL)) return; record_event (perf_log, get_time(), event, (const guchar *)&arg, sizeof (arg)); } /** * cinnamon_perf_log_event_x: * @perf_log: a #CinnamonPerfLog * @name: name of the event * @arg: the argument * * Records a performance event with one 64-bit integer argument. */ void cinnamon_perf_log_event_x (CinnamonPerfLog *perf_log, const char *name, gint64 arg) { CinnamonPerfEvent *event = lookup_event (perf_log, name, "x"); if (G_UNLIKELY (event == NULL)) return; record_event (perf_log, get_time(), event, (const guchar *)&arg, sizeof (arg)); } /** * cinnamon_perf_log_event_s: * @perf_log: a #CinnamonPerfLog * @name: name of the event * @arg: the argument * * Records a performance event with one string argument. */ void cinnamon_perf_log_event_s (CinnamonPerfLog *perf_log, const char *name, const char *arg) { CinnamonPerfEvent *event = lookup_event (perf_log, name, "s"); if (G_UNLIKELY (event == NULL)) return; record_event (perf_log, get_time(), event, (const guchar *)arg, strlen (arg) + 1); } /** * cinnamon_perf_log_define_statistic: * @name: name of the statistic and of the corresponding event. * This should follow the same guidelines as for cinnamon_perf_log_define_event() * @description: human readable description of the statistic. * @signature: The type of the data stored for statistic. Must * currently be 'i' or 'x'. * * Defines a statistic. A statistic is a numeric value that is stored * by the performance log and recorded periodically or when * cinnamon_perf_log_collect_statistics() is called explicitly. * * Code that defines a statistic should update it by calling * the update function for the particular data type of the statistic, * such as cinnamon_perf_log_update_statistic_i(). This can be done * at any time, but would normally done inside a function registered * with cinnamon_perf_log_add_statistics_callback(). These functions * are called immediately before statistics are recorded. */ void cinnamon_perf_log_define_statistic (CinnamonPerfLog *perf_log, const char *name, const char *description, const char *signature) { CinnamonPerfEvent *event; CinnamonPerfStatistic *statistic; if (strcmp (signature, "i") != 0 && strcmp (signature, "x") != 0) { g_warning ("Only supported statistic signatures are 'i' and 'x'\n"); return; } event = define_event (perf_log, name, description, signature); if (event == NULL) return; statistic = g_slice_new (CinnamonPerfStatistic); statistic->event = event; statistic->initialized = FALSE; statistic->recorded = FALSE; g_ptr_array_add (perf_log->statistics, statistic); g_hash_table_insert (perf_log->statistics_by_name, event->name, statistic); } static CinnamonPerfStatistic * lookup_statistic (CinnamonPerfLog *perf_log, const char *name, const char *signature) { CinnamonPerfStatistic *statistic = g_hash_table_lookup (perf_log->statistics_by_name, name); if (G_UNLIKELY (statistic == NULL)) { g_warning ("Unknown statistic '%s'\n", name); return NULL; } if (G_UNLIKELY (strcmp (statistic->event->signature, signature) != 0)) { g_warning ("Statistic '%s'; defined with signature '%s', used with '%s'\n", name, statistic->event->signature, signature); return NULL; } return statistic; } /** * cinnamon_perf_log_update_statistic_i: * @perf_log: a #CinnamonPerfLog * @name: name of the statistic * @value: new value for the statistic * * Updates the current value of an 32-bit integer statistic. */ void cinnamon_perf_log_update_statistic_i (CinnamonPerfLog *perf_log, const char *name, gint32 value) { CinnamonPerfStatistic *statistic; statistic = lookup_statistic (perf_log, name, "i"); if (G_UNLIKELY (statistic == NULL)) return; statistic->current_value.i = value; statistic->initialized = TRUE; } /** * cinnamon_perf_log_update_statistic_x: * @perf_log: a #CinnamonPerfLog * @name: name of the statistic * @value: new value for the statistic * * Updates the current value of an 64-bit integer statistic. */ void cinnamon_perf_log_update_statistic_x (CinnamonPerfLog *perf_log, const char *name, gint64 value) { CinnamonPerfStatistic *statistic; statistic = lookup_statistic (perf_log, name, "x"); if (G_UNLIKELY (statistic == NULL)) return; statistic->current_value.x = value; statistic->initialized = TRUE; } /** * cinnamon_perf_log_add_statistics_callback: * @perf_log: a #CinnamonPerfLog * @callback: function to call before recording statistics * @user_data: data to pass to @callback * @notify: function to call when @user_data is no longer needed * * Adds a function that will be called before statistics are recorded. * The function would typically compute one or more statistics values * and call a function such as cinnamon_perf_log_update_statistic_i() * to update the value that will be recorded. */ void cinnamon_perf_log_add_statistics_callback (CinnamonPerfLog *perf_log, CinnamonPerfStatisticsCallback callback, gpointer user_data, GDestroyNotify notify) { CinnamonPerfStatisticsClosure *closure = g_slice_new (CinnamonPerfStatisticsClosure); closure->callback = callback; closure->user_data = user_data; closure->notify = notify; g_ptr_array_add (perf_log->statistics_closures, closure); } /** * cinnamon_perf_log_collect_statistics: * @perf_log: a #CinnamonPerfLog * * Calls all the update functions added with * cinnamon_perf_log_add_statistics_callback() and then records events * for all statistics, followed by a perf.statisticsCollected event. */ void cinnamon_perf_log_collect_statistics (CinnamonPerfLog *perf_log) { gint64 event_time = get_time (); gint64 collection_time; int i; if (!perf_log->enabled) return; for (i = 0; i < perf_log->statistics_closures->len; i++) { CinnamonPerfStatisticsClosure *closure; closure = g_ptr_array_index (perf_log->statistics_closures, i); closure->callback (perf_log, closure->user_data); } collection_time = get_time() - event_time; for (i = 0; i < perf_log->statistics->len; i++) { CinnamonPerfStatistic *statistic = g_ptr_array_index (perf_log->statistics, i); if (!statistic->initialized) continue; switch (statistic->event->signature[0]) { case 'i': if (!statistic->recorded || statistic->current_value.i != statistic->last_value.i) { record_event (perf_log, event_time, statistic->event, (const guchar *)&statistic->current_value.i, sizeof (gint32)); statistic->last_value.i = statistic->current_value.i; statistic->recorded = TRUE; } break; case 'x': if (!statistic->recorded || statistic->current_value.x != statistic->last_value.x) { record_event (perf_log, event_time, statistic->event, (const guchar *)&statistic->current_value.x, sizeof (gint64)); statistic->last_value.x = statistic->current_value.x; statistic->recorded = TRUE; } break; } } record_event (perf_log, event_time, g_ptr_array_index (perf_log->events, EVENT_STATISTICS_COLLECTED), (const guchar *)&collection_time, sizeof (gint64)); } /** * cinnamon_perf_log_replay: * @perf_log: a #CinnamonPerfLog * @replay_function: (scope call): function to call for each event in the log * @user_data: data to pass to @replay_function * * Replays the log by calling the given function for each event * in the log. */ void cinnamon_perf_log_replay (CinnamonPerfLog *perf_log, CinnamonPerfReplayFunction replay_function, gpointer user_data) { gint64 event_time = perf_log->start_time; GList *iter; for (iter = perf_log->blocks->head; iter; iter = iter->next) { CinnamonPerfBlock *block = iter->data; guint32 pos = 0; while (pos < block->bytes) { CinnamonPerfEvent *event; guint16 id; guint32 time_delta; GValue arg = { 0, }; memcpy (&time_delta, block->buffer + pos, sizeof (guint32)); pos += sizeof (guint32); memcpy (&id, block->buffer + pos, sizeof (guint16)); pos += sizeof (guint16); if (id == EVENT_SET_TIME) { /* Internal, we don't include in the replay */ memcpy (&event_time, block->buffer + pos, sizeof (gint64)); pos += sizeof (gint64); continue; } else { event_time += time_delta; } event = g_ptr_array_index (perf_log->events, id); if (strcmp (event->signature, "") == 0) { /* We need to pass something, so pass an empty string */ g_value_init (&arg, G_TYPE_STRING); } else if (strcmp (event->signature, "i") == 0) { gint32 l; memcpy (&l, block->buffer + pos, sizeof (gint32)); pos += sizeof (gint32); g_value_init (&arg, G_TYPE_INT); g_value_set_int (&arg, l); } else if (strcmp (event->signature, "x") == 0) { gint64 l; memcpy (&l, block->buffer + pos, sizeof (gint64)); pos += sizeof (gint64); g_value_init (&arg, G_TYPE_INT64); g_value_set_int64 (&arg, l); } else if (strcmp (event->signature, "s") == 0) { g_value_init (&arg, G_TYPE_STRING); g_value_set_string (&arg, (char *)block->buffer + pos); pos += strlen ((char *)(block->buffer + pos)) + 1; } replay_function (event_time, event->name, event->signature, &arg, user_data); g_value_unset (&arg); } } } static char * escape_quotes (const char *input) { GString *result; const char *p; if (strchr (input, '"') == NULL) return (char *)input; result = g_string_new (NULL); for (p = input; *p; p++) { if (*p == '"') g_string_append (result, "\\\""); else g_string_append_c (result, *p); } return g_string_free (result, FALSE); } static gboolean write_string (GOutputStream *out, const char *str, GError **error) { return g_output_stream_write_all (out, str, strlen (str), NULL, NULL, error); } /** * cinnamon_perf_log_dump_events: * @perf_log: a #CinnamonPerfLog * @out: output stream into which to write the event definitions * @error: location to store #GError, or %NULL * * Dump the definition of currently defined events and statistics, formatted * as JSON, to the specified output stream. The JSON output is an array, * with each element being a dictionary of the form: * * { name: , * description: events->len; i++) { CinnamonPerfEvent *event = g_ptr_array_index (perf_log->events, i); char *escaped_description = escape_quotes (event->description); gboolean is_statistic = g_hash_table_lookup (perf_log->statistics_by_name, event->name) != NULL; if (i != 0) g_string_append (output, ",\n "); g_string_append_printf (output, "{ \"name\": \"%s\",\n" " \"description\": \"%s\"", event->name, escaped_description); if (is_statistic) g_string_append (output, ",\n \"statistic\": true"); g_string_append (output, " }"); if (escaped_description != event->description) g_free (escaped_description); } g_string_append (output, " ]"); return write_string (out, g_string_free (output, FALSE), error); } typedef struct { GOutputStream *out; GError *error; gboolean first; } ReplayToJsonClosure; static void replay_to_json (gint64 time, const char *name, const char *signature, GValue *arg, gpointer user_data) { ReplayToJsonClosure *closure = user_data; char *event_str; if (closure->error != NULL) return; if (!closure->first) { if (!write_string (closure->out, ",\n ", &closure->error)) return; } closure->first = FALSE; if (strcmp (signature, "") == 0) { event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\"]", time, name); } else if (strcmp (signature, "i") == 0) { event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", %i]", time, name, g_value_get_int (arg)); } else if (strcmp (signature, "x") == 0) { event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", %"G_GINT64_FORMAT "]", time, name, g_value_get_int64 (arg)); } else if (strcmp (signature, "s") == 0) { const char *arg_str = g_value_get_string (arg); char *escaped = escape_quotes (arg_str); event_str = g_strdup_printf ("[%" G_GINT64_FORMAT ", \"%s\", \"%s\"]", time, name, g_value_get_string (arg)); if (escaped != arg_str) g_free (escaped); } else { g_assert_not_reached (); } if (!write_string (closure->out, event_str, &closure->error)) return; } /** * cinnamon_perf_log_dump_log: * @perf_log: a #CinnamonPerfLog * @out: output stream into which to write the event log * @error: location to store #GError, or %NULL * * Writes the performance event log, formatted as JSON, to the specified * output stream. For performance reasons, the output stream passed * in should generally be a buffered (or memory) output stream, since * it will be written to in small pieces. The JSON output is an array * with the elements of the array also being arrays, of the form * '[' ', '<b>mismatched</i>'); assertEscapes('mismatched', '<b>mismatched</i>'); assertConverts('mismatched/unknown', '<b>mismatched/unknown</bunknown>'); assertEscapes('mismatched/unknown', '<b>mismatched/unknown</bunknown>'); Cinnamon-2.8.6/tests/Makefile.am0000664000175000017500000000175412625666430015434 0ustar fabiofabionoinst_SCRIPTS = run-test.sh EXTRA_DIST = run-test.sh.in TEST_JS = \ interactive/borders.js \ interactive/border-radius.js \ interactive/border-width.js \ interactive/box-layout.js \ interactive/box-shadows.js \ interactive/calendar.js \ interactive/css-fonts.js \ interactive/entry.js \ interactive/icons.js \ interactive/inline-style.js \ interactive/scrolling.js \ interactive/scroll-view-sizing.js \ interactive/table.js \ interactive/transitions.js \ testcommon/border-image.png \ testcommon/face-plain.png \ testcommon/ui.js \ unit/format.js \ unit/markup.js \ unit/url.js EXTRA_DIST += $(TEST_JS) TEST_MISC = \ testcommon/test.css EXTRA_DIST += $(TEST_MISC) run-test.sh: run-test.sh.in $(AM_V_GEN) sed \ -e "s|@MUFFIN_TYPELIB_DIR[@]|$(MUFFIN_TYPELIB_DIR)|" \ -e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|" \ -e "s|@srcdir[@]|$(srcdir)|" \ $< > $@ && chmod a+x $@ CLEANFILES = run-test.sh Cinnamon-2.8.6/po/0000775000175000017500000000000012625666430012645 5ustar fabiofabioCinnamon-2.8.6/po/LINGUAS0000664000175000017500000000000012625666430013660 0ustar fabiofabioCinnamon-2.8.6/po/POTFILES.skip0000664000175000017500000000010412625666430014755 0ustar fabiofabiodata/cinnamon.desktop.in data/cinnamon-clock-preferences.desktop.in Cinnamon-2.8.6/po/POTFILES.in0000664000175000017500000000127112625666430014423 0ustar fabiofabiodata/cinnamon.desktop.in.in data/org.cinnamon.gschema.xml.in js/misc/util.js js/ui/appFavorites.js js/ui/autorunManager.js js/ui/calendar.js js/ui/endSessionDialog.js js/ui/extensionSystem.js js/ui/keyboard.js js/ui/lookingGlass.js js/ui/messageTray.js js/ui/networkAgent.js js/ui/notificationDaemon.js js/ui/overview.js js/ui/panel.js js/ui/polkitAuthenticationAgent.js js/ui/popupMenu.js js/ui/runDialog.js js/ui/cinnamonEntry.js js/ui/cinnamonMountOperation.js js/ui/windowAttentionHandler.js src/gvc/gvc-mixer-control.c src/main.c src/cinnamon-app.c src/cinnamon-app-system.c src/cinnamon-global.c src/cinnamon-mobile-providers.c src/cinnamon-polkit-authentication-agent.c src/cinnamon-util.c Cinnamon-2.8.6/po/README0000664000175000017500000000052512625666430013527 0ustar fabiofabioTranslations are done on Launchpad. From Launchpad we export two sets of files: - The mo files go to files/usr/share/locale/ and eventually end up in the Cinnamon package itself - The po files go to po/po-files/. They are not compiled into mo files (this is done by Launchpad) but are present in this archive as source for the mo files. Cinnamon-2.8.6/NEWS0000664000175000017500000000606212625666430012732 0ustar fabiofabioFixes needed ------------ - Administration and preference don't appear in menu editor - Move up and Move down buttons don't work in menu editor (if Gnome 3 broke that we need to switch back to the GTK2 version of this tool, as on the original fork) - Don't hide spotify in systray - Conky/mintupload windows appear (careful not to hide gimp utility windows though or make it configurable) - workspaces can overlap in expo grid - recommend muffin - depend on gsjdbus - muffin should depend on libmuffin0 and gir-muffin of the same version (currently it's possible to upgrade cinnamon and libmuffin without upgrading other muffin packages and it results in crashes) - Alt-arrow shortcuts don't work in nemo 1.8 ----- - New theme style class: "workspace-overview-background-shade" allows theming of workspace backgrounds in Scale and Expo. 1.6.1 ----- - Fixed: Administration category missing in menu - Fixed: Blur in Alt-Tab preview - Fixed: Network Manager applet consumes a lot of CPU - Fixed: Calendar applet consumes a lot of CPU - Updated translations - Flags in keyboard applet - Fixed: Removing workspace while editing its name results in crash - Added songbird support to sound applet - Fixed: Scrolling in window list applet brings all windows to the active workspace - Fixed: panels visible in firefox fullscreen mode x-shockwave-flash video - Fixed: two issues with dnd and themes using different background for hovering - Less CPU: We don't need to fetch the pointer actor, if track_hover is not set. Muffin 1.1.1 ------------ - Fixed: Menubar issues with all Java programs Nemo 1.0.2 ---------- - Dropbox extension - Fileroller extension - Configurable units (decimal by default as well as decimal-long, binary and binary-long) - Use Nautilus style to render the desktop when the theme doesn't support Nemo NEMO ---- - Add gtk bookmarks to right-click->copy-to/move-to TODO ---- - i18n: Update translations - i18n: Localize menu categories - Applets: Fix power applet appearing when applets are moved (code refactoring needed) - System: Fallback on Metacity when Cinnamon crashes - System: Improve logging (capture stderr output into log files) - Applet API: Allow applets to provide a gsettings schema (which cinnamon-settings compiles when the applet is installed) - Settings: Fix settings moved to gsettings since 3.4 changes in cinnamon-settings - Settings: In Cinnamon Settings, add the ability to delete themes, extensions and applets located in the user's home - Settings: In Cinnamon Settings, add the ability to view and install themes, extensions and applets from cinnamon-settings - Layout: Give Cinnamon WM tiling features (fill, grid..etc). - Fix menu crashing Cinnamon (show a category, Internet for instance, click elsewhere, open a terminal, install a program in that category, Opera for instance, open the menu) - Fix tooltips on GTK popups appearing behind the popups (and move tooltips when they appear on top of the panel) - Fix https://github.com/linuxmint/Cinnamon/issues/767 - Add one-hand keyboard-shortcuts for scale and expo Cinnamon-2.8.6/.gitignore0000664000175000017500000000433212625666430014221 0ustar fabiofabio.pc ABOUT-NLS Makefile Makefile.in aclocal.m4 autom4te.cache/ browser-plugin/* config.h config.h.in config.log config.status config/ configure data/Makefile data/Makefile.in data/cinnamon.desktop data/gschemas.compiled data/icons/Makefile data/org.cinnamon.gschema.valid data/org.cinnamon.gschema.xml data/theme/gdm.css docs/Makefile docs/reference/*/*-docs.sgml docs/reference/*/*.args docs/reference/*/*.hierarchy docs/reference/*/*.interfaces docs/reference/*/*.prerequisites docs/reference/*/*.signals docs/reference/*/*.stamp docs/reference/*/*.txt docs/reference/*/*.types docs/reference/*/.libs/ docs/reference/*/Makefile docs/reference/*/html/ docs/reference/*/xml/ docs/reference/Makefile docs/reference/cinnamon-js/__pycache__/ docs/reference/cinnamon-js/misc/ docs/reference/cinnamon-js/ui/ files/Makefile gtk-doc.make js/Makefile js/Makefile.in js/gdm/* js/misc/config.js libtool m4/ man/Makefile man/Makefile.in po/Makefile po/Makefile.in.in po/Makevars.template po/POTFILES po/Rules-quot po/boldquot.sed po/en@boldquot.header po/en@quot.header po/insert-header.sin po/quot.sed po/remove-potcdate.sin po/stamp-it src/*.gir src/*.la src/*.lo src/*.o src/*.typelib src/.deps/ src/.libs/ src/Makefile src/Makefile.in src/cinnamon src/cinnamon-enum-types.c src/cinnamon-enum-types.h src/cinnamon-extension-tool src/cinnamon-hotplug-sniffer src/cinnamon-marshal.c src/cinnamon-marshal.h src/cinnamon-perf-helper src/cinnamon-real src/cinnamon_perf_helper-cinnamon-perf-helper.o src/cinnamon_real-main.o src/gvc/*.lo src/gvc/.deps/ src/gvc/.dirstamp src/gvc/.libs/ src/hotplug-sniffer/*.o src/hotplug-sniffer/.deps/ src/hotplug-sniffer/.dirstamp src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service src/na-marshal.c src/na-marshal.h src/run-js-test src/run_js_test-run-js-test.o src/st-enum-types.c src/st-enum-types.h src/st-marshal.c src/st-marshal.h src/st.h src/st/*.lo src/st/.deps/ src/st/.dirstamp src/st/.libs/ src/st/test_theme-test-theme.o src/stamp-cinnamon-enum-types.h src/stamp-cinnamon-marshal.h src/stamp-na-marshal.h src/stamp-st-enum-types.h src/stamp-st-marshal.h src/stamp-st.h src/test-recorder src/test-theme src/tray/*.lo src/tray/.deps/ src/tray/.dirstamp src/tray/.libs/ stamp-h1 tests/Makefile tests/Makefile.in tests/run-test.sh