diff options
author | Anish Mangal <anish@activitycentral.com> | 2012-02-06 16:51:17 (GMT) |
---|---|---|
committer | Anish Mangal <anish@activitycentral.com> | 2012-02-06 16:51:17 (GMT) |
commit | b56201b47ee251d898752c0fc744820fbc001504 (patch) | |
tree | 09b1074ee0be130819929c7b3fcf335bbe51e2b1 | |
parent | 5560fbe670ffeb187ee4a95fa7118610a9459093 (diff) |
bump to sugar development patchset 20120206
82 files changed, 6956 insertions, 359 deletions
diff --git a/rpms/sugar/0001-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch b/rpms/sugar/0001-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch index e97ed18..c4ed86f 100644 --- a/rpms/sugar/0001-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch +++ b/rpms/sugar/0001-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch @@ -1,9 +1,9 @@ -From 8eb6a1d751038e94eb520fb29224f9082f0aab80 Mon Sep 17 00:00:00 2001 +From 2470ab3a11d6f6fa0c870ba5f0ed246f1c7d32ed Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 19 Oct 2011 14:35:09 +0200 -Subject: [PATCH sugar 01/74] Commit from Sugar Labs: Translation System by - user godiard.: 379 of 379 messages translated - (0 fuzzy). +Subject: [PATCH 01/82] Commit from Sugar Labs: Translation System by user + godiard.: 379 of 379 messages translated (0 fuzzy). +Organization: Sugar Labs Foundation --- po/es.po | 4 ++-- @@ -32,5 +32,5 @@ index dd798d7..83b28d3 100644 #: ../src/jarabe/view/viewsource.py:263 msgid "Instance Source" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0002-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch b/rpms/sugar/0002-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch index 32ec81d..05e8af9 100644 --- a/rpms/sugar/0002-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch +++ b/rpms/sugar/0002-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch @@ -1,9 +1,9 @@ -From 717c7d9539b5ec7b4ffcf8fad241cdc5ad9a0195 Mon Sep 17 00:00:00 2001 +From 8fff6fa38dd140409bccefd1a0a99f4facbd779c Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 19 Oct 2011 14:48:49 +0200 -Subject: [PATCH sugar 02/74] Commit from Sugar Labs: Translation System by - user godiard.: 379 of 379 messages translated - (0 fuzzy). +Subject: [PATCH 02/82] Commit from Sugar Labs: Translation System by user + godiard.: 379 of 379 messages translated (0 fuzzy). +Organization: Sugar Labs Foundation --- po/af.po | 1938 +++++++++++++++++++++++++++++++++++++++++--------- @@ -128359,5 +128359,5 @@ index bd5b1b2..cead223 100644 +#~ msgid "Unknown Mesh" +#~ msgstr "未知网状网络" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0003-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0003-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index e5345f5..1343296 100644 --- a/rpms/sugar/0003-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0003-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 7ee2e2f837a4d8c5d2ddd2136c211bdb1dd78a2d Mon Sep 17 00:00:00 2001 +From 067d58ed95c70682ca9a954d0809f5668ab06bae Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Fri, 28 Oct 2011 09:16:06 +0200 -Subject: [PATCH sugar 03/74] Commit from Sugar Labs: Translation System by - user cjl.: 227 of 379 messages translated (11 - fuzzy). +Subject: [PATCH 03/82] Commit from Sugar Labs: Translation System by user + cjl.: 227 of 379 messages translated (11 fuzzy). +Organization: Sugar Labs Foundation --- po/km.po | 605 ++++++++++++++++++++++++++++++++++---------------------------- @@ -1572,5 +1572,5 @@ index 3079c2a..06d730f 100644 #~ msgstr "បិត" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0004-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0004-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index 0dfe991..f87344d 100644 --- a/rpms/sugar/0004-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0004-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From d3f897b11d0d7c6fcbbb3cef359c2e9f75b6f50f Mon Sep 17 00:00:00 2001 +From 7bce7d5ca9b5b690ad427720786dca370983aeb4 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Sun, 20 Nov 2011 02:39:51 +0200 -Subject: [PATCH sugar 04/74] Commit from Sugar Labs: Translation System by - user cjl.: 202 of 379 messages translated (15 - fuzzy). +Subject: [PATCH 04/82] Commit from Sugar Labs: Translation System by user + cjl.: 202 of 379 messages translated (15 fuzzy). +Organization: Sugar Labs Foundation --- po/quz.po | 414 +++++++++++++++++++++++++++++++------------------------------ @@ -1481,5 +1481,5 @@ index dae29d9..8ef1d33 100644 -msgstr "" +msgstr "Juk qhawanapi Azucar" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0005-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0005-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index 04b155e..e58bc6b 100644 --- a/rpms/sugar/0005-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0005-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 5882cbad10552e6e97858287f8fa89377c897035 Mon Sep 17 00:00:00 2001 +From 05da5baffd45d31e09e81ed33477e23ce4a3a589 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 23 Nov 2011 07:44:44 +0200 -Subject: [PATCH sugar 05/74] Commit from Sugar Labs: Translation System by - user cjl.: 95 of 379 messages translated (5 - fuzzy). +Subject: [PATCH 05/82] Commit from Sugar Labs: Translation System by user + cjl.: 95 of 379 messages translated (5 fuzzy). +Organization: Sugar Labs Foundation --- po/aym.po | 684 +++++++++++++++---------------------------------------------- @@ -2277,5 +2277,5 @@ index 94a2580..9fbe07e 100644 -msgstr "" +msgstr "Mis'ki wakichawi" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0006-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0006-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index b8eff3d..86a4a56 100644 --- a/rpms/sugar/0006-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0006-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From d8a572d57a751b42fcd0b31e112f4ebc5243bbd3 Mon Sep 17 00:00:00 2001 +From 5cdfb641931df23caf6478f33b3d2eefbf626eea Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 23 Nov 2011 16:11:08 +0200 -Subject: [PATCH sugar 06/74] Commit from Sugar Labs: Translation System by - user cjl.: 202 of 379 messages translated (15 - fuzzy). +Subject: [PATCH 06/82] Commit from Sugar Labs: Translation System by user + cjl.: 202 of 379 messages translated (15 fuzzy). +Organization: Sugar Labs Foundation --- po/quz.po | 46 +++++++++++++++++++++++----------------------- @@ -214,5 +214,5 @@ index 8ef1d33..3e3a080 100644 # "Fuente de la instancia" #: ../src/jarabe/view/viewsource.py:263 -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0007-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0007-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index a9cdcb4..80c4876 100644 --- a/rpms/sugar/0007-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0007-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 9a0b85ff8b13eb5397e30dd93ae7ec61d62856e1 Mon Sep 17 00:00:00 2001 +From a6fe4afff3aafe2ebfec9beac4d8cdf0e677e188 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 23 Nov 2011 19:20:07 +0200 -Subject: [PATCH sugar 07/74] Commit from Sugar Labs: Translation System by - user cjl.: 96 of 379 messages translated (5 - fuzzy). +Subject: [PATCH 07/82] Commit from Sugar Labs: Translation System by user + cjl.: 96 of 379 messages translated (5 fuzzy). +Organization: Sugar Labs Foundation --- po/aym.po | 26 +++++++++++++------------- @@ -117,5 +117,5 @@ index 9fbe07e..959eaef 100644 #: ../src/jarabe/view/palettes.py:139 msgid "Start new" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0008-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0008-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index 0abbe79..0e682f3 100644 --- a/rpms/sugar/0008-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0008-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From b4261bc90bbad048f41ec5e0c8846b01b1cc900c Mon Sep 17 00:00:00 2001 +From 94a334f256cda8227babeede1db83f81fb435200 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Wed, 23 Nov 2011 19:23:10 +0200 -Subject: [PATCH sugar 08/74] Commit from Sugar Labs: Translation System by - user cjl.: 202 of 379 messages translated (15 - fuzzy). +Subject: [PATCH 08/82] Commit from Sugar Labs: Translation System by user + cjl.: 202 of 379 messages translated (15 fuzzy). +Organization: Sugar Labs Foundation --- po/quz.po | 20 +++++++++++--------- @@ -93,5 +93,5 @@ index 3e3a080..ca61ad1 100644 # "Fuente de la instancia" #: ../src/jarabe/view/viewsource.py:263 -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0009-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0009-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index b12f986..7f2e780 100644 --- a/rpms/sugar/0009-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0009-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 7d11f7a2d58e97df6df6f388f17a205aed2b1f80 Mon Sep 17 00:00:00 2001 +From f1f58e7eae8529d43824fc6e6248311dc02bc0c1 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Thu, 24 Nov 2011 05:24:53 +0200 -Subject: [PATCH sugar 09/74] Commit from Sugar Labs: Translation System by - user cjl.: 132 of 379 messages translated (6 - fuzzy). +Subject: [PATCH 09/82] Commit from Sugar Labs: Translation System by user + cjl.: 132 of 379 messages translated (6 fuzzy). +Organization: Sugar Labs Foundation --- po/aym.po | 78 ++++++++++++++++++++++++++++++------------------------------ @@ -326,5 +326,5 @@ index 959eaef..5033a2d 100644 #: ../src/jarabe/view/viewsource.py:374 msgid "Activity Bundle Source" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0010-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0010-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index 11031df..1d081a4 100644 --- a/rpms/sugar/0010-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0010-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From d327f755aad4813969a97ea6bae06b3c8fbd2213 Mon Sep 17 00:00:00 2001 +From 20d306ca5d288051e553fc9890ee2fd92cee3e7c Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Sat, 26 Nov 2011 08:33:12 +0200 -Subject: [PATCH sugar 10/74] Commit from Sugar Labs: Translation System by - user cjl.: 215 of 379 messages translated (7 - fuzzy). +Subject: [PATCH 10/82] Commit from Sugar Labs: Translation System by user + cjl.: 215 of 379 messages translated (7 fuzzy). +Organization: Sugar Labs Foundation --- po/quz.po | 27 ++++++++++----------------- @@ -148,5 +148,5 @@ index ca61ad1..8aac412 100644 # "La razón para el cambio de estado del dispositivo es desconocida." #: ../src/jarabe/model/network.py:163 -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0011-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0011-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index d3b32ff..c99cd69 100644 --- a/rpms/sugar/0011-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0011-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 07eb94622ab0f5863cfde3a773e2d28f65515154 Mon Sep 17 00:00:00 2001 +From d4eabea75f5671e8688a3f61c9c6477995d684ea Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Sun, 27 Nov 2011 01:20:11 +0200 -Subject: [PATCH sugar 11/74] Commit from Sugar Labs: Translation System by - user cjl.: 105 of 379 messages translated (16 - fuzzy). +Subject: [PATCH 11/82] Commit from Sugar Labs: Translation System by user + cjl.: 105 of 379 messages translated (16 fuzzy). +Organization: Sugar Labs Foundation --- po/te.po | 14 ++++++++------ @@ -60,5 +60,5 @@ index 9b88e94..15e14b7 100644 #: ../src/jarabe/desktop/schoolserver.py:143 msgid "Cannot connect to the server." -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0012-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch b/rpms/sugar/0012-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch index 9c5b821..6ba7512 100644 --- a/rpms/sugar/0012-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch +++ b/rpms/sugar/0012-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch @@ -1,9 +1,9 @@ -From 24ea1bc47ed29c3f670371cdf5dee63d6441dbb9 Mon Sep 17 00:00:00 2001 +From 2cca38df3a08b4e2b1ac15a38e8abc321d04d781 Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Mon, 28 Nov 2011 07:24:01 +0200 -Subject: [PATCH sugar 12/74] Commit from Sugar Labs: Translation System by - user cjl.: 187 of 379 messages translated (2 - fuzzy). +Subject: [PATCH 12/82] Commit from Sugar Labs: Translation System by user + cjl.: 187 of 379 messages translated (2 fuzzy). +Organization: Sugar Labs Foundation --- po/sq.po | 24 +++++++++++------------- @@ -113,5 +113,5 @@ index 30bf88c..0b4b67c 100644 #: ../src/jarabe/view/viewsource.py:263 msgid "Instance Source" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0013-Control-Panel-Power-section-check-if-powerd-is-avail.patch b/rpms/sugar/0013-Control-Panel-Power-section-check-if-powerd-is-avail.patch index 23a6f9e..97b79e7 100644 --- a/rpms/sugar/0013-Control-Panel-Power-section-check-if-powerd-is-avail.patch +++ b/rpms/sugar/0013-Control-Panel-Power-section-check-if-powerd-is-avail.patch @@ -1,8 +1,9 @@ -From ab59f75d4e6e7a95975a9311344f5a4f2120cd1c Mon Sep 17 00:00:00 2001 +From 4cd1d56de808b57c0a6ecc1e1141e3a79772d94e Mon Sep 17 00:00:00 2001 From: Simon Schampijer <simon@schampijer.de> Date: Mon, 28 Nov 2011 10:02:31 +0100 -Subject: [PATCH sugar 13/74] Control Panel, Power section: check if powerd is +Subject: [PATCH 13/82] Control Panel, Power section: check if powerd is available OLPC #11437 +Organization: Sugar Labs Foundation Recent versions of the kernel export the Open Firmware device tree to user space as /proc/device-tree, rather than /ofw (the latter was @@ -25,7 +26,7 @@ diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py index 2f55951..46810aa 100644 --- a/src/jarabe/controlpanel/gui.py +++ b/src/jarabe/controlpanel/gui.py -@@ -30,6 +30,7 @@ +@@ -30,6 +30,7 @@ from jarabe.controlpanel.toolbar import MainToolbar from jarabe.controlpanel.toolbar import SectionToolbar from jarabe import config @@ -33,7 +34,7 @@ index 2f55951..46810aa 100644 _logger = logging.getLogger('ControlPanel') -@@ -129,7 +130,7 @@ def _setup_main(self): +@@ -129,7 +130,7 @@ class ControlPanel(gtk.Window): self.__search_changed_cb) def _setup_options(self): @@ -43,5 +44,5 @@ index 2f55951..46810aa 100644 try: -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0014-Commit-from-Sugar-Labs-Translation-System-by-user-an.patch b/rpms/sugar/0014-Commit-from-Sugar-Labs-Translation-System-by-user-an.patch index 31842ec..5757434 100644 --- a/rpms/sugar/0014-Commit-from-Sugar-Labs-Translation-System-by-user-an.patch +++ b/rpms/sugar/0014-Commit-from-Sugar-Labs-Translation-System-by-user-an.patch @@ -1,9 +1,10 @@ -From b5a869340a931744e5b3e05a50d6bfffd78d7cdd Mon Sep 17 00:00:00 2001 +From 1e7a8f2fa17b0a2e6cbf8a9042a8d4a431277f3c Mon Sep 17 00:00:00 2001 From: Pootle daemon <pootle@pootle.sugarlabs.org> Date: Mon, 12 Dec 2011 15:43:31 +0200 -Subject: [PATCH sugar 14/74] Commit from Sugar Labs: Translation System by - user anush.mkrtchyan.: 377 of 379 messages - translated (2 fuzzy). +Subject: [PATCH 14/82] Commit from Sugar Labs: Translation System by user + anush.mkrtchyan.: 377 of 379 messages translated (2 + fuzzy). +Organization: Sugar Labs Foundation --- po/hy.po | 100 ++++++++++++++++++++++++++++++++----------------------------- @@ -383,5 +384,5 @@ index 9ee2940..652f85a 100644 #: ../src/jarabe/view/viewsource.py:400 ../src/jarabe/view/viewsource.py:401 #, python-format -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0015-Add-cpu-and-memory-resource-indicator-to-frame.patch b/rpms/sugar/0015-Add-cpu-and-memory-resource-indicator-to-frame.patch index 4aa6700..e9ed5a4 100644 --- a/rpms/sugar/0015-Add-cpu-and-memory-resource-indicator-to-frame.patch +++ b/rpms/sugar/0015-Add-cpu-and-memory-resource-indicator-to-frame.patch @@ -1,7 +1,8 @@ -From 29835fe3fd9c47665060836766ecc5cfd1fbb6c5 Mon Sep 17 00:00:00 2001 +From a1e6a9ce9c92d876149a9a13f1dfec9fde7ba0e6 Mon Sep 17 00:00:00 2001 From: anishmangal2002 <anishmangal2002@gmail.com> Date: Fri, 2 Jul 2010 20:45:28 +0530 -Subject: [PATCH sugar 15/74] Add cpu and memory resource indicator to frame +Subject: [PATCH 15/82] Add cpu and memory resource indicator to frame +Organization: Sugar Labs Foundation This patch adds an icon to the frame, whose palette menu displays the memory and cpu resources. For computing @@ -272,5 +273,5 @@ index 9e46831..b799339 100644 extensions/deviceicon/touchpad.py extensions/deviceicon/volume.py -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0016-set-default-scaling-to-100.patch.patch b/rpms/sugar/0016-set-default-scaling-to-100.patch.patch index 9c58df7..1f74eeb 100644 --- a/rpms/sugar/0016-set-default-scaling-to-100.patch.patch +++ b/rpms/sugar/0016-set-default-scaling-to-100.patch.patch @@ -1,7 +1,8 @@ -From 5265fbe6b0e59eb00baaf7c1f04cc239689d698b Mon Sep 17 00:00:00 2001 +From 696eb9a50de4f5e8a70654c6b9dbdcaed248956f Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 17:49:50 +0100 -Subject: [PATCH sugar 16/74] set-default-scaling-to-100.patch +Subject: [PATCH 16/82] set-default-scaling-to-100.patch +Organization: Sugar Labs Foundation --- bin/sugar.in | 2 +- @@ -21,5 +22,5 @@ index 12098db..d6295bf 100644 export GTK2_RC_FILES="@prefix@/share/sugar/data/sugar-$SUGAR_SCALING.gtkrc" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0017-Add-font-dpi-schema.patch b/rpms/sugar/0017-Add-font-dpi-schema.patch index 858b332..81b3bea 100644 --- a/rpms/sugar/0017-Add-font-dpi-schema.patch +++ b/rpms/sugar/0017-Add-font-dpi-schema.patch @@ -1,7 +1,8 @@ -From cd0ea7bfa330b45351a9aa61c85b39dfc376bf8d Mon Sep 17 00:00:00 2001 +From 7e4af894cd048f3fa8c0b1aed2e0370285cb5418 Mon Sep 17 00:00:00 2001 From: Jorge Saldivar <jsaldivar@paraguayeduca.org> Date: Fri, 18 Jun 2010 13:02:46 -0400 -Subject: [PATCH sugar 17/74] Add font dpi schema +Subject: [PATCH 17/82] Add font dpi schema +Organization: Sugar Labs Foundation --- data/sugar.schemas.in | 11 +++++++++++ @@ -30,5 +31,5 @@ index 8b3e1ad..c9bc7fe 100644 <schema> <key>/schemas/desktop/sugar/i18n/langpackdir</key> -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0018-Change-the-method-to-add-new-actitivities-in-the-act.patch b/rpms/sugar/0018-Change-the-method-to-add-new-actitivities-in-the-act.patch index fe9b7ed..dd49c6f 100644 --- a/rpms/sugar/0018-Change-the-method-to-add-new-actitivities-in-the-act.patch +++ b/rpms/sugar/0018-Change-the-method-to-add-new-actitivities-in-the-act.patch @@ -1,8 +1,9 @@ -From f5aee59dea8bb3c11cd67a9faa48d18afbd34cbc Mon Sep 17 00:00:00 2001 +From 0c77dec511f32a8d425d8c60120789e69b233ace Mon Sep 17 00:00:00 2001 From: Jorge Saldivar <jsaldivar@paraguayeduca.org> Date: Thu, 1 Jul 2010 17:39:24 -0400 -Subject: [PATCH sugar 18/74] Change the method to add new actitivities in the +Subject: [PATCH 18/82] Change the method to add new actitivities in the activities list in home view +Organization: Sugar Labs Foundation --- src/jarabe/desktop/activitieslist.py | 2 +- @@ -12,7 +13,7 @@ diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitie index b3b1f9d..44b0725 100644 --- a/src/jarabe/desktop/activitieslist.py +++ b/src/jarabe/desktop/activitieslist.py -@@ -222,7 +222,7 @@ def _add_activity(self, activity_info): +@@ -222,7 +222,7 @@ class ListModel(gtk.TreeModelSort): '<span style="italic" weight="light">%s</span>' % \ (activity_info.get_name(), tags) @@ -22,5 +23,5 @@ index b3b1f9d..44b0725 100644 activity_info.get_icon(), title, -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch b/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch index 4b1594a..45167c8 100644 --- a/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch +++ b/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch @@ -1,7 +1,8 @@ -From a0a18343e1823367813de3ec3cea579202ae5ea4 Mon Sep 17 00:00:00 2001 +From d82aaaf497a4f284110d2216acce3eb36c5cb3de Mon Sep 17 00:00:00 2001 From: Martin Abente <mabente@paraguayeduca.org> Date: Tue, 22 Jun 2010 15:59:13 -0400 -Subject: [PATCH sugar 19/74] Journal Volumes Backup and Restore +Subject: [PATCH 19/82] Journal Volumes Backup and Restore +Organization: Sugar Labs Foundation Add a basic backup and restore feature for the Sugar Journal. It provides: @@ -497,7 +498,7 @@ diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumesto index 71b6ea8..48e25ec 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py -@@ -36,7 +36,7 @@ +@@ -36,7 +36,7 @@ from sugar.graphics.xocolor import XoColor from sugar import env from jarabe.journal import model @@ -506,7 +507,7 @@ index 71b6ea8..48e25ec 100644 _JOURNAL_0_METADATA_DIR = '.olpc.store' -@@ -341,7 +341,7 @@ def __init__(self, mount): +@@ -341,7 +341,7 @@ class VolumeButton(BaseButton): self.props.xo_color = color def create_palette(self): @@ -642,7 +643,7 @@ index 3195c0c..a9e3629 100644 # # 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 -@@ -31,6 +33,7 @@ +@@ -31,6 +33,7 @@ from sugar.graphics import style from sugar.graphics.xocolor import XoColor from sugar.activity.i18n import pgettext @@ -650,7 +651,7 @@ index 3195c0c..a9e3629 100644 from jarabe.model import shell from jarabe.view.viewsource import setup_view_source from jarabe.journal import misc -@@ -253,3 +256,44 @@ def __popup_cb(self, palette): +@@ -253,3 +256,44 @@ class VolumePalette(Palette): self._progress_bar.props.fraction = fraction self._free_space_label.props.label = _('%(free_space)d MB Free') % \ {'free_space': free_space / (1024 * 1024)} @@ -696,5 +697,5 @@ index 3195c0c..a9e3629 100644 + dialog = VolumeRestoreDialog(mount_path) + dialog.show() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0020-Journal-XS-backup-and-restore.patch b/rpms/sugar/0020-Journal-XS-backup-and-restore.patch index 9d253ee..686251d 100644 --- a/rpms/sugar/0020-Journal-XS-backup-and-restore.patch +++ b/rpms/sugar/0020-Journal-XS-backup-and-restore.patch @@ -1,7 +1,8 @@ -From b7560ab35c80232c3c5b223207e5f4a4259f24b5 Mon Sep 17 00:00:00 2001 +From f5f305267ddc2a1a872183fdf7341fb1ba31675c Mon Sep 17 00:00:00 2001 From: Martin Abente <mabente@paraguayeduca.org> Date: Wed, 23 Jun 2010 17:12:59 -0400 -Subject: [PATCH sugar 20/74] Journal XS backup and restore +Subject: [PATCH 20/82] Journal XS backup and restore +Organization: Sugar Labs Foundation Include a new journal volumes toolbar icon. The icon represents the schoolserver. The icon palette provides a backup and restore @@ -17,7 +18,7 @@ diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesv index 132d14f..75795cd 100644 --- a/src/jarabe/desktop/favoritesview.py +++ b/src/jarabe/desktop/favoritesview.py -@@ -41,6 +41,7 @@ +@@ -41,6 +41,7 @@ from jarabe.view.buddymenu import BuddyMenu from jarabe.model.buddy import get_owner_instance from jarabe.model import shell from jarabe.model import bundleregistry @@ -25,7 +26,7 @@ index 132d14f..75795cd 100644 from jarabe.journal import misc from jarabe.desktop import schoolserver -@@ -342,6 +343,7 @@ def __register_activate_cb(self, icon): +@@ -342,6 +343,7 @@ class FavoritesView(hippo.Canvas): alert.props.msg = _('You are now registered ' \ 'with your school server.') self._my_icon.set_registered() @@ -37,7 +38,7 @@ diff --git a/src/jarabe/journal/processdialog.py b/src/jarabe/journal/processdia index b96abd9..08c6d11 100644 --- a/src/jarabe/journal/processdialog.py +++ b/src/jarabe/journal/processdialog.py -@@ -246,3 +246,33 @@ def _resetup_information(self, volume_path): +@@ -246,3 +246,33 @@ class VolumeRestoreDialog(ProcessDialog): self._message.set_markup('%s %s.\n\n' % (_('Journal content will be restored from'), volume_path) + \ '<big><b>%s</b> %s</big>' % (_('Warning:'), _('Current Journal content will be deleted!'))) @@ -75,7 +76,7 @@ diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumesto index 48e25ec..1356099 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py -@@ -30,13 +30,15 @@ +@@ -30,13 +30,15 @@ import simplejson import tempfile import shutil @@ -92,7 +93,7 @@ index 48e25ec..1356099 100644 _JOURNAL_0_METADATA_DIR = '.olpc.store' -@@ -182,6 +184,7 @@ def __init__(self): +@@ -182,6 +184,7 @@ class VolumesToolbar(gtk.Toolbar): self.connect('destroy', self.__destroy_cb) @@ -100,7 +101,7 @@ index 48e25ec..1356099 100644 gobject.idle_add(self._set_up_volumes) def __destroy_cb(self, widget): -@@ -189,6 +192,21 @@ def __destroy_cb(self, widget): +@@ -189,6 +192,21 @@ class VolumesToolbar(gtk.Toolbar): volume_monitor.disconnect(self._mount_added_hid) volume_monitor.disconnect(self._mount_removed_hid) @@ -122,7 +123,7 @@ index 48e25ec..1356099 100644 def _set_up_volumes(self): self._set_up_documents_button() -@@ -260,8 +278,9 @@ def __unmount_cb(self, source, result): +@@ -260,8 +278,9 @@ class VolumesToolbar(gtk.Toolbar): def _get_button_for_mount(self, mount): mount_point = mount.get_root().get_path() for button in self.get_children(): @@ -134,7 +135,7 @@ index 48e25ec..1356099 100644 logging.error('Couldnt find button with mount_point %r', mount_point) return None -@@ -402,3 +421,17 @@ def __init__(self, documents_path): +@@ -402,3 +421,17 @@ class DocumentsButton(BaseButton): client = gconf.client_get_default() color = XoColor(client.get_string('/desktop/sugar/user/color')) self.props.xo_color = color @@ -156,7 +157,7 @@ diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py index a9e3629..02648b7 100644 --- a/src/jarabe/view/palettes.py +++ b/src/jarabe/view/palettes.py -@@ -33,7 +33,7 @@ +@@ -33,7 +33,7 @@ from sugar.graphics import style from sugar.graphics.xocolor import XoColor from sugar.activity.i18n import pgettext @@ -165,7 +166,7 @@ index a9e3629..02648b7 100644 from jarabe.model import shell from jarabe.view.viewsource import setup_view_source from jarabe.journal import misc -@@ -297,3 +297,42 @@ def __journal_backup_activate_cb(self, menu_item, mount_path): +@@ -297,3 +297,42 @@ class JournalVolumePalette(VolumePalette): def __journal_restore_activate_cb(self, menu_item, mount_path): dialog = VolumeRestoreDialog(mount_path) dialog.show() @@ -209,5 +210,5 @@ index a9e3629..02648b7 100644 + dialog = XSRestoreDialog(xs_hostname) + dialog.show() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0021-Processdialog-prerequisite-check-support.patch b/rpms/sugar/0021-Processdialog-prerequisite-check-support.patch index eb21667..97b4b24 100644 --- a/rpms/sugar/0021-Processdialog-prerequisite-check-support.patch +++ b/rpms/sugar/0021-Processdialog-prerequisite-check-support.patch @@ -1,7 +1,8 @@ -From aa883ed6be6ef81692cf92f0ead21954331a6081 Mon Sep 17 00:00:00 2001 +From 8dec6faf44edd4f1477d46f9644368146e3e7de6 Mon Sep 17 00:00:00 2001 From: Martin Abente <mabente@paraguayeduca.org> Date: Thu, 29 Jul 2010 17:20:45 -0400 -Subject: [PATCH sugar 21/74] Processdialog prerequisite check support +Subject: [PATCH 21/82] Processdialog prerequisite check support +Organization: Sugar Labs Foundation --- src/jarabe/journal/processdialog.py | 23 ++++++++++++++++++++--- @@ -11,7 +12,7 @@ diff --git a/src/jarabe/journal/processdialog.py b/src/jarabe/journal/processdia index 08c6d11..181174d 100644 --- a/src/jarabe/journal/processdialog.py +++ b/src/jarabe/journal/processdialog.py -@@ -48,6 +48,7 @@ def __init__(self, process_script='', process_params=[], restart_after=True): +@@ -48,6 +48,7 @@ class ProcessDialog(gtk.Window): self._start_message = _('Running') self._failed_message = _('Failed') self._finished_message = _('Finished') @@ -19,7 +20,7 @@ index 08c6d11..181174d 100644 self.set_border_width(style.LINE_WIDTH) width = gtk.gdk.screen_width() -@@ -125,7 +126,7 @@ def _setup_options(self): +@@ -125,7 +126,7 @@ class ProcessDialog(gtk.Window): self._close_button = gtk.Button() self._close_button.set_image(icon) @@ -28,7 +29,7 @@ index 08c6d11..181174d 100644 self._close_button.connect('clicked', self.__close_cb) self._close_button.show() -@@ -165,12 +166,18 @@ def __close_cb(self, button): +@@ -165,12 +166,18 @@ class ProcessDialog(gtk.Window): self.destroy() def __start_cb(self, button): @@ -48,7 +49,7 @@ index 08c6d11..181174d 100644 def _set_status_started(self, model, data=None): self._message.set_markup(self._start_message) -@@ -199,12 +206,13 @@ def _set_status_finished(self, model, data=None): +@@ -199,12 +206,13 @@ class ProcessDialog(gtk.Window): else: self._close_button.show() @@ -63,7 +64,7 @@ index 08c6d11..181174d 100644 logging.error(error_message) -@@ -246,6 +254,11 @@ def _resetup_information(self, volume_path): +@@ -246,6 +254,11 @@ class VolumeRestoreDialog(ProcessDialog): self._message.set_markup('%s %s.\n\n' % (_('Journal content will be restored from'), volume_path) + \ '<big><b>%s</b> %s</big>' % (_('Warning:'), _('Current Journal content will be deleted!'))) @@ -75,7 +76,7 @@ index 08c6d11..181174d 100644 class XSBackupDialog(ProcessDialog): def __init__(self, xs_hostname): -@@ -276,3 +289,7 @@ def _resetup_information(self, xs_hostname): +@@ -276,3 +289,7 @@ class XSRestoreDialog(ProcessDialog): self._message.set_text('%s %s.' % (_('Journal content will be restored from'), xs_hostname)) @@ -84,5 +85,5 @@ index 08c6d11..181174d 100644 + def _check_prerequisites(self): + return len(shell.get_model()) <= 1 -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0022-Journal-Backup-save-lease.patch b/rpms/sugar/0022-Journal-Backup-save-lease.patch index 7dbc7c8..332f261 100644 --- a/rpms/sugar/0022-Journal-Backup-save-lease.patch +++ b/rpms/sugar/0022-Journal-Backup-save-lease.patch @@ -1,7 +1,8 @@ -From ad40835c07112c886bdf404cfd7eae4f14d75626 Mon Sep 17 00:00:00 2001 +From 7efbd5c3601a611a093bfd77054a0e3609a26dd2 Mon Sep 17 00:00:00 2001 From: Martin Abente <mabente@paraguayeduca.org> Date: Tue, 13 Jul 2010 11:02:27 -0400 -Subject: [PATCH sugar 22/74] Journal Backup save lease +Subject: [PATCH 22/82] Journal Backup save lease +Organization: Sugar Labs Foundation This feature was requested by Caacupe CATS. --- @@ -33,5 +34,5 @@ index 9246760..7c5d32a 100644 #subprocess.call(['pkill', '-9', '-f', 'python.*datastore-service']) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0023-backup-translations.patch.patch b/rpms/sugar/0023-backup-translations.patch.patch index db3f0d8..ce238da 100644 --- a/rpms/sugar/0023-backup-translations.patch.patch +++ b/rpms/sugar/0023-backup-translations.patch.patch @@ -1,7 +1,8 @@ -From ce7f28ee6bc5ec4dc9c8a897f17de22f18aeea77 Mon Sep 17 00:00:00 2001 +From 4f5d6e79e77e289c30f873e35f1b325d0f494e71 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:26:05 +0100 -Subject: [PATCH sugar 23/74] backup-translations.patch +Subject: [PATCH 23/82] backup-translations.patch +Organization: Sugar Labs Foundation --- po/es.po | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- @@ -105,5 +106,5 @@ index 83b28d3..474d28f 100644 # priority over the normal wireless device. NM doesn't have a "disconnect" # method for a device either (for various reasons) so this doesn't -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0024-accessibility_0001_cp_accessibility_keyboard.patch.patch b/rpms/sugar/0024-accessibility_0001_cp_accessibility_keyboard.patch.patch index 67b1f2e..078c38a 100644 --- a/rpms/sugar/0024-accessibility_0001_cp_accessibility_keyboard.patch.patch +++ b/rpms/sugar/0024-accessibility_0001_cp_accessibility_keyboard.patch.patch @@ -1,7 +1,8 @@ -From 0ebe85157993bc1793488edbe479ed9149e5bf43 Mon Sep 17 00:00:00 2001 +From 2e53dd59f803a3abf559f8ac74b193dea1b5a354 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 17:56:53 +0100 -Subject: [PATCH sugar 24/74] accessibility_0001_cp_accessibility_keyboard.patch +Subject: [PATCH 24/82] accessibility_0001_cp_accessibility_keyboard.patch +Organization: Sugar Labs Foundation --- bin/sugar-session | 7 ++ @@ -519,5 +520,5 @@ index 0000000..b851b3c + keyboard = Keyboard() + keyboard.run_config_keyboard() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0025-accessibility_0002_magnifier_extension.patch.patch b/rpms/sugar/0025-accessibility_0002_magnifier_extension.patch.patch index 9a2a432..10a02fa 100644 --- a/rpms/sugar/0025-accessibility_0002_magnifier_extension.patch.patch +++ b/rpms/sugar/0025-accessibility_0002_magnifier_extension.patch.patch @@ -1,7 +1,8 @@ -From efd8f0a023902aae41d07bc576908e6b067e67aa Mon Sep 17 00:00:00 2001 +From 2c84cd708300e3caae117fbc2f85d81539b80f91 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 17:59:06 +0100 -Subject: [PATCH sugar 25/74] accessibility_0002_magnifier_extension.patch +Subject: [PATCH 25/82] accessibility_0002_magnifier_extension.patch +Organization: Sugar Labs Foundation --- extensions/globalkey/Makefile.am | 1 + @@ -123,5 +124,5 @@ index 0000000..c25d9e7 + return looking_path + return None -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0026-accessibility_0003_cp_accessibility_contrast.patch.patch b/rpms/sugar/0026-accessibility_0003_cp_accessibility_contrast.patch.patch index 7659473..c8a73f5 100644 --- a/rpms/sugar/0026-accessibility_0003_cp_accessibility_contrast.patch.patch +++ b/rpms/sugar/0026-accessibility_0003_cp_accessibility_contrast.patch.patch @@ -1,7 +1,8 @@ -From 5e5effd135a455a3d18c02dc8c24248497a7cf57 Mon Sep 17 00:00:00 2001 +From 672394813bff30a2d00a2ed2baa08cf66db9a132 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:20:13 +0100 -Subject: [PATCH sugar 26/74] accessibility_0003_cp_accessibility_contrast.patch +Subject: [PATCH 26/82] accessibility_0003_cp_accessibility_contrast.patch +Organization: Sugar Labs Foundation [fix shell syntax] Signed-off-by: Sascha Silbe <sascha-pgp@silbe.org> @@ -137,7 +138,7 @@ diff --git a/extensions/cpsection/accessibility/view.py b/extensions/cpsection/a index 9f291ac..bae14dc 100644 --- a/extensions/cpsection/accessibility/view.py +++ b/extensions/cpsection/accessibility/view.py -@@ -23,7 +23,6 @@ +@@ -23,7 +23,6 @@ from gettext import gettext as _ from sugar.graphics import style @@ -145,7 +146,7 @@ index 9f291ac..bae14dc 100644 from jarabe.controlpanel.sectionview import SectionView from jarabe.controlpanel.inlinealert import InlineAlert -@@ -32,29 +31,73 @@ def __init__(self, model, alerts=None): +@@ -32,29 +31,73 @@ class accessibility(SectionView): SectionView.__init__(self) self._model = model @@ -233,7 +234,7 @@ index 9f291ac..bae14dc 100644 def _set_mouse_keys(self, widget): state = widget.get_active() -@@ -68,22 +111,35 @@ def _set_bounce_keys(self, widget): +@@ -68,22 +111,35 @@ class accessibility(SectionView): state = widget.get_active() self._model.set_bounce_keys(state) @@ -271,7 +272,7 @@ index 9f291ac..bae14dc 100644 lbl_mouse.show() def _view_sticky_keys(self): -@@ -91,13 +147,13 @@ def _view_sticky_keys(self): +@@ -91,13 +147,13 @@ class accessibility(SectionView): self._sticky_pm_change_handler = self.btn_sticky_keys.connect("toggled", self._set_sticky_keys) self.init_state_sticky_keys = self._model.get_sticky_keys() self.btn_sticky_keys.set_active(self.init_state_sticky_keys) @@ -287,7 +288,7 @@ index 9f291ac..bae14dc 100644 lbl_sticky.show() def _view_bounce_keys(self): -@@ -105,10 +161,28 @@ def _view_bounce_keys(self): +@@ -105,10 +161,28 @@ class accessibility(SectionView): self._bounce_pm_change_handler = self.btn_bounce_keys.connect("toggled", self._set_bounce_keys) self.init_state_bounce_keys = self._model.get_bounce_keys() self.btn_bounce_keys.set_active(self.init_state_bounce_keys) @@ -328,7 +329,7 @@ index 46810aa..61520e0 100644 # # 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 -@@ -242,7 +243,7 @@ def show_section_view(self, option): +@@ -242,7 +243,7 @@ class ControlPanel(gtk.Window): self._section_view.connect('request-close', self.__close_request_cb) self._main_view.modify_bg(gtk.STATE_NORMAL, @@ -348,7 +349,7 @@ index 44b0725..e34abd6 100644 # # 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 -@@ -269,7 +270,7 @@ def __init__(self, tree_view): +@@ -269,7 +270,7 @@ class CellRendererActivityIcon(CellRendererIcon): self.props.width = style.GRID_CELL_SIZE self.props.height = style.GRID_CELL_SIZE self.props.size = style.STANDARD_ICON_SIZE @@ -368,7 +369,7 @@ index 75795cd..c2ff370 100644 # # 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 -@@ -427,7 +428,7 @@ def __get_last_activity_error_handler_cb(self, error): +@@ -427,7 +428,7 @@ class ActivityIcon(CanvasIcon): def _update(self): self.palette = None if not self._resume_mode or not self._journal_entries: @@ -377,7 +378,7 @@ index 75795cd..c2ff370 100644 style.COLOR_TRANSPARENT.get_svg())) else: xo_color = misc.get_icon_color(self._journal_entries[0]) -@@ -536,7 +537,7 @@ def __init__(self, activity_info, journal_entries): +@@ -536,7 +537,7 @@ class FavoritePalette(ActivityPalette): ActivityPalette.__init__(self, activity_info) if not journal_entries: @@ -399,7 +400,7 @@ index b851b3c..0324495 100644 class Keyboard: def get_mouse_keys(self): -@@ -67,6 +65,24 @@ def run_config_keyboard(self): +@@ -67,6 +65,24 @@ class Keyboard: cmd += ['-mousekeys', 'mousemaxspeed', '3000', 'mousetimetomax', '1000', '+timeout', '+repeatkeys'] subprocess.call(cmd) @@ -425,5 +426,5 @@ index b851b3c..0324495 100644 def setup_accessibility(self): client = gconf.client_get_default() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0027-accessibility_0004_cp_accessibility_mouse.patch.patch b/rpms/sugar/0027-accessibility_0004_cp_accessibility_mouse.patch.patch index d8cabbf..6191192 100644 --- a/rpms/sugar/0027-accessibility_0004_cp_accessibility_mouse.patch.patch +++ b/rpms/sugar/0027-accessibility_0004_cp_accessibility_mouse.patch.patch @@ -1,7 +1,8 @@ -From 9e33a1bc941bdd0f4a9a15be83742d290c112142 Mon Sep 17 00:00:00 2001 +From 4ea9b855934afac206a86fb99dafbbc0e75f58db Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:22:25 +0100 -Subject: [PATCH sugar 27/74] accessibility_0004_cp_accessibility_mouse.patch +Subject: [PATCH 27/82] accessibility_0004_cp_accessibility_mouse.patch +Organization: Sugar Labs Foundation [fixed shell script syntax] Signed-off-by: Sascha Silbe <sascha-pgp@silbe.org> @@ -63,7 +64,7 @@ diff --git a/extensions/cpsection/accessibility/model.py b/extensions/cpsection/ index 8769d24..6e5b154 100644 --- a/extensions/cpsection/accessibility/model.py +++ b/extensions/cpsection/accessibility/model.py -@@ -21,8 +21,9 @@ +@@ -21,8 +21,9 @@ from jarabe.model import accessibility keyboard = accessibility.Keyboard() screen = accessibility.Screen() @@ -104,7 +105,7 @@ diff --git a/extensions/cpsection/accessibility/view.py b/extensions/cpsection/a index bae14dc..23de47e 100644 --- a/extensions/cpsection/accessibility/view.py +++ b/extensions/cpsection/accessibility/view.py -@@ -57,6 +57,7 @@ def __init__(self, model, alerts=None): +@@ -57,6 +57,7 @@ class accessibility(SectionView): self._view_keyboard_options() self._view_screen_options() @@ -112,7 +113,7 @@ index bae14dc..23de47e 100644 def _view_keyboard_options(self): -@@ -98,6 +99,26 @@ def _view_screen_options(self): +@@ -98,6 +99,26 @@ class accessibility(SectionView): self._vbox_section.pack_start(self.box_pm_screen, expand=False) self.box_pm_screen.show() @@ -139,7 +140,7 @@ index bae14dc..23de47e 100644 def _set_mouse_keys(self, widget): state = widget.get_active() -@@ -119,6 +140,17 @@ def _set_contrast(self, widget): +@@ -119,6 +140,17 @@ class accessibility(SectionView): self._zone_alert.props.msg = self.restart_msg self._zone_alert.show() @@ -157,7 +158,7 @@ index bae14dc..23de47e 100644 def undo(self): self._model.set_mouse_keys(self.init_state_mouse_keys) self._model.set_sticky_keys(self.init_state_sticky_keys) -@@ -126,6 +158,12 @@ def undo(self): +@@ -126,6 +158,12 @@ class accessibility(SectionView): self._model.set_contrast(self.init_state_contrast) self.btn_contrast.set_active(self.init_state_contrast) @@ -170,7 +171,7 @@ index bae14dc..23de47e 100644 self.needs_restart = False self._zone_alert.hide() -@@ -186,3 +224,45 @@ def _view_contrast(self): +@@ -186,3 +224,45 @@ class accessibility(SectionView): lbl_contrast.set_alignment(0, 0) self.box_pm_screen.pack_start(lbl_contrast, True, True, 2) lbl_contrast.show() @@ -229,7 +230,7 @@ index 0324495..c06f5c0 100644 import subprocess import gconf -@@ -67,8 +68,10 @@ def run_config_keyboard(self): +@@ -67,8 +68,10 @@ class Keyboard: class Screen: @@ -241,7 +242,7 @@ index 0324495..c06f5c0 100644 def get_contrast(self): client = gconf.client_get_default() -@@ -79,9 +82,52 @@ def set_contrast(self, activar): +@@ -79,9 +82,52 @@ class Screen: client = gconf.client_get_default() if (activar): client.set_string("/desktop/sugar/interface/gtk_theme", self.CONTRAST_THEME) @@ -294,12 +295,12 @@ index 0324495..c06f5c0 100644 class AccessibilityManager: def setup_accessibility(self): -@@ -90,3 +136,5 @@ def setup_accessibility(self): +@@ -90,3 +136,5 @@ class AccessibilityManager: if is_accessibility: keyboard = Keyboard() keyboard.run_config_keyboard() + mouse = Mouse() + mouse.run_config_mouse() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0028-accessibility_0005_cp_accessibility_capital_letters..patch b/rpms/sugar/0028-accessibility_0005_cp_accessibility_capital_letters..patch index 395e27c..b5703aa 100644 --- a/rpms/sugar/0028-accessibility_0005_cp_accessibility_capital_letters..patch +++ b/rpms/sugar/0028-accessibility_0005_cp_accessibility_capital_letters..patch @@ -1,7 +1,8 @@ -From 1de0246a0af984e6f9807059095dc4e492eadd27 Mon Sep 17 00:00:00 2001 +From 414ae95a09d09543af8e45cc70e7891aeaa97208 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:22:54 +0100 -Subject: [PATCH sugar 28/74] accessibility_0005_cp_accessibility_capital_letters.patch +Subject: [PATCH 28/82] accessibility_0005_cp_accessibility_capital_letters.patch +Organization: Sugar Labs Foundation --- extensions/cpsection/accessibility/model.py | 11 +++++++- @@ -13,7 +14,7 @@ diff --git a/extensions/cpsection/accessibility/model.py b/extensions/cpsection/ index 6e5b154..9afec04 100644 --- a/extensions/cpsection/accessibility/model.py +++ b/extensions/cpsection/accessibility/model.py -@@ -23,7 +23,7 @@ +@@ -23,7 +23,7 @@ keyboard = accessibility.Keyboard() screen = accessibility.Screen() mouse = accessibility.Mouse() @@ -42,7 +43,7 @@ diff --git a/extensions/cpsection/accessibility/view.py b/extensions/cpsection/a index 23de47e..f06f7ae 100644 --- a/extensions/cpsection/accessibility/view.py +++ b/extensions/cpsection/accessibility/view.py -@@ -96,6 +96,7 @@ def _view_screen_options(self): +@@ -96,6 +96,7 @@ class accessibility(SectionView): self.box_pm_screen.set_spacing(style.DEFAULT_SPACING) self._view_contrast() @@ -50,7 +51,7 @@ index 23de47e..f06f7ae 100644 self._vbox_section.pack_start(self.box_pm_screen, expand=False) self.box_pm_screen.show() -@@ -140,6 +141,14 @@ def _set_contrast(self, widget): +@@ -140,6 +141,14 @@ class accessibility(SectionView): self._zone_alert.props.msg = self.restart_msg self._zone_alert.show() @@ -65,7 +66,7 @@ index 23de47e..f06f7ae 100644 def _set_white_mouse(self, widget): state = widget.get_active() self._model.set_white_mouse(state) -@@ -159,6 +168,9 @@ def undo(self): +@@ -159,6 +168,9 @@ class accessibility(SectionView): self._model.set_contrast(self.init_state_contrast) self.btn_contrast.set_active(self.init_state_contrast) @@ -75,7 +76,7 @@ index 23de47e..f06f7ae 100644 self._model.set_white_mouse(self.init_state_white_mouse) self.btn_white_mouse.set_active(self.init_state_white_mouse) -@@ -225,6 +237,24 @@ def _view_contrast(self): +@@ -225,6 +237,24 @@ class accessibility(SectionView): self.box_pm_screen.pack_start(lbl_contrast, True, True, 2) lbl_contrast.show() @@ -100,7 +101,7 @@ index 23de47e..f06f7ae 100644 def _view_white_mouse(self): self.btn_white_mouse = gtk.CheckButton(_('White Mouse')) self._white_mouse_pm_change_handler = self.btn_white_mouse.connect("toggled", self._set_white_mouse) -@@ -265,4 +295,7 @@ def _view_acceleration_mouse(self): +@@ -265,4 +295,7 @@ class accessibility(SectionView): desc_accel_mouse = gtk.Label(_('Controller acceleration mouse.')) desc_accel_mouse.set_alignment(0, 0) self.box_pm_mouse.pack_start(desc_accel_mouse, True, True, 2) @@ -115,7 +116,7 @@ diff --git a/src/jarabe/model/accessibility.py b/src/jarabe/model/accessibility. index c06f5c0..299f8d4 100644 --- a/src/jarabe/model/accessibility.py +++ b/src/jarabe/model/accessibility.py -@@ -68,10 +68,12 @@ def run_config_keyboard(self): +@@ -68,10 +68,12 @@ class Keyboard: class Screen: @@ -132,7 +133,7 @@ index c06f5c0..299f8d4 100644 def get_contrast(self): client = gconf.client_get_default() -@@ -87,6 +89,19 @@ def set_contrast(self, activar): +@@ -87,6 +89,19 @@ class Screen: client.set_string("/desktop/sugar/interface/gtk_theme", self.DEFAULT_THEME) client.set_float('/desktop/sugar/font/default_size', self.DEFAULT_FONT_SIZE) @@ -153,5 +154,5 @@ index c06f5c0..299f8d4 100644 WHITE_CURSOR_THEME="FlatbedCursors.White.Huge" -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0029-accessibility_0006_virtualkeyboard.patch.patch b/rpms/sugar/0029-accessibility_0006_virtualkeyboard.patch.patch index faa7fb0..0510813 100644 --- a/rpms/sugar/0029-accessibility_0006_virtualkeyboard.patch.patch +++ b/rpms/sugar/0029-accessibility_0006_virtualkeyboard.patch.patch @@ -1,7 +1,8 @@ -From d99a48e5d8690826d90594d5956a42e3fcb16802 Mon Sep 17 00:00:00 2001 +From 68592bab96a54c81771c6047ab6622135896ecec Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:24:08 +0100 -Subject: [PATCH sugar 29/74] accessibility_0006_virtualkeyboard.patch +Subject: [PATCH 29/82] accessibility_0006_virtualkeyboard.patch +Organization: Sugar Labs Foundation --- bin/sugar.in | 2 + @@ -2247,5 +2248,5 @@ index 0000000..b873ae4 + img.set_from_pixbuf(icon) + self.hbox.add(img) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0030-accessibility_0008_cp_show-virtualkeyboard-for-acces.patch b/rpms/sugar/0030-accessibility_0008_cp_show-virtualkeyboard-for-acces.patch index a84d066..ca7ef5b 100644 --- a/rpms/sugar/0030-accessibility_0008_cp_show-virtualkeyboard-for-acces.patch +++ b/rpms/sugar/0030-accessibility_0008_cp_show-virtualkeyboard-for-acces.patch @@ -1,7 +1,8 @@ -From 8c907b5c0c77b1a9adb69976c44e74a5615d127e Mon Sep 17 00:00:00 2001 +From 502f4d96a32219cc9eb7b59ff74333a144548b61 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:24:33 +0100 -Subject: [PATCH sugar 30/74] accessibility_0008_cp_show-virtualkeyboard-for-accessibility.patch +Subject: [PATCH 30/82] accessibility_0008_cp_show-virtualkeyboard-for-accessibility.patch +Organization: Sugar Labs Foundation --- extensions/cpsection/accessibility/model.py | 13 +++++++++- @@ -15,7 +16,7 @@ diff --git a/extensions/cpsection/accessibility/model.py b/extensions/cpsection/ index 9afec04..2298ec0 100644 --- a/extensions/cpsection/accessibility/model.py +++ b/extensions/cpsection/accessibility/model.py -@@ -23,7 +23,7 @@ +@@ -23,7 +23,7 @@ keyboard = accessibility.Keyboard() screen = accessibility.Screen() mouse = accessibility.Mouse() @@ -51,7 +52,7 @@ diff --git a/extensions/cpsection/accessibility/view.py b/extensions/cpsection/a index f06f7ae..6208466 100644 --- a/extensions/cpsection/accessibility/view.py +++ b/extensions/cpsection/accessibility/view.py -@@ -77,6 +77,7 @@ def _view_keyboard_options(self): +@@ -77,6 +77,7 @@ class accessibility(SectionView): self._view_mouse_keys() self._view_sticky_keys() self._view_bounce_keys() @@ -59,7 +60,7 @@ index f06f7ae..6208466 100644 self._vbox_section.pack_start(self.box_pm_keyboard, expand=False) self.box_pm_keyboard.show() -@@ -133,6 +134,14 @@ def _set_bounce_keys(self, widget): +@@ -133,6 +134,14 @@ class accessibility(SectionView): state = widget.get_active() self._model.set_bounce_keys(state) @@ -74,7 +75,7 @@ index f06f7ae..6208466 100644 def _set_contrast(self, widget): state = widget.get_active() self._model.set_contrast(state) -@@ -165,6 +174,9 @@ def undo(self): +@@ -165,6 +174,9 @@ class accessibility(SectionView): self._model.set_sticky_keys(self.init_state_sticky_keys) self._model.set_bounce_keys(self.init_state_bounce_keys) @@ -84,7 +85,7 @@ index f06f7ae..6208466 100644 self._model.set_contrast(self.init_state_contrast) self.btn_contrast.set_active(self.init_state_contrast) -@@ -219,6 +231,24 @@ def _view_bounce_keys(self): +@@ -219,6 +231,24 @@ class accessibility(SectionView): self.box_pm_keyboard.pack_start(lbl_bounce, True, True, 2) lbl_bounce.show() @@ -109,7 +110,7 @@ index f06f7ae..6208466 100644 def _view_contrast(self): self.btn_contrast = gtk.CheckButton(_('Contrast')) self._contrast_pm_change_handler = self.btn_contrast.connect("toggled", self._set_contrast) -@@ -298,4 +328,4 @@ def _view_acceleration_mouse(self): +@@ -298,4 +328,4 @@ class accessibility(SectionView): desc_accel_mouse.show() def setup(self): @@ -120,7 +121,7 @@ diff --git a/extensions/deviceicon/virtualkeyboard.py b/extensions/deviceicon/vi index 955f79b..fbd0b35 100644 --- a/extensions/deviceicon/virtualkeyboard.py +++ b/extensions/deviceicon/virtualkeyboard.py -@@ -13,6 +13,7 @@ +@@ -13,6 +13,7 @@ from sugar.graphics import style from jarabe.frame.frameinvoker import FrameWidgetInvoker import jarabe.view.virtualkeyboard @@ -128,7 +129,7 @@ index 955f79b..fbd0b35 100644 class DeviceView(TrayIcon): -@@ -66,7 +67,10 @@ def _close_activate_cb(self, gobject_ref): +@@ -66,7 +67,10 @@ class VirtualkeyboardPalette(Palette): except: pass @@ -168,7 +169,7 @@ diff --git a/src/jarabe/model/accessibility.py b/src/jarabe/model/accessibility. index 299f8d4..b1b2b8a 100644 --- a/src/jarabe/model/accessibility.py +++ b/src/jarabe/model/accessibility.py -@@ -50,6 +50,14 @@ def set_bounce_keys(self, activar): +@@ -50,6 +50,14 @@ class Keyboard: client.set_bool("/desktop/sugar/accessibility/keyboard/bouncekeys_enable", activar) self.run_config_keyboard() @@ -184,5 +185,5 @@ index 299f8d4..b1b2b8a 100644 cmd = ['ax'] if self.get_sticky_keys(): -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0031-accessibility_0007_cp_translations.patch.patch b/rpms/sugar/0031-accessibility_0007_cp_translations.patch.patch index 87b2143..53815f2 100644 --- a/rpms/sugar/0031-accessibility_0007_cp_translations.patch.patch +++ b/rpms/sugar/0031-accessibility_0007_cp_translations.patch.patch @@ -1,7 +1,8 @@ -From ed6349c92c0395921af793d3ae9c60b20d6b63de Mon Sep 17 00:00:00 2001 +From fb991675cd4c55bddde5a1b7d30bf04da883d503 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:25:42 +0100 -Subject: [PATCH sugar 31/74] accessibility_0007_cp_translations.patch +Subject: [PATCH 31/82] accessibility_0007_cp_translations.patch +Organization: Sugar Labs Foundation --- po/es.po | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -87,5 +88,5 @@ index 474d28f..ea4579c 100644 # priority over the normal wireless device. NM doesn't have a "disconnect" # method for a device either (for various reasons) so this doesn't -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0032-accessibility_0009_cp_show-virtualkeyboard-for-acces.patch b/rpms/sugar/0032-accessibility_0009_cp_show-virtualkeyboard-for-acces.patch index f6c49cf..f501c85 100644 --- a/rpms/sugar/0032-accessibility_0009_cp_show-virtualkeyboard-for-acces.patch +++ b/rpms/sugar/0032-accessibility_0009_cp_show-virtualkeyboard-for-acces.patch @@ -1,7 +1,8 @@ -From 162523de5dc3ad7739f0b5cc8a026cd4b74ab247 Mon Sep 17 00:00:00 2001 +From 5b2bc8c6baedc0858ca7b778f22fe66a7c3c1960 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 1 Nov 2010 18:27:08 +0100 -Subject: [PATCH sugar 32/74] accessibility_0009_cp_show-virtualkeyboard-for-accessibility-traslate.patch +Subject: [PATCH 32/82] accessibility_0009_cp_show-virtualkeyboard-for-accessibility-traslate.patch +Organization: Sugar Labs Foundation --- po/es.po | 8 ++++++++ @@ -27,5 +28,5 @@ index ea4579c..241b8b6 100644 # priority over the normal wireless device. NM doesn't have a "disconnect" # method for a device either (for various reasons) so this doesn't -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0033-accessibility_0012_add-accel-mouse-default.patch.patch b/rpms/sugar/0033-accessibility_0012_add-accel-mouse-default.patch.patch index b04f955..7c45b4d 100644 --- a/rpms/sugar/0033-accessibility_0012_add-accel-mouse-default.patch.patch +++ b/rpms/sugar/0033-accessibility_0012_add-accel-mouse-default.patch.patch @@ -1,7 +1,8 @@ -From b91753755ce6f3fa8867a3ffa47b97ffe5a6ac62 Mon Sep 17 00:00:00 2001 +From b68492777cdf580f27bacb8774c2ecbacf0e7770 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 18 Jul 2011 20:23:02 +0200 -Subject: [PATCH sugar 33/74] accessibility_0012_add-accel-mouse-default.patch +Subject: [PATCH 33/82] accessibility_0012_add-accel-mouse-default.patch +Organization: Sugar Labs Foundation --- src/jarabe/model/accessibility.py | 6 +++++- @@ -33,5 +34,5 @@ index b1b2b8a..a5cb7ed 100644 + mouse.set_accel_mouse(mouse.DEFAULT_ACCEL_MOUSE) + mouse._set_accel_mouse_setting() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0034-accessibility_0013_add-theme-mouse-default.patch.patch b/rpms/sugar/0034-accessibility_0013_add-theme-mouse-default.patch.patch index ccc0bc4..32aaceb 100644 --- a/rpms/sugar/0034-accessibility_0013_add-theme-mouse-default.patch.patch +++ b/rpms/sugar/0034-accessibility_0013_add-theme-mouse-default.patch.patch @@ -1,7 +1,8 @@ -From 99403402adac7cb8247667dc951a66fcef7dddbf Mon Sep 17 00:00:00 2001 +From f2b815307082b11c96e9bce9d1e92d7b39134da9 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 18 Jul 2011 20:23:30 +0200 -Subject: [PATCH sugar 34/74] accessibility_0013_add-theme-mouse-default.patch +Subject: [PATCH 34/82] accessibility_0013_add-theme-mouse-default.patch +Organization: Sugar Labs Foundation --- src/jarabe/model/accessibility.py | 1 + @@ -11,12 +12,12 @@ diff --git a/src/jarabe/model/accessibility.py b/src/jarabe/model/accessibility. index a5cb7ed..4c5e93c 100644 --- a/src/jarabe/model/accessibility.py +++ b/src/jarabe/model/accessibility.py -@@ -164,4 +164,5 @@ def setup_accessibility(self): +@@ -164,4 +164,5 @@ class AccessibilityManager: mouse.run_config_mouse() else: mouse.set_accel_mouse(mouse.DEFAULT_ACCEL_MOUSE) + mouse.set_white_mouse(False) mouse._set_accel_mouse_setting() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0035-Share-3g-connection.patch b/rpms/sugar/0035-Share-3g-connection.patch index 32da69f..eb73b40 100644 --- a/rpms/sugar/0035-Share-3g-connection.patch +++ b/rpms/sugar/0035-Share-3g-connection.patch @@ -1,7 +1,8 @@ -From c16b121d6ceab84da73d7c4bf1e703b8e45e60c4 Mon Sep 17 00:00:00 2001 +From 5d87a4bcf14825aecb80b1f45d996375515bf3c5 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.org> Date: Mon, 1 Nov 2010 14:23:51 -0300 -Subject: [PATCH sugar 35/74] Share 3g connection +Subject: [PATCH 35/82] Share 3g connection +Organization: Sugar Labs Foundation dextrose backport --- @@ -12,7 +13,7 @@ diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py index 789ea13..234b06b 100644 --- a/extensions/deviceicon/network.py +++ b/extensions/deviceicon/network.py -@@ -23,6 +23,8 @@ +@@ -23,6 +23,8 @@ import logging import hashlib import socket import struct @@ -21,7 +22,7 @@ index 789ea13..234b06b 100644 import datetime import time import gtk -@@ -39,6 +41,7 @@ +@@ -39,6 +41,7 @@ from sugar.graphics.tray import TrayIcon from sugar.graphics.menuitem import MenuItem from sugar.graphics.icon import Icon from sugar.graphics import xocolor @@ -29,7 +30,7 @@ index 789ea13..234b06b 100644 from sugar import profile from jarabe.model import network -@@ -58,6 +61,7 @@ +@@ -58,6 +61,7 @@ _NM_SERIAL_IFACE = 'org.freedesktop.NetworkManager.Device.Serial' _NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh' _NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' _NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' @@ -37,7 +38,7 @@ index 789ea13..234b06b 100644 _GSM_STATE_NOT_READY = 0 _GSM_STATE_DISCONNECTED = 1 -@@ -65,6 +69,12 @@ +@@ -65,6 +69,12 @@ _GSM_STATE_CONNECTING = 2 _GSM_STATE_CONNECTED = 3 _GSM_STATE_FAILED = 4 @@ -60,7 +61,7 @@ index 789ea13..234b06b 100644 } def __init__(self): -@@ -215,12 +228,17 @@ def __init__(self): +@@ -215,12 +228,17 @@ class GsmPalette(Palette): self._current_state = None self._failed_connection = False @@ -78,7 +79,7 @@ index 789ea13..234b06b 100644 self.info_box = gtk.VBox() self.error_title_label = gtk.Label("") -@@ -305,6 +323,9 @@ def _update_label_and_text(self, reason=0): +@@ -305,6 +323,9 @@ class GsmPalette(Palette): icon = Icon(icon_name='media-eject', \ icon_size=gtk.ICON_SIZE_MENU) self._toggle_state_item.set_image(icon) @@ -88,7 +89,7 @@ index 789ea13..234b06b 100644 elif self._current_state == _GSM_STATE_FAILED: message_error = self._get_error_by_nm_reason(reason) -@@ -313,6 +334,8 @@ def _update_label_and_text(self, reason=0): +@@ -313,6 +334,8 @@ class GsmPalette(Palette): raise ValueError('Invalid GSM state while updating label and ' \ 'text, %s' % str(self._current_state)) @@ -97,7 +98,7 @@ index 789ea13..234b06b 100644 def __toggle_state_cb(self, menuitem): if self._current_state == _GSM_STATE_NOT_READY: pass -@@ -373,6 +396,38 @@ def _get_error_by_nm_reason(self, reason): +@@ -373,6 +396,38 @@ class GsmPalette(Palette): message_tuple = (network.get_error_by_reason(reason), message) return message_tuple @@ -136,7 +137,7 @@ index 789ea13..234b06b 100644 class WirelessDeviceView(ToolButton): -@@ -530,8 +585,16 @@ def _update_state(self): +@@ -530,8 +585,16 @@ class WirelessDeviceView(ToolButton): else: state = network.DEVICE_STATE_UNKNOWN @@ -155,7 +156,7 @@ index 789ea13..234b06b 100644 if state == network.DEVICE_STATE_ACTIVATED: icon_name = '%s-connected' % 'network-wireless' else: -@@ -540,14 +603,6 @@ def _update_state(self): +@@ -540,14 +603,6 @@ class WirelessDeviceView(ToolButton): icon_name = get_icon_state(icon_name, self._strength) if icon_name: self._icon.props.icon_name = icon_name @@ -179,7 +180,7 @@ index 789ea13..234b06b 100644 client = gconf.client_get_default() color = xocolor.XoColor(client.get_string('/desktop/sugar/user/color')) -@@ -754,6 +811,8 @@ def create_palette(self): +@@ -754,6 +811,8 @@ class GsmDeviceView(TrayIcon): palette.set_group_id('frame') palette.connect('gsm-connect', self.__gsm_connect_cb) palette.connect('gsm-disconnect', self.__gsm_disconnect_cb) @@ -188,7 +189,7 @@ index 789ea13..234b06b 100644 self._palette = palette -@@ -879,6 +938,106 @@ def __connection_timecount_cb(self): +@@ -879,6 +938,106 @@ class GsmDeviceView(TrayIcon): self._palette.update_connection_time(connection_time) return True @@ -296,5 +297,5 @@ index 789ea13..234b06b 100644 class WirelessDeviceObserver(object): def __init__(self, device, tray): -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0036-Make-sure-Adhoc-is-Sugar-Adhoc.patch b/rpms/sugar/0036-Make-sure-Adhoc-is-Sugar-Adhoc.patch index 300aa4d..95e3ce3 100644 --- a/rpms/sugar/0036-Make-sure-Adhoc-is-Sugar-Adhoc.patch +++ b/rpms/sugar/0036-Make-sure-Adhoc-is-Sugar-Adhoc.patch @@ -1,7 +1,8 @@ -From 24f5ac148b8b3420432ed7d7efbf40971afcdd20 Mon Sep 17 00:00:00 2001 +From ae6cc8dfef60abd3e5bd97dad93c6d0e8e5bf117 Mon Sep 17 00:00:00 2001 From: Unknown <unknown@unknown.org> Date: Mon, 17 Jan 2011 12:42:33 +0100 -Subject: [PATCH sugar 36/74] Make sure Adhoc is Sugar-Adhoc +Subject: [PATCH 36/82] Make sure Adhoc is Sugar-Adhoc +Organization: Sugar Labs Foundation --- src/jarabe/desktop/networkviews.py | 5 +++-- @@ -11,7 +12,7 @@ diff --git a/src/jarabe/desktop/networkviews.py b/src/jarabe/desktop/networkview index 677452d..2fb8593 100644 --- a/src/jarabe/desktop/networkviews.py +++ b/src/jarabe/desktop/networkviews.py -@@ -360,8 +360,9 @@ def _connect(self): +@@ -360,8 +360,9 @@ class WirelessNetworkView(CanvasPulsingIcon): elif self._mode == network.NM_802_11_MODE_ADHOC: settings.wireless.mode = 'adhoc' settings.wireless.band = 'bg' @@ -24,5 +25,5 @@ index 677452d..2fb8593 100644 wireless_security = self._get_security() settings.wireless_security = wireless_security -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0037-Simple-messages-notification-extension.patch b/rpms/sugar/0037-Simple-messages-notification-extension.patch index 2d69945..3959dad 100644 --- a/rpms/sugar/0037-Simple-messages-notification-extension.patch +++ b/rpms/sugar/0037-Simple-messages-notification-extension.patch @@ -1,7 +1,8 @@ -From 733b96d67567be12e611bf711614e78ffddb66dc Mon Sep 17 00:00:00 2001 +From 872ab7f998613626b061edc05a079fa04600a497 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Wed, 1 Dec 2010 10:27:44 -0300 -Subject: [PATCH sugar 37/74] Simple messages notification extension +Subject: [PATCH 37/82] Simple messages notification extension +Organization: Sugar Labs Foundation Extend jarabe.frame.notification with new graphical elements in order to display message notifications. @@ -42,7 +43,7 @@ diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py index 079eeeb..96e848b 100644 --- a/src/jarabe/frame/frame.py +++ b/src/jarabe/frame/frame.py -@@ -19,6 +19,7 @@ +@@ -19,6 +19,7 @@ import logging import gtk import gobject import hippo @@ -50,7 +51,7 @@ index 079eeeb..96e848b 100644 from sugar.graphics import animator from sugar.graphics import style -@@ -33,6 +34,7 @@ +@@ -33,6 +34,7 @@ from jarabe.frame.devicestray import DevicesTray from jarabe.frame.framewindow import FrameWindow from jarabe.frame.clipboardpanelwindow import ClipboardPanelWindow from jarabe.frame.notification import NotificationIcon, NotificationWindow @@ -58,7 +59,7 @@ index 079eeeb..96e848b 100644 from jarabe.model import notifications -@@ -44,6 +46,8 @@ +@@ -44,6 +46,8 @@ BOTTOM_LEFT = 3 _FRAME_HIDING_DELAY = 500 _NOTIFICATION_DURATION = 5000 @@ -67,7 +68,7 @@ index 079eeeb..96e848b 100644 class _Animation(animator.Animation): def __init__(self, frame, end): -@@ -119,6 +123,10 @@ def __init__(self): +@@ -119,6 +123,10 @@ class Frame(object): self._event_area.connect('enter', self._enter_corner_cb) self._event_area.show() @@ -78,7 +79,7 @@ index 079eeeb..96e848b 100644 self._top_panel = self._create_top_panel() self._bottom_panel = self._create_bottom_panel() self._left_panel = self._create_left_panel() -@@ -131,6 +139,7 @@ def __init__(self): +@@ -131,6 +139,7 @@ class Frame(object): self._mouse_listener = _MouseListener(self) self._notif_by_icon = {} @@ -86,7 +87,7 @@ index 079eeeb..96e848b 100644 notification_service = notifications.get_service() notification_service.notification_received.connect( -@@ -190,6 +199,7 @@ def _create_top_panel(self): +@@ -190,6 +199,7 @@ class Frame(object): panel.append(hippo.CanvasWidget(widget=activities_tray), hippo.PACK_EXPAND) activities_tray.show() @@ -94,7 +95,7 @@ index 079eeeb..96e848b 100644 return panel -@@ -201,6 +211,7 @@ def _create_bottom_panel(self): +@@ -201,6 +211,7 @@ class Frame(object): panel.append(hippo.CanvasWidget(widget=devices_tray), hippo.PACK_EXPAND) devices_tray.show() @@ -102,7 +103,7 @@ index 079eeeb..96e848b 100644 return panel -@@ -210,6 +221,7 @@ def _create_right_panel(self): +@@ -210,6 +221,7 @@ class Frame(object): tray = FriendsTray() panel.append(hippo.CanvasWidget(widget=tray), hippo.PACK_EXPAND) tray.show() @@ -110,7 +111,7 @@ index 079eeeb..96e848b 100644 return panel -@@ -285,15 +297,7 @@ def _drag_leave_cb(self, window, drag_context, timestamp): +@@ -285,15 +297,7 @@ class Frame(object): def _enter_corner_cb(self, event_area): self._mouse_listener.mouse_enter() @@ -127,7 +128,7 @@ index 079eeeb..96e848b 100644 window = NotificationWindow() screen = gtk.gdk.screen_get_default() -@@ -309,6 +313,46 @@ def add_notification(self, icon, corner=gtk.CORNER_TOP_LEFT, +@@ -309,6 +313,46 @@ class Frame(object): else: raise ValueError('Inalid corner: %r' % corner) @@ -174,7 +175,7 @@ index 079eeeb..96e848b 100644 window.add(icon) icon.show() window.show() -@@ -327,28 +371,76 @@ def remove_notification(self, icon): +@@ -327,28 +371,76 @@ class Frame(object): window.destroy() del self._notif_by_icon[icon] @@ -498,7 +499,7 @@ index 3471e2c..b5724d5 100644 def do_set_property(self, pspec, value): if pspec.name == 'xo-color': -@@ -86,17 +261,13 @@ def _get_palette(self): +@@ -86,17 +261,13 @@ class NotificationIcon(gtk.EventBox): class NotificationWindow(gtk.Window): __gtype_name__ = 'SugarNotificationWindow' @@ -522,7 +523,7 @@ diff --git a/src/jarabe/view/pulsingicon.py b/src/jarabe/view/pulsingicon.py index 9a98a80..0a97377 100644 --- a/src/jarabe/view/pulsingicon.py +++ b/src/jarabe/view/pulsingicon.py -@@ -88,12 +88,23 @@ def __init__(self, **kwargs): +@@ -88,12 +88,23 @@ class PulsingIcon(Icon): self._pulse_color = None self._paused = False self._pulsing = False @@ -546,7 +547,7 @@ index 9a98a80..0a97377 100644 def set_pulse_color(self, pulse_color): self._pulse_color = pulse_color self._pulser.update() -@@ -140,10 +151,20 @@ def get_paused(self): +@@ -140,10 +151,20 @@ class PulsingIcon(Icon): type=bool, default=False, getter=get_paused, setter=set_paused) def set_pulsing(self, pulsing): @@ -567,7 +568,7 @@ index 9a98a80..0a97377 100644 else: self._pulser.stop() -@@ -163,6 +184,9 @@ def _set_palette(self, palette): +@@ -163,6 +184,9 @@ class PulsingIcon(Icon): palette = property(_get_palette, _set_palette) @@ -578,5 +579,5 @@ index 9a98a80..0a97377 100644 self._pulser.stop() if self._palette is not None: -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0038-Improve-message-notification-behaviour.patch b/rpms/sugar/0038-Improve-message-notification-behaviour.patch index b8ae4f3..7b826df 100644 --- a/rpms/sugar/0038-Improve-message-notification-behaviour.patch +++ b/rpms/sugar/0038-Improve-message-notification-behaviour.patch @@ -1,7 +1,8 @@ -From 2a7d9d90e963e4f7ba4ae28c8fac47176ff94efd Mon Sep 17 00:00:00 2001 +From d55fe1f6b99085ed0e863f29bf4bf3abf6d46722 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Mon, 20 Dec 2010 14:27:32 -0300 -Subject: [PATCH sugar 38/74] Improve message notification behaviour +Subject: [PATCH 38/82] Improve message notification behaviour +Organization: Sugar Labs Foundation Corner's notification icon will use the same icon as the frame notification button. @@ -17,7 +18,7 @@ diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py index 96e848b..6a9735b 100644 --- a/src/jarabe/frame/frame.py +++ b/src/jarabe/frame/frame.py -@@ -380,22 +380,23 @@ def add_message(self, body, summary='', icon_name=_DEFAULT_ICON, +@@ -380,22 +380,23 @@ class Frame(object): button = self._notif_by_message.get(corner, None) if button is None: @@ -49,7 +50,7 @@ diff --git a/src/jarabe/frame/notification.py b/src/jarabe/frame/notification.py index b5724d5..99030e5 100644 --- a/src/jarabe/frame/notification.py +++ b/src/jarabe/frame/notification.py -@@ -38,17 +38,18 @@ +@@ -38,17 +38,18 @@ _PULSE_TIMEOUT = 3 _PULSE_COLOR = XoColor('%s,%s' % \ (style.COLOR_BUTTON_GREY.get_svg(), style.COLOR_TRANSPARENT.get_svg())) _BODY_FILTERS = "<img.*?/>" @@ -91,7 +92,7 @@ index b5724d5..99030e5 100644 } def __init__(self): -@@ -177,18 +179,23 @@ def __init__(self): +@@ -177,18 +179,23 @@ class HistoryPalette(Palette): self.menu.append(clear_option) @@ -117,7 +118,7 @@ index b5724d5..99030e5 100644 self.set_icon_widget(self._icon) self._icon.show() self.set_palette_invoker(FrameWidgetInvoker(self)) -@@ -196,6 +203,9 @@ def __init__(self, xo_color): +@@ -196,6 +203,9 @@ class NotificationButton(ToolButton): def start_pulsing(self): self._icon.props.pulsing = True @@ -128,5 +129,5 @@ index b5724d5..99030e5 100644 class NotificationIcon(gtk.EventBox): __gtype_name__ = 'SugarNotificationIcon' -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0039-S-non-intrusive-NamingAlert.patch b/rpms/sugar/0039-S-non-intrusive-NamingAlert.patch index cc89ec3..980bcee 100644 --- a/rpms/sugar/0039-S-non-intrusive-NamingAlert.patch +++ b/rpms/sugar/0039-S-non-intrusive-NamingAlert.patch @@ -1,7 +1,8 @@ -From ff6faeea7c871cd71e7daf9ea4a2f35abddcf514 Mon Sep 17 00:00:00 2001 +From 891e037292d8179edb62edc21c3ab361f34b381d Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.org> Date: Tue, 2 Nov 2010 18:54:16 -0300 -Subject: [PATCH sugar 39/74] S non-intrusive NamingAlert +Subject: [PATCH 39/82] S non-intrusive NamingAlert +Organization: Sugar Labs Foundation Add a new gconf value to the scheme to determine when or not to show NamingAlert when an activities close. @@ -32,5 +33,5 @@ index c9bc7fe..ba404ec 100644 </schemalist> </gconfschemafile> -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0040-Globalkey-for-touchpad-device-icon.patch b/rpms/sugar/0040-Globalkey-for-touchpad-device-icon.patch index 9b595ac..7f6b1ee 100644 --- a/rpms/sugar/0040-Globalkey-for-touchpad-device-icon.patch +++ b/rpms/sugar/0040-Globalkey-for-touchpad-device-icon.patch @@ -1,7 +1,8 @@ -From 2d77d002d70ce8a4f7cd53567592e538654a7ac7 Mon Sep 17 00:00:00 2001 +From d3a8c2fd306b931a944a9b184b4fd8f7c124ec05 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Wed, 3 Nov 2010 18:27:30 -0300 -Subject: [PATCH sugar 40/74] Globalkey for touchpad device icon +Subject: [PATCH 40/82] Globalkey for touchpad device icon +Organization: Sugar Labs Foundation Add a new keyboard shortcut as a globalkey, this will toggle the touchpad mode when <Alt + m> is pressed. @@ -22,7 +23,7 @@ diff --git a/extensions/deviceicon/touchpad.py b/extensions/deviceicon/touchpad. index 6773afc..ba02037 100644 --- a/extensions/deviceicon/touchpad.py +++ b/extensions/deviceicon/touchpad.py -@@ -45,6 +45,8 @@ +@@ -45,6 +45,8 @@ STATUS_ICON = { # NODE_PATH is used to communicate with the touchpad device. NODE_PATH = '/sys/devices/platform/i8042/serio1/ptmode' @@ -31,7 +32,7 @@ index 6773afc..ba02037 100644 class DeviceView(TrayIcon): """ Manage the touchpad mode from the device palette on the Frame. """ -@@ -66,14 +68,14 @@ def create_palette(self): +@@ -66,14 +68,14 @@ class DeviceView(TrayIcon): """ Create a palette for this icon; called by the Sugar framework when a palette needs to be displayed. """ label = glib.markup_escape_text(_('My touchpad')) @@ -50,7 +51,7 @@ index 6773afc..ba02037 100644 return True -@@ -111,10 +113,12 @@ def toggle_mode(self): +@@ -111,10 +113,12 @@ class ResourcePalette(Palette): def setup(tray): @@ -116,5 +117,5 @@ index 0000000..eeaba40 + if touchpad._view is not None: + touchpad._view._palette.toggle_mode() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0041-Dextrose-Escape-all-text-passed-to-Palette.primary_t.patch b/rpms/sugar/0041-Dextrose-Escape-all-text-passed-to-Palette.primary_t.patch index 4ec2b7f..cf1195b 100644 --- a/rpms/sugar/0041-Dextrose-Escape-all-text-passed-to-Palette.primary_t.patch +++ b/rpms/sugar/0041-Dextrose-Escape-all-text-passed-to-Palette.primary_t.patch @@ -1,8 +1,9 @@ -From cecd2a7a9af344292b6b274afb6007f56857d453 Mon Sep 17 00:00:00 2001 +From e34646506e6ce2a5e0eadc1e33d297ada6c9039a Mon Sep 17 00:00:00 2001 From: Sascha Silbe <silbe@activitycentral.com> Date: Sat, 25 Jun 2011 12:12:55 +0200 -Subject: [PATCH sugar 41/74] Dextrose: Escape all text passed to +Subject: [PATCH 41/82] Dextrose: Escape all text passed to Palette.primary_text and .secondary_text +Organization: Sugar Labs Foundation sugar.graphics.palette.Palette passes primary_text and secondary_text through to GTK without escaping it, so we need to make sure it doesn't contain @@ -28,5 +29,5 @@ index 02648b7..7a17f32 100644 vbox = gtk.VBox() self.set_content(vbox) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0042-Database-support-for-3G-control-panel.patch b/rpms/sugar/0042-Database-support-for-3G-control-panel.patch index b169a97..aa9c753 100644 --- a/rpms/sugar/0042-Database-support-for-3G-control-panel.patch +++ b/rpms/sugar/0042-Database-support-for-3G-control-panel.patch @@ -1,7 +1,8 @@ -From e6412a96e8f7b7e9a498b4d300cccb0acd50abe6 Mon Sep 17 00:00:00 2001 +From ba97615ed4f708be32807d9f39136a272c72c729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Ambrois?= <andresambrois@gmail.com> Date: Tue, 11 Jan 2011 19:52:52 +0000 -Subject: [PATCH sugar 42/74] Database support for 3G control panel +Subject: [PATCH 42/82] Database support for 3G control panel +Organization: Sugar Labs Foundation For more information please look at #1630 --- @@ -229,7 +230,7 @@ index c31edba..fad141d 100644 self._timeout_sid = 0 self._changed_handler = None -@@ -46,11 +44,11 @@ def __init__(self, label_text): +@@ -46,11 +44,11 @@ class EntryWithLabel(gtk.HBox): self.pack_start(self.label, expand=False) self.label.show() @@ -246,7 +247,7 @@ index c31edba..fad141d 100644 def __entry_changed_cb(self, widget, data=None): if self._timeout_sid: -@@ -61,11 +59,11 @@ def __entry_changed_cb(self, widget, data=None): +@@ -61,11 +59,11 @@ class EntryWithLabel(gtk.HBox): def __timeout_cb(self): self._timeout_sid = 0 @@ -260,7 +261,7 @@ index c31edba..fad141d 100644 except ValueError: self._is_valid = False else: -@@ -76,16 +74,17 @@ def __timeout_cb(self): +@@ -76,16 +74,17 @@ class EntryWithLabel(gtk.HBox): return False def set_text_from_model(self): @@ -280,7 +281,7 @@ index c31edba..fad141d 100644 is_valid = gobject.property(type=bool, getter=_get_is_valid, default=True) -@@ -168,60 +167,149 @@ def __init__(self, model, alerts=None): +@@ -168,60 +167,149 @@ class ModemConfiguration(SectionView): self._model = model self.restart_alerts = alerts @@ -446,7 +447,7 @@ index c31edba..fad141d 100644 self._puk_entry.show() self.setup() -@@ -239,14 +327,37 @@ def setup(self): +@@ -239,14 +327,37 @@ class ModemConfiguration(SectionView): def undo(self): self._model.undo() @@ -490,5 +491,5 @@ index c31edba..fad141d 100644 self.props.is_valid = False -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0043-Fix-no-restart-after-provider-info-selection.patch b/rpms/sugar/0043-Fix-no-restart-after-provider-info-selection.patch index ec9cc4e..de322e8 100644 --- a/rpms/sugar/0043-Fix-no-restart-after-provider-info-selection.patch +++ b/rpms/sugar/0043-Fix-no-restart-after-provider-info-selection.patch @@ -1,7 +1,8 @@ -From a65cd94c79bf0cddf35316401a2f2d916e41734d Mon Sep 17 00:00:00 2001 +From 0a4d42795418260e2d21d43832e70616fc25e615 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Tue, 11 Jan 2011 19:54:07 +0000 -Subject: [PATCH sugar 43/74] Fix no restart after provider info selection +Subject: [PATCH 43/82] Fix no restart after provider info selection +Organization: Sugar Labs Foundation Current 3g provider information selection logic bypassed the validation process, thefore no restart was required. @@ -18,7 +19,7 @@ diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsect index fad141d..a16de88 100644 --- a/extensions/cpsection/modemconfiguration/view.py +++ b/extensions/cpsection/modemconfiguration/view.py -@@ -341,14 +341,10 @@ def __provider_selected_cb(self, combo): +@@ -341,14 +341,10 @@ class ModemConfiguration(SectionView): def __plan_selected_cb(self, combo): model = combo.get_model() plan = model.get_row_plan(combo.get_active()) @@ -38,5 +39,5 @@ index fad141d..a16de88 100644 def _validate(self): if self._username_entry.is_valid and \ -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0044-MAY-CONTAIN-TYPO-Flickering-and-unknown-icons-in-the.patch b/rpms/sugar/0044-MAY-CONTAIN-TYPO-Flickering-and-unknown-icons-in-the.patch index 898e00f..29c6cd0 100644 --- a/rpms/sugar/0044-MAY-CONTAIN-TYPO-Flickering-and-unknown-icons-in-the.patch +++ b/rpms/sugar/0044-MAY-CONTAIN-TYPO-Flickering-and-unknown-icons-in-the.patch @@ -1,8 +1,9 @@ -From 8589f886f92bba68e0b6333bc6873d5680e326ab Mon Sep 17 00:00:00 2001 +From e21481f3900fcd3211f0d27eff1a1b0f6675f588 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Mon, 18 Jul 2011 20:39:06 +0200 -Subject: [PATCH sugar 44/74] MAY CONTAIN TYPO: Flickering and unknown icons - in the window bar #870 +Subject: [PATCH 44/82] MAY CONTAIN TYPO: Flickering and unknown icons in the + window bar #870 +Organization: Sugar Labs Foundation --- src/jarabe/journal/misc.py | 3 ++- @@ -27,7 +28,7 @@ diff --git a/src/jarabe/view/launcher.py b/src/jarabe/view/launcher.py index 5c645c4..44f49bb 100644 --- a/src/jarabe/view/launcher.py +++ b/src/jarabe/view/launcher.py -@@ -29,7 +29,7 @@ +@@ -29,7 +29,7 @@ from jarabe.view.pulsingicon import PulsingIcon class LaunchWindow(gtk.Window): @@ -36,7 +37,7 @@ index 5c645c4..44f49bb 100644 gobject.GObject.__init__(self) self.props.type_hint = gtk.gdk.WINDOW_TYPE_HINT_NORMAL -@@ -48,6 +48,7 @@ def __init__(self, activity_id, icon_path, icon_color): +@@ -48,6 +48,7 @@ class LaunchWindow(gtk.Window): canvas.pack_start(header, expand=False) self._activity_id = activity_id @@ -44,7 +45,7 @@ index 5c645c4..44f49bb 100644 self._activity_icon = PulsingIcon(file=icon_path, pixel_size=style.XLARGE_ICON_SIZE) -@@ -93,6 +94,7 @@ def _update_size(self): +@@ -93,6 +94,7 @@ class LaunchWindow(gtk.Window): self.resize(gtk.gdk.screen_width(), gtk.gdk.screen_height()) def __realize_cb(self, widget): @@ -77,5 +78,5 @@ index 5c645c4..44f49bb 100644 -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0045-Clipboard-menu-off-screen-2201.patch b/rpms/sugar/0045-Clipboard-menu-off-screen-2201.patch index d09d04d..d067652 100644 --- a/rpms/sugar/0045-Clipboard-menu-off-screen-2201.patch +++ b/rpms/sugar/0045-Clipboard-menu-off-screen-2201.patch @@ -1,7 +1,8 @@ -From 36ab56d8a68660bc1a727f1ef2af000eb00c641b Mon Sep 17 00:00:00 2001 +From 49a1daa58bf944449ad05a932c3d1aacd52241b4 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Fri, 17 Dec 2010 16:37:05 +0000 -Subject: [PATCH sugar 45/74] Clipboard menu off screen #2201 +Subject: [PATCH 45/82] Clipboard menu off screen #2201 +Organization: Sugar Labs Foundation Rely on sugar-toolkit in setting most appropriate text_maxlen --- @@ -12,7 +13,7 @@ diff --git a/src/jarabe/frame/clipboardmenu.py b/src/jarabe/frame/clipboardmenu. index 4c077d9..7d0dd36 100644 --- a/src/jarabe/frame/clipboardmenu.py +++ b/src/jarabe/frame/clipboardmenu.py -@@ -41,7 +41,7 @@ +@@ -41,7 +41,7 @@ from jarabe.model import bundleregistry class ClipboardMenu(Palette): def __init__(self, cb_object): @@ -22,5 +23,5 @@ index 4c077d9..7d0dd36 100644 self._cb_object = cb_object -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0046-Add-lease-duration-information-in-about-my-computer.patch b/rpms/sugar/0046-Add-lease-duration-information-in-about-my-computer.patch index 36ce7a7..be7c21c 100644 --- a/rpms/sugar/0046-Add-lease-duration-information-in-about-my-computer.patch +++ b/rpms/sugar/0046-Add-lease-duration-information-in-about-my-computer.patch @@ -1,8 +1,8 @@ -From 85297850f1791781aedcf33b170f5ae3d2d6e079 Mon Sep 17 00:00:00 2001 +From 841c7b66a77cb814c6442a3fefbed5862f68f78e Mon Sep 17 00:00:00 2001 From: Anish Mangal <anish@sugarlabs.org> Date: Thu, 20 Jan 2011 23:18:42 -0300 -Subject: [PATCH sugar 46/74] Add lease duration information in about my - computer +Subject: [PATCH 46/82] Add lease duration information in about my computer +Organization: Sugar Labs Foundation Signed-off-by: Anish Mangal <anish@sugarlabs.org> --- @@ -14,7 +14,7 @@ diff --git a/extensions/cpsection/aboutcomputer/model.py b/extensions/cpsection/ index 431c9c0..52a1094 100644 --- a/extensions/cpsection/aboutcomputer/model.py +++ b/extensions/cpsection/aboutcomputer/model.py -@@ -21,6 +21,7 @@ +@@ -21,6 +21,7 @@ import re import subprocess from gettext import gettext as _ import errno @@ -22,7 +22,7 @@ index 431c9c0..52a1094 100644 import dbus -@@ -39,6 +40,9 @@ +@@ -39,6 +40,9 @@ _DMI_DIRECTORY = '/sys/class/dmi/id' _SN = 'serial-number' _MODEL = 'openprom/model' @@ -67,7 +67,7 @@ diff --git a/extensions/cpsection/aboutcomputer/view.py b/extensions/cpsection/a index 257e165..d719372 100644 --- a/extensions/cpsection/aboutcomputer/view.py +++ b/extensions/cpsection/aboutcomputer/view.py -@@ -79,6 +79,21 @@ def _setup_identity(self): +@@ -79,6 +79,21 @@ class AboutComputer(SectionView): vbox_identity.pack_start(box_identity, expand=False) box_identity.show() @@ -90,5 +90,5 @@ index 257e165..d719372 100644 vbox_identity.show() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0047-Extend-sugar-launch-with-more-options.patch b/rpms/sugar/0047-Extend-sugar-launch-with-more-options.patch index 6102e78..c91addf 100644 --- a/rpms/sugar/0047-Extend-sugar-launch-with-more-options.patch +++ b/rpms/sugar/0047-Extend-sugar-launch-with-more-options.patch @@ -1,7 +1,8 @@ -From f17357af17f782b720b6b8c3126dfe54fd1b778f Mon Sep 17 00:00:00 2001 +From 60c3fb58acefbadb7999c6a439600c1ec43c2f53 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Wed, 19 Jan 2011 20:20:35 +0000 -Subject: [PATCH sugar 47/74] Extend sugar-launch with more options +Subject: [PATCH 47/82] Extend sugar-launch with more options +Organization: Sugar Labs Foundation Add activity_id, object_id and uri options. @@ -41,5 +42,5 @@ index 7297a8e..18c0bb7 100644 def _which(exec_file): if 'PATH' in os.environ: -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0048-Show-register-gconf-value.patch b/rpms/sugar/0048-Show-register-gconf-value.patch index b4af581..1e2d1b5 100644 --- a/rpms/sugar/0048-Show-register-gconf-value.patch +++ b/rpms/sugar/0048-Show-register-gconf-value.patch @@ -1,7 +1,8 @@ -From ab1b99cb8b44474f95722c94bd6b489e552e2bce Mon Sep 17 00:00:00 2001 +From 35fc5a6762c60f51fef54a724134ec70106ba7f5 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Mon, 18 Jul 2011 20:50:32 +0200 -Subject: [PATCH sugar 48/74] Show register gconf value +Subject: [PATCH 48/82] Show register gconf value +Organization: Sugar Labs Foundation Add a new gconf show_register value in the sugar scheme. This value determines whether or not the Register option will be displayed at @@ -42,7 +43,7 @@ diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesv index c2ff370..81e32c8 100644 --- a/src/jarabe/desktop/favoritesview.py +++ b/src/jarabe/desktop/favoritesview.py -@@ -638,6 +638,14 @@ def create_palette(self): +@@ -638,6 +638,14 @@ class OwnerIcon(BuddyIcon): palette = BuddyMenu(get_owner_instance()) client = gconf.client_get_default() @@ -57,7 +58,7 @@ index c2ff370..81e32c8 100644 backup_url = client.get_string('/desktop/sugar/backup_url') if not backup_url: -@@ -650,8 +658,6 @@ def create_palette(self): +@@ -650,8 +658,6 @@ class OwnerIcon(BuddyIcon): palette.menu.append(self._register_menu) self._register_menu.show() @@ -67,5 +68,5 @@ index c2ff370..81e32c8 100644 return hippo.get_canvas_for_item(self).get_toplevel() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0049-Yum-updater-notifications-integration.patch b/rpms/sugar/0049-Yum-updater-notifications-integration.patch index 4dade2a..81b721f 100644 --- a/rpms/sugar/0049-Yum-updater-notifications-integration.patch +++ b/rpms/sugar/0049-Yum-updater-notifications-integration.patch @@ -1,7 +1,8 @@ -From c1df98e7db463c4be76278d7716d069428bdd93d Mon Sep 17 00:00:00 2001 +From d617c3dab7307b527edf9085054f29ebd7ce13db Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Thu, 17 Feb 2011 02:16:59 +0000 -Subject: [PATCH sugar 49/74] Yum-updater notifications integration +Subject: [PATCH 49/82] Yum-updater notifications integration +Organization: Sugar Labs Foundation Original-code: http://wiki.sugarlabs.org/go/Dextrose/Updater dextrose-port-by: Martin Abente <martin.abente.lahaye@gmail.com> @@ -17,7 +18,7 @@ diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py index 07deff7..f101757 100644 --- a/src/jarabe/desktop/homewindow.py +++ b/src/jarabe/desktop/homewindow.py -@@ -18,6 +18,8 @@ +@@ -18,6 +18,8 @@ import logging import gobject import gtk @@ -26,7 +27,7 @@ index 07deff7..f101757 100644 from sugar.graphics import style from sugar.graphics import palettegroup -@@ -28,6 +30,7 @@ +@@ -28,6 +30,7 @@ from jarabe.desktop.groupbox import GroupBox from jarabe.desktop.transitionbox import TransitionBox from jarabe.model.shell import ShellModel from jarabe.model import shell @@ -34,7 +35,7 @@ index 07deff7..f101757 100644 _HOME_PAGE = 0 -@@ -35,6 +38,12 @@ +@@ -35,6 +38,12 @@ _GROUP_PAGE = 1 _MESH_PAGE = 2 _TRANSITION_PAGE = 3 @@ -47,7 +48,7 @@ index 07deff7..f101757 100644 _instance = None -@@ -79,6 +88,33 @@ def __init__(self): +@@ -79,6 +88,33 @@ class HomeWindow(gtk.Window): shell.get_model().zoom_level_changed.connect( self.__zoom_level_changed_cb) @@ -82,5 +83,5 @@ index 07deff7..f101757 100644 group = palettegroup.get_group('default') group.popdown() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0050-Patch-to-add-feedback-icon-to-frame.patch b/rpms/sugar/0050-Patch-to-add-feedback-icon-to-frame.patch index edc6e99..9e3a33a 100644 --- a/rpms/sugar/0050-Patch-to-add-feedback-icon-to-frame.patch +++ b/rpms/sugar/0050-Patch-to-add-feedback-icon-to-frame.patch @@ -1,7 +1,8 @@ -From 274fc4539ea8d2b2cd33ae60321fdfb629375e40 Mon Sep 17 00:00:00 2001 +From 81d1ef62f69a4f823dbabfd27f5d84c34aa383f6 Mon Sep 17 00:00:00 2001 From: Mukesh Gupta <mukeshgupta.2006@gmail.com> Date: Tue, 18 Jan 2011 07:54:15 +0000 -Subject: [PATCH sugar 50/74] Patch to add feedback icon to frame +Subject: [PATCH 50/82] Patch to add feedback icon to frame +Organization: Sugar Labs Foundation Signed-off-by: Mukesh Gupta <mukeshgupta.2006@gmail.com> --- @@ -158,5 +159,5 @@ index 0000000..dc40a6e + if client.get_bool('/desktop/sugar/feedback/enabled'): + tray.add_device(DeviceView()) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0051-Two-kinds-of-feedback-submits.patch b/rpms/sugar/0051-Two-kinds-of-feedback-submits.patch index db534ad..e253b72 100644 --- a/rpms/sugar/0051-Two-kinds-of-feedback-submits.patch +++ b/rpms/sugar/0051-Two-kinds-of-feedback-submits.patch @@ -1,7 +1,8 @@ -From 404e4581d8159b772cc236e095bd6767fd69d6b0 Mon Sep 17 00:00:00 2001 +From 854d6f0a26982163eef24060e54040809b155a4a Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Tue, 18 Jan 2011 07:53:44 +0000 -Subject: [PATCH sugar 51/74] Two kinds of feedback submits +Subject: [PATCH 51/82] Two kinds of feedback submits +Organization: Sugar Labs Foundation --- extensions/deviceicon/feedback.py | 214 +++++++++++++++++++++++-------------- @@ -258,5 +259,5 @@ index dc40a6e..4f5198f 100644 client = gconf.client_get_default() if client.get_bool('/desktop/sugar/feedback/enabled'): -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0052-Initial-client-implementation-for-feedback-feature.patch b/rpms/sugar/0052-Initial-client-implementation-for-feedback-feature.patch index 0df6344..7a66f36 100644 --- a/rpms/sugar/0052-Initial-client-implementation-for-feedback-feature.patch +++ b/rpms/sugar/0052-Initial-client-implementation-for-feedback-feature.patch @@ -1,8 +1,8 @@ -From 8976c8e9cc399b83be1e28f7e4e958d93294af14 Mon Sep 17 00:00:00 2001 +From 6c367d09405bbdf1ced9723b1088171d90e15f62 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Mon, 18 Jul 2011 20:29:49 +0200 -Subject: [PATCH sugar 52/74] Initial client implementation for feedback - feature +Subject: [PATCH 52/82] Initial client implementation for feedback feature +Organization: Sugar Labs Foundation See http://git.sugarlabs.org/feedback-server/ for the server side. @@ -322,7 +322,7 @@ diff --git a/src/jarabe/model/shell.py b/src/jarabe/model/shell.py index 31605f7..1f980cf 100644 --- a/src/jarabe/model/shell.py +++ b/src/jarabe/model/shell.py -@@ -23,12 +23,15 @@ +@@ -23,12 +23,15 @@ import wnck import gobject import gtk import dbus @@ -338,7 +338,7 @@ index 31605f7..1f980cf 100644 _SERVICE_NAME = 'org.laptop.Activity' _SERVICE_PATH = '/org/laptop/Activity' -@@ -234,6 +237,12 @@ def get_bundle_path(self): +@@ -234,6 +237,12 @@ class Activity(gobject.GObject): else: return self._activity_info.get_path() @@ -351,7 +351,7 @@ index 31605f7..1f980cf 100644 def get_activity_name(self): """Returns the activity's bundle name""" if self._activity_info is None: -@@ -654,6 +663,14 @@ def notify_launch_failed(self, activity_id): +@@ -654,6 +663,14 @@ class ShellModel(gobject.GObject): logging.error('Model for activity id %s does not exist.', activity_id) @@ -370,7 +370,7 @@ diff --git a/src/jarabe/view/service.py b/src/jarabe/view/service.py index 29e46b2..0f5e94f 100644 --- a/src/jarabe/view/service.py +++ b/src/jarabe/view/service.py -@@ -88,3 +88,8 @@ def NotifyLaunch(self, bundle_id, activity_id): +@@ -88,3 +88,8 @@ class UIService(dbus.service.Object): in_signature='s', out_signature='') def NotifyLaunchFailure(self, activity_id): shell.get_model().notify_launch_failed(activity_id) @@ -380,5 +380,5 @@ index 29e46b2..0f5e94f 100644 + def Feedback(self, activity_id, report, log_file): + shell.get_model().notify_feedback(activity_id, report, log_file) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0053-Message-Notifications-second-behaviour-enhancement.patch b/rpms/sugar/0053-Message-Notifications-second-behaviour-enhancement.patch index 0b58b4e..b346e8f 100644 --- a/rpms/sugar/0053-Message-Notifications-second-behaviour-enhancement.patch +++ b/rpms/sugar/0053-Message-Notifications-second-behaviour-enhancement.patch @@ -1,8 +1,8 @@ -From 341719f37739f31400f8eb4afb803125b0df5a8a Mon Sep 17 00:00:00 2001 +From 8330a87f6c2d3c1b1826c05242aed866340172d6 Mon Sep 17 00:00:00 2001 From: Martin Abente <martin.abente.lahaye@gmail.com> Date: Tue, 11 Jan 2011 19:49:39 +0000 -Subject: [PATCH sugar 53/74] Message Notifications second behaviour - enhancement +Subject: [PATCH 53/82] Message Notifications second behaviour enhancement +Organization: Sugar Labs Foundation Corner notification icon will only show up if the frame is not visible. @@ -14,7 +14,7 @@ diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py index 6a9735b..cd1dc20 100644 --- a/src/jarabe/frame/frame.py +++ b/src/jarabe/frame/frame.py -@@ -396,8 +396,10 @@ def add_message(self, body, summary='', icon_name=_DEFAULT_ICON, +@@ -396,8 +396,10 @@ class Frame(object): button.start_pulsing() palette.push_message(body, summary, icon_name, xo_color) @@ -27,5 +27,5 @@ index 6a9735b..cd1dc20 100644 def remove_message(self, palette, corner): if corner not in self._notif_by_message: -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0054-Enable-disable-personalized-and-anonymous-feedback-s.patch b/rpms/sugar/0054-Enable-disable-personalized-and-anonymous-feedback-s.patch index 9c811ea..c1520f3 100644 --- a/rpms/sugar/0054-Enable-disable-personalized-and-anonymous-feedback-s.patch +++ b/rpms/sugar/0054-Enable-disable-personalized-and-anonymous-feedback-s.patch @@ -1,8 +1,9 @@ -From 9e9f27c8398e5210e63d918d700f9c00f741111e Mon Sep 17 00:00:00 2001 +From c3d9bc35ed6ba13af48e1d2d3e0bef515fe3b4d9 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Fri, 4 Feb 2011 01:29:28 +0000 -Subject: [PATCH sugar 54/74] Enable/disable personalized and anonymous - feedback submits unrelated +Subject: [PATCH 54/82] Enable/disable personalized and anonymous feedback + submits unrelated +Organization: Sugar Labs Foundation --- bin/sugar-session | 5 +++-- @@ -85,7 +86,7 @@ diff --git a/extensions/deviceicon/feedback.py b/extensions/deviceicon/feedback. index 4f5198f..49db09b 100644 --- a/extensions/deviceicon/feedback.py +++ b/extensions/deviceicon/feedback.py -@@ -175,5 +175,5 @@ def __realize_cb(self, widget): +@@ -175,5 +175,5 @@ class _Window(gtk.Window): def setup(tray): client = gconf.client_get_default() @@ -93,5 +94,5 @@ index 4f5198f..49db09b 100644 + if client.get_bool('/desktop/sugar/feedback/personalized_submit'): tray.add_device(DeviceView()) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0055-Send-XO-serial-numbers-with-anonymous-reports.patch b/rpms/sugar/0055-Send-XO-serial-numbers-with-anonymous-reports.patch index fef05e3..843f203 100644 --- a/rpms/sugar/0055-Send-XO-serial-numbers-with-anonymous-reports.patch +++ b/rpms/sugar/0055-Send-XO-serial-numbers-with-anonymous-reports.patch @@ -1,7 +1,8 @@ -From 6b5f89da40f0d71bec2995ac72d0041ea7d7b0fe Mon Sep 17 00:00:00 2001 +From cc561b474b5c3cf4efdafa59d6099bd1f8f1f2d8 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Fri, 4 Feb 2011 01:29:29 +0000 -Subject: [PATCH sugar 55/74] Send XO serial numbers with anonymous reports +Subject: [PATCH 55/82] Send XO serial numbers with anonymous reports +Organization: Sugar Labs Foundation --- data/sugar.schemas.in | 11 +++++++++++ @@ -59,5 +60,5 @@ index 5e92fbd..93fa704 100644 def _submit(data=None): -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0056-Let-call-the-frame-from-arbitrary-code-avoiding-curc.patch b/rpms/sugar/0056-Let-call-the-frame-from-arbitrary-code-avoiding-curc.patch index 70d9b3e..d0855a3 100644 --- a/rpms/sugar/0056-Let-call-the-frame-from-arbitrary-code-avoiding-curc.patch +++ b/rpms/sugar/0056-Let-call-the-frame-from-arbitrary-code-avoiding-curc.patch @@ -1,8 +1,9 @@ -From 9640640f44486b325027b79be2117dab5472f496 Mon Sep 17 00:00:00 2001 +From d104ecfecdc27eb90090fa3eba2f34eeaf037f6b Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@member.fsf.org> Date: Wed, 12 Jan 2011 19:32:31 +0000 -Subject: [PATCH sugar 56/74] Let call the frame from arbitrary code avoiding +Subject: [PATCH 56/82] Let call the frame from arbitrary code avoiding curcular imports +Organization: Sugar Labs Foundation --- src/jarabe/frame/__init__.py | 4 +--- @@ -28,5 +29,5 @@ index b3e4b80..a047b91 100644 if not _view: _view = Frame() -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0057-Notify-on-not-sent-feedbacks-fix-issue-with-not-auto.patch b/rpms/sugar/0057-Notify-on-not-sent-feedbacks-fix-issue-with-not-auto.patch index c40fa82..915dc38 100644 --- a/rpms/sugar/0057-Notify-on-not-sent-feedbacks-fix-issue-with-not-auto.patch +++ b/rpms/sugar/0057-Notify-on-not-sent-feedbacks-fix-issue-with-not-auto.patch @@ -1,8 +1,9 @@ -From 15fd367960ee975ed58174702cd74a2d7d66396e Mon Sep 17 00:00:00 2001 +From 68ddd54ac92e38f7c6af6201cf6135ff61695558 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@activitycentral.org> Date: Sun, 6 Feb 2011 15:52:50 +0000 -Subject: [PATCH sugar 57/74] Notify on not sent feedbacks; fix issue with not - auto resend +Subject: [PATCH 57/82] Notify on not sent feedbacks; fix issue with not auto + resend +Organization: Sugar Labs Foundation --- src/jarabe/model/feedback_collector.py | 32 ++++++++++++++++++++------------ @@ -12,7 +13,7 @@ diff --git a/src/jarabe/model/feedback_collector.py b/src/jarabe/model/feedback_ index 93fa704..e239c9a 100644 --- a/src/jarabe/model/feedback_collector.py +++ b/src/jarabe/model/feedback_collector.py -@@ -25,12 +25,14 @@ +@@ -25,12 +25,14 @@ from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.generator import Generator from email.encoders import encode_noop @@ -88,7 +89,7 @@ index 93fa704..e239c9a 100644 def run(self): try: -@@ -151,11 +154,16 @@ def run(self): +@@ -151,11 +154,16 @@ class _SubmitThread(threading.Thread): response = conn.getresponse() if response.status != 200: @@ -108,5 +109,5 @@ index 93fa704..e239c9a 100644 os.unlink(self._tar_file) self._tar_file = None -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0058-Switch-default-feedback-serve-to-feedback.sl.o-clean.patch b/rpms/sugar/0058-Switch-default-feedback-serve-to-feedback.sl.o-clean.patch index 7f8d518..3da8f71 100644 --- a/rpms/sugar/0058-Switch-default-feedback-serve-to-feedback.sl.o-clean.patch +++ b/rpms/sugar/0058-Switch-default-feedback-serve-to-feedback.sl.o-clean.patch @@ -1,8 +1,9 @@ -From 0c5badda0f28f9f75a2d1316c24649e50a623711 Mon Sep 17 00:00:00 2001 +From ee0aa73559938cdb5d03a2ae2e03b6dd867abb0f Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@activitycentral.org> Date: Sat, 12 Feb 2011 06:56:55 +0000 -Subject: [PATCH sugar 58/74] Switch default feedback serve to feedback.sl.o; - clean up gconf doc strings +Subject: [PATCH 58/82] Switch default feedback serve to feedback.sl.o; clean + up gconf doc strings +Organization: Sugar Labs Foundation --- data/sugar.schemas.in | 6 +++--- @@ -40,5 +41,5 @@ index fb18d19..6c0d70b 100644 <short>Server host to send reports to</short> <long>Server that will handle reports sent via HTTPS POST requests.</long> -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0059-Do-not-send-empty-feedback-reports-if-anonymous_with.patch b/rpms/sugar/0059-Do-not-send-empty-feedback-reports-if-anonymous_with.patch index 6e29242..986a01e 100644 --- a/rpms/sugar/0059-Do-not-send-empty-feedback-reports-if-anonymous_with.patch +++ b/rpms/sugar/0059-Do-not-send-empty-feedback-reports-if-anonymous_with.patch @@ -1,8 +1,9 @@ -From 2ef6c6d0fc0cf10698a28880b89208b85f0b32e7 Mon Sep 17 00:00:00 2001 +From 28ab638b996a6429644e8112ba2a0f52d19d5fc6 Mon Sep 17 00:00:00 2001 From: Aleksey Lim <alsroot@activitycentral.org> Date: Sat, 12 Feb 2011 07:17:17 +0000 -Subject: [PATCH sugar 59/74] Do not send empty feedback reports if +Subject: [PATCH 59/82] Do not send empty feedback reports if anonymous_with_sn is enabled +Organization: Sugar Labs Foundation --- src/jarabe/model/feedback_collector.py | 24 +++++++++++++----------- @@ -62,5 +63,5 @@ index e239c9a..95dc4ee 100644 report = simplejson.dumps(_reports) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch b/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch index 3cb7872..cf89d8f 100644 --- a/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch +++ b/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch @@ -1,8 +1,9 @@ -From 15aa8fb2be53a1950f15d1a4ae630967a82319b9 Mon Sep 17 00:00:00 2001 +From 67d9c3117b51865c209063a8c285ce23d1d99238 Mon Sep 17 00:00:00 2001 From: Anish Mangal <anish@sugarlabs.org> Date: Mon, 10 Jan 2011 22:36:03 -0300 -Subject: [PATCH sugar 60/74] Replace activity updater with microformat - compatible one +Subject: [PATCH 60/82] Replace activity updater with microformat compatible + one +Organization: Sugar Labs Foundation This patch replaces the Sugar activity updater with one, that supports activity microformats. @@ -460,7 +461,7 @@ diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/update index 7ea445f..d7fd528 100755 --- a/extensions/cpsection/updater/model.py +++ b/extensions/cpsection/updater/model.py -@@ -37,8 +37,26 @@ +@@ -37,8 +37,26 @@ from sugar.bundle.bundleversion import NormalizedVersion from jarabe.model import bundleregistry @@ -488,7 +489,7 @@ index 7ea445f..d7fd528 100755 class UpdateModel(gobject.GObject): __gtype_name__ = 'SugarUpdateModel' -@@ -63,45 +81,51 @@ def __init__(self): +@@ -63,45 +81,51 @@ class UpdateModel(gobject.GObject): self._downloader = None self._cancelling = False @@ -568,7 +569,7 @@ index 7ea445f..d7fd528 100755 def update(self, bundle_ids): self._bundles_to_update = [] -@@ -226,12 +250,15 @@ def _cancel_updating(self): +@@ -226,12 +250,15 @@ class UpdateModel(gobject.GObject): class BundleUpdate(object): @@ -589,7 +590,7 @@ diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater index 814658f..30875e4 100644 --- a/extensions/cpsection/updater/view.py +++ b/extensions/cpsection/updater/view.py -@@ -122,7 +122,7 @@ def __progress_cb(self, model, action, bundle_name, current, total): +@@ -122,7 +122,7 @@ class ActivityUpdater(SectionView): return if action == UpdateModel.ACTION_CHECKING: @@ -598,7 +599,7 @@ index 814658f..30875e4 100644 elif action == UpdateModel.ACTION_DOWNLOADING: message = _('Downloading %s...') % bundle_name elif action == UpdateModel.ACTION_UPDATING: -@@ -361,11 +361,17 @@ def __init__(self, model): +@@ -361,11 +361,17 @@ class UpdateListModel(gtk.ListStore): row[self.SELECTED] = True row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon() @@ -622,5 +623,5 @@ index 814658f..30875e4 100644 row[self.DESCRIPTION] = '<b>%s</b>\n%s' % \ (bundle_update.bundle.get_name(), details) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0061-Don-t-choke-on-non-integer-activities.patch b/rpms/sugar/0061-Don-t-choke-on-non-integer-activities.patch index 7933006..97b4b85 100644 --- a/rpms/sugar/0061-Don-t-choke-on-non-integer-activities.patch +++ b/rpms/sugar/0061-Don-t-choke-on-non-integer-activities.patch @@ -1,7 +1,8 @@ -From 8f2c08718bff601b1f94dadd0c47a63b6db22c7d Mon Sep 17 00:00:00 2001 +From 4ca2eb24e162ecaf9f2edfa286b3deaaede44af8 Mon Sep 17 00:00:00 2001 From: Ajay Garg <ajaygargnsit@gmail.com> Date: Fri, 9 Sep 2011 17:21:50 +0000 -Subject: [PATCH sugar 61/74] Don't choke on non-integer activities +Subject: [PATCH 61/82] Don't choke on non-integer activities +Organization: Sugar Labs Foundation Since Sugar 0.92 activity versions can be non-integer strings. @@ -19,7 +20,7 @@ diff --git a/extensions/cpsection/updater/backends/microformat.py b/extensions/c index 97499aa..ca4f1d5 100644 --- a/extensions/cpsection/updater/backends/microformat.py +++ b/extensions/cpsection/updater/backends/microformat.py -@@ -133,7 +133,7 @@ def handle_starttag(self, tag, attrs): +@@ -133,7 +133,7 @@ class MicroformatParser(HTMLParser): def handle_data(self, data): if self._inside_activity_version: @@ -32,7 +33,7 @@ diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/update index d7fd528..1a3941c 100755 --- a/extensions/cpsection/updater/model.py +++ b/extensions/cpsection/updater/model.py -@@ -106,8 +106,10 @@ def __bundle_info_fetched_cb(self, new_bundles, error_message): +@@ -106,8 +106,10 @@ class UpdateModel(gobject.GObject): else: for bundle_id, info in new_bundles.items(): if bundle_id in self._current_bundles: @@ -49,7 +50,7 @@ diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater index 30875e4..559ab8d 100644 --- a/extensions/cpsection/updater/view.py +++ b/extensions/cpsection/updater/view.py -@@ -362,7 +362,7 @@ def __init__(self, model): +@@ -362,7 +362,7 @@ class UpdateListModel(gtk.ListStore): row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon() if bundle_update.package_type == 'update': @@ -59,5 +60,5 @@ index 30875e4..559ab8d 100644 {'current': bundle_update.bundle.get_activity_version(), 'new': bundle_update.version, -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0062-updater-Only-pre-select-already-installed-activities.patch b/rpms/sugar/0062-updater-Only-pre-select-already-installed-activities.patch index 5135637..74ff097 100644 --- a/rpms/sugar/0062-updater-Only-pre-select-already-installed-activities.patch +++ b/rpms/sugar/0062-updater-Only-pre-select-already-installed-activities.patch @@ -1,8 +1,9 @@ -From f20db12c6428f4124fd9feac7828f0a2ffa740a9 Mon Sep 17 00:00:00 2001 +From 841b64472b422b1ca91bd5e6a60cfd5a1bf83909 Mon Sep 17 00:00:00 2001 From: Ajay Garg <ajaygargnsit@gmail.com> Date: Fri, 9 Sep 2011 17:21:50 +0000 -Subject: [PATCH sugar 62/74] updater: Only pre-select already installed - activities (fixes SL#2822, AU#383) +Subject: [PATCH 62/82] updater: Only pre-select already installed activities + (fixes SL#2822, AU#383) +Organization: Sugar Labs Foundation OLPC AU uses the software updater to offer easy installing of optional activities. For this to work properly new activities must not be selected by @@ -19,7 +20,7 @@ diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater index 559ab8d..891f552 100644 --- a/extensions/cpsection/updater/view.py +++ b/extensions/cpsection/updater/view.py -@@ -358,10 +358,11 @@ def __init__(self, model): +@@ -358,10 +358,11 @@ class UpdateListModel(gtk.ListStore): for bundle_update in model.updates: row = [None] * 5 row[self.BUNDLE_ID] = bundle_update.bundle.get_bundle_id() @@ -33,5 +34,5 @@ index 559ab8d..891f552 100644 details = details % \ {'current': bundle_update.bundle.get_activity_version(), -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0063-Add-quz-and-aym-to-ALL_LINGUAS-take-2.patch b/rpms/sugar/0063-Add-quz-and-aym-to-ALL_LINGUAS-take-2.patch index c00a7b0..eae28e7 100644 --- a/rpms/sugar/0063-Add-quz-and-aym-to-ALL_LINGUAS-take-2.patch +++ b/rpms/sugar/0063-Add-quz-and-aym-to-ALL_LINGUAS-take-2.patch @@ -1,7 +1,8 @@ -From 2c5be25e218629f1b074bf0340ce4ca0ff715d17 Mon Sep 17 00:00:00 2001 +From 63900aa313843d2d36e21bafa9d2b8648c2594bb Mon Sep 17 00:00:00 2001 From: bernie <bernie@codewiz.org> Date: Fri, 25 Nov 2011 20:34:51 +0000 -Subject: [PATCH sugar 63/74] Add quz and aym to ALL_LINGUAS (take 2) +Subject: [PATCH 63/82] Add quz and aym to ALL_LINGUAS (take 2) +Organization: Sugar Labs Foundation Signed-off-by: Bernie Innocenti <bernie@codewiz.org> --- @@ -22,5 +23,5 @@ index 86f6116..8e6d871 100644 GETTEXT_PACKAGE=sugar AC_PROG_INTLTOOL([0.33]) -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0064-sugar-session-export-manual-Gnome-proxy-settings-as-.patch b/rpms/sugar/0064-sugar-session-export-manual-Gnome-proxy-settings-as-.patch index a5aa53e..864db39 100644 --- a/rpms/sugar/0064-sugar-session-export-manual-Gnome-proxy-settings-as-.patch +++ b/rpms/sugar/0064-sugar-session-export-manual-Gnome-proxy-settings-as-.patch @@ -1,8 +1,9 @@ -From 005a15f4c6954d5a2d25f3762b1f702117e854e2 Mon Sep 17 00:00:00 2001 +From 4b6d197541ef9c708d4ade7734b26f7477a05683 Mon Sep 17 00:00:00 2001 From: Jerry Vonau <jvonau@shaw.ca> Date: Mon, 12 Dec 2011 19:09:06 +0100 -Subject: [PATCH sugar 64/74] sugar-session: export manual Gnome proxy - settings as $http_proxy +Subject: [PATCH 64/82] sugar-session: export manual Gnome proxy settings as + $http_proxy +Organization: Sugar Labs Foundation Some applications and tools and even some parts of Sugar will use the http_proxy environment variable if set, but don't use the Gnome (GConf) proxy @@ -69,5 +70,5 @@ index ee0048d..7eaa8f1 100755 # this must be added early, so that it executes and unfreezes the screen -- -1.7.6 +1.7.4.4 diff --git a/rpms/sugar/0065-Localise-sugar-install-bundle.patch b/rpms/sugar/0065-Localise-sugar-install-bundle.patch new file mode 100644 index 0000000..1d456f2 --- /dev/null +++ b/rpms/sugar/0065-Localise-sugar-install-bundle.patch @@ -0,0 +1,83 @@ +From a5fa91d58ca9277c858d527cefb20292439b233d Mon Sep 17 00:00:00 2001 +From: Sascha Silbe <silbe@activitycentral.com> +Date: Mon, 5 Sep 2011 15:10:33 +0000 +Subject: [PATCH 65/82] Localise sugar-install-bundle +Organization: Sugar Labs Foundation + +sugar-install-bundle contains user-visible strings that should be translated. + +By default xgettext guesses the programming language based on the file +extension. Since the scripts in bin/ don't have any extension, we need to tell +xgettext the source language by explicitly stating it in XGETTEXT_OPTIONS. +This works because the sugar package only contains Python files. + +Signed-off-by: Sascha Silbe <silbe@activitycentral.com> +--- + bin/sugar-install-bundle | 19 +++++++++++++++---- + po/Makevars | 6 +++++- + po/POTFILES.in | 1 + + 3 files changed, 21 insertions(+), 5 deletions(-) + +diff --git a/bin/sugar-install-bundle b/bin/sugar-install-bundle +index 52deada..d0b6fc5 100644 +--- a/bin/sugar-install-bundle ++++ b/bin/sugar-install-bundle +@@ -1,20 +1,31 @@ + #!/usr/bin/env python ++import gettext + import sys + + from sugar.bundle.activitybundle import ActivityBundle + ++from jarabe import config ++ + from dbus.mainloop.glib import DBusGMainLoop +-DBusGMainLoop(set_as_default=True) ++ + + def cmd_help(): +- print 'Usage: sugar-install-bundle [ bundlename ] \n\n\ +- Install an activity bundle (.xo). \n' ++ print _('Usage: sugar-install-bundle [ bundlename ] \n\n' ++ 'Install an activity bundle (.xo). \n') ++ ++ ++gettext.bindtextdomain('sugar', config.locale_path) ++gettext.bindtextdomain('sugar-toolkit', config.locale_path) ++gettext.textdomain('sugar') ++_ = gettext.gettext + + if len(sys.argv) != 2: + cmd_help() + sys.exit(2) + ++DBusGMainLoop(set_as_default=True) ++ + bundle = ActivityBundle(sys.argv[1]) + bundle.install() + +-print "%s: '%s' installed." % (sys.argv[0], sys.argv[1]) ++print _('%s: %r installed.') % (sys.argv[0], sys.argv[1]) +diff --git a/po/Makevars b/po/Makevars +index da4dba6..d9251f9 100644 +--- a/po/Makevars ++++ b/po/Makevars +@@ -1 +1,5 @@ +-XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=Q_ --keyword=g_dgettext:2 --keyword=g_dngettext:2,3 --keyword=g_dpgettext:2 --keyword=g_dpgettext2=2c,3 --keyword=pgettext:1c,2 ++XGETTEXT_OPTIONS = --language=python --keyword=_ --keyword=N_ \ ++ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=Q_ \ ++ --keyword=g_dgettext:2 --keyword=g_dngettext:2,3 \ ++ --keyword=g_dpgettext:2 --keyword=g_dpgettext2=2c,3 \ ++ --keyword=pgettext:1c,2 +diff --git a/po/POTFILES.in b/po/POTFILES.in +index b799339..aa19b58 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -1,3 +1,4 @@ ++bin/sugar-install-bundle + extensions/cpsection/aboutme/__init__.py + extensions/cpsection/aboutme/model.py + extensions/cpsection/aboutme/view.py +-- +1.7.4.4 + diff --git a/rpms/sugar/0066-sugar-install-bundle-skip-older-bundles-by-default-a.patch b/rpms/sugar/0066-sugar-install-bundle-skip-older-bundles-by-default-a.patch new file mode 100644 index 0000000..32960f1 --- /dev/null +++ b/rpms/sugar/0066-sugar-install-bundle-skip-older-bundles-by-default-a.patch @@ -0,0 +1,83 @@ +From 67ce1a5064d157ed7eaac25c3616ed741e6afb64 Mon Sep 17 00:00:00 2001 +From: Sascha Silbe <silbe@activitycentral.com> +Date: Mon, 5 Sep 2011 15:10:34 +0000 +Subject: [PATCH 66/82] sugar-install-bundle: skip older bundles by default, + accept multiple bundles +Organization: Sugar Labs Foundation + +If the user has already installed a newer version of an activity, +sugar-install-bundle shouldn't overwrite it. Since there might be cases where +this is still useful we provide an option to force installing the bundle even +if it's the same or even an older version. + +The changes necessary to parse the CLI options also add back the support for +installing multiple bundles in one invocation. + +Signed-off-by: Sascha Silbe <silbe@activitycentral.com> +Reviewed-by: James Cameron <quozl@laptop.org> +--- + bin/sugar-install-bundle | 34 +++++++++++++++++++++++----------- + 1 files changed, 23 insertions(+), 11 deletions(-) + +diff --git a/bin/sugar-install-bundle b/bin/sugar-install-bundle +index d0b6fc5..47b0084 100644 +--- a/bin/sugar-install-bundle ++++ b/bin/sugar-install-bundle +@@ -1,17 +1,26 @@ + #!/usr/bin/env python + import gettext ++from optparse import OptionParser + import sys + + from sugar.bundle.activitybundle import ActivityBundle ++from sugar.bundle.bundle import AlreadyInstalledException + + from jarabe import config ++from jarabe.model import bundleregistry + + from dbus.mainloop.glib import DBusGMainLoop + + +-def cmd_help(): +- print _('Usage: sugar-install-bundle [ bundlename ] \n\n' +- 'Install an activity bundle (.xo). \n') ++def install_bundle(name, force): ++ bundle = ActivityBundle(name) ++ registry = bundleregistry.get_registry() ++ try: ++ registry.install(bundle, force_downgrade=force) ++ except AlreadyInstalledException: ++ print _("%s: %r or newer is already installed") % (sys.argv[0], name) ++ else: ++ print _('%s: %r installed.') % (sys.argv[0], name) + + + gettext.bindtextdomain('sugar', config.locale_path) +@@ -19,13 +28,16 @@ gettext.bindtextdomain('sugar-toolkit', config.locale_path) + gettext.textdomain('sugar') + _ = gettext.gettext + +-if len(sys.argv) != 2: +- cmd_help() +- sys.exit(2) ++parser = OptionParser(usage=_('usage: %prog [options] {bundle}'), ++ description=_('Install an activity bundle (.xo)')) ++parser.add_option('-f', '--force', dest='force', ++ help=_("Install bundle even if it isn't newer than" ++ " what's currently installed"), ++ action='store_true', default=False) ++options, args = parser.parse_args() ++if not args: ++ parser.error(_('no bundle given')) + + DBusGMainLoop(set_as_default=True) +- +-bundle = ActivityBundle(sys.argv[1]) +-bundle.install() +- +-print _('%s: %r installed.') % (sys.argv[0], sys.argv[1]) ++for file_name in args: ++ install_bundle(file_name, options.force) +-- +1.7.4.4 + diff --git a/rpms/sugar/0067-Partial-fix-for-GSM-connection-time-see-SL-2992.patch b/rpms/sugar/0067-Partial-fix-for-GSM-connection-time-see-SL-2992.patch new file mode 100644 index 0000000..770185b --- /dev/null +++ b/rpms/sugar/0067-Partial-fix-for-GSM-connection-time-see-SL-2992.patch @@ -0,0 +1,67 @@ +From 58e16f8d7c2d860e7442884e54e0c12a61502655 Mon Sep 17 00:00:00 2001 +From: Sascha Silbe <silbe@activitycentral.com> +Date: Mon, 9 Jan 2012 19:45:38 +0100 +Subject: [PATCH 67/82] (Partial) fix for GSM connection time (see SL#2992) +Organization: Sugar Labs Foundation + +The connection time has apparently been off by a couple of hours (depending +on the time zone; see SL#2992 [1]) ever since we had this feature +(commit f5daf6e). + +This patch addresses the most important use case (controlling the GSM +connection using the Sugar UI) by fixing the time format. If a system +connection is used and brought up before Sugar starts, connection time +calculation will still be off; this seems to be unfixable [1]. + +[1] http://mail.gnome.org/archives/networkmanager-list/2012-January/thread.html#00022 + +Signed-off-by: Sascha Silbe <silbe@activitycentral.com> +--- + extensions/deviceicon/network.py | 17 +++++++---------- + 1 files changed, 7 insertions(+), 10 deletions(-) + +diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py +index 234b06b..c70db70 100644 +--- a/extensions/deviceicon/network.py ++++ b/extensions/deviceicon/network.py +@@ -364,11 +364,10 @@ class GsmPalette(Palette): + self.error_description_label.set_text(message) + self.error_description_label.show() + +- def update_connection_time(self, connection_time=None): +- if connection_time is not None: +- formatted_time = connection_time.strftime('%H:%M:%S') +- else: +- formatted_time = '00:00:00' ++ def update_connection_time(self, connection_time=0): ++ """Set the time we have been connected for, in seconds""" ++ formatted_time = time.strftime('%H:%M:%S', ++ time.gmtime(connection_time)) + text = _('Connected for %s') % (formatted_time, ) + self.props.secondary_text = glib.markup_escape_text(text) + +@@ -888,8 +887,8 @@ class GsmDeviceView(TrayIcon): + connection = network.find_gsm_connection() + if connection is not None: + connection.set_connected() +- self._connection_timestamp = time.time() - \ +- connection.get_settings().connection.timestamp ++ settings = connection.get_settings() ++ self._connection_timestamp = settings.connection.timestamp + self._connection_time_handler = gobject.timeout_add_seconds( \ + 1, self.__connection_timecount_cb) + self._palette.update_connection_time() +@@ -932,9 +931,7 @@ class GsmDeviceView(TrayIcon): + self._palette.update_stats(in_bytes, out_bytes) + + def __connection_timecount_cb(self): +- self._connection_timestamp = self._connection_timestamp + 1 +- connection_time = \ +- datetime.datetime.fromtimestamp(self._connection_timestamp) ++ connection_time = time.time() - self._connection_timestamp + self._palette.update_connection_time(connection_time) + return True + +-- +1.7.4.4 + diff --git a/rpms/sugar/0068-Journal-detail-view-don-t-choke-on-invalid-keep-prop.patch b/rpms/sugar/0068-Journal-detail-view-don-t-choke-on-invalid-keep-prop.patch new file mode 100644 index 0000000..9e3a6a3 --- /dev/null +++ b/rpms/sugar/0068-Journal-detail-view-don-t-choke-on-invalid-keep-prop.patch @@ -0,0 +1,48 @@ +From 70264e5e5c5440bdf1e36e0b3591aac9c5d98b06 Mon Sep 17 00:00:00 2001 +From: Sascha Silbe <silbe@activitycentral.com> +Date: Wed, 18 Jan 2012 01:56:22 +0000 +Subject: [PATCH 68/82] Journal detail view: don't choke on invalid 'keep' + property (fixes SL#1591) +Organization: Sugar Labs Foundation + +Properties of data store entries can get corrupted, e.g. due to low level +crashes or running out of battery (see OLPC#11372 [1] for a real-life +example). In addition any activity can - accidentally or on purpose - write +data store entries with arbitrary metadata. + +By comparing the 'keep' property as a string we can avoid the ValueError that +might happen when trying to convert the property value to an integer. + +[1] https://dev.laptop.org/ticket/11372 + +Reported-by: Gary Martin <garycmartin@googlemail.com> +Signed-off-by: Sascha Silbe <silbe@activitycentral.com> +--- + src/jarabe/journal/expandedentry.py | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py +index 4e99dc2..03f8cd1 100644 +--- a/src/jarabe/journal/expandedentry.py ++++ b/src/jarabe/journal/expandedentry.py +@@ -144,7 +144,7 @@ class ExpandedEntry(hippo.CanvasBox): + return + self._metadata = metadata + +- self._keep_icon.keep = (int(metadata.get('keep', 0)) == 1) ++ self._keep_icon.keep = (str(metadata.get('keep', 0)) == '1') + + self._icon = self._create_icon() + self._icon_box.clear() +@@ -419,7 +419,7 @@ class ExpandedEntry(hippo.CanvasBox): + self._update_title_sid = None + + def get_keep(self): +- return int(self._metadata.get('keep', 0)) == 1 ++ return (str(self._metadata.get('keep', 0)) == '1') + + def _keep_icon_activated_cb(self, keep_icon): + if self.get_keep(): +-- +1.7.4.4 + diff --git a/rpms/sugar/0069-Add-capability-to-connect-to-WPA-WPA2-Enterprise-Net.patch b/rpms/sugar/0069-Add-capability-to-connect-to-WPA-WPA2-Enterprise-Net.patch new file mode 100644 index 0000000..3fa385c --- /dev/null +++ b/rpms/sugar/0069-Add-capability-to-connect-to-WPA-WPA2-Enterprise-Net.patch @@ -0,0 +1,811 @@ +From 4574f11d51cbb5c6b8ff9856e7d58c9dadaac26f Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Thu, 15 Dec 2011 14:55:13 +0000 +Subject: [PATCH 69/82] Add capability to connect to WPA/WPA2-Enterprise + Networks. +Organization: Sugar Labs Foundation + +(Note that this is a consolidated patch, to be applied in full; +and NOT OVER version-4, version-3, version-2, and version-1 patches). + +Enhancements/Fixes of current version (version-5), over version-4 :: +-------------------------------------------------------------------- + +a. Fixed the regression - Unable to connect to Unprotected-Wireless-Networks. + Catcher :: Anish. + +For the record. +---------------- +Enhancements/Fixes of version-4, over version-3 :: +-------------------------------------------------- + +a. Fixing logging statements, and some formatting-changes (Thanks Sascha). +b. Not passing parameters to NetworkManager, that are not entered (required), + as in TTLS- and PEAP-configuration (Thanks Anish). + +For the record. +---------------- +Enhancements/Fixes of version-3, over version-2 :: +-------------------------------------------------------------------- + +a. Now, TLS-based-authentication is also supported. + ----------------------------------------------- + + Thus, now, the following three authentication types are supported : + (i) TTLS + (ii) PEAP + (iii) TLS + + Following authentication types are still not supported : + (i) LEAP (actually this may work, but the set-up has not been + able to be worked out, and hence, this has not been + verified, even with nm-applet). + +b. Journal-Chooser integration. + ---------------------------- + + This is useful in picking up chooser-entries (especially in case + of certificates requirements, like in TLS and TTLS). + +For the record. +---------------- +Enhancements/Fixes of version-2, over version-1 :: +-------------------------------------------------- + +a. Network-Autoconnect-Upon-Hibernate-Resume + ------------------------------------------ + + Fixing the case, when network wouldn't (auto-)come-up, + when the XO resumed from hibernation. (Thanks Anish for + catching that :-) ). + However, there wasn't a problem with auto-connect-on-reboot; + it's working fine. + +Signed-off-by: Ajay Garg <ajay@activitycentral.com> +--- + src/jarabe/desktop/keydialog.py | 247 +++++++++++++++++++++++++++++++++- + src/jarabe/desktop/networkviews.py | 259 ++++++++++++++++++++++++++++++++++- + src/jarabe/journal/objectchooser.py | 19 +++ + src/jarabe/model/network.py | 28 +++- + 4 files changed, 541 insertions(+), 12 deletions(-) + +diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py +index c72f498..d8c8cf9 100644 +--- a/src/jarabe/desktop/keydialog.py ++++ b/src/jarabe/desktop/keydialog.py +@@ -20,9 +20,13 @@ from gettext import gettext as _ + + import gtk + import dbus ++import os ++import shutil + ++from sugar import env + from jarabe.model import network + from jarabe.model.network import Secrets ++from jarabe.journal.objectchooser import ObjectChooser + + + IW_AUTH_ALG_OPEN_SYSTEM = 'open' +@@ -32,6 +36,11 @@ WEP_PASSPHRASE = 1 + WEP_HEX = 2 + WEP_ASCII = 3 + ++SETTING_TYPE_STRING = 1 ++SETTING_TYPE_LIST = 2 ++SETTING_TYPE_CHOOSER = 3 ++ ++ + + def string_is_hex(key): + is_hex = True +@@ -120,6 +129,226 @@ class KeyDialog(gtk.Dialog): + return self._response + + ++class NetworkParameters(gtk.HBox): ++ def __init__(self, auth_param): ++ gtk.HBox.__init__(self, homogeneous=True) ++ self._key = auth_param._key_name ++ self._label = gtk.Label(_(auth_param._key_label)) ++ self._key_type = auth_param._key_type ++ self._auth_param = auth_param ++ ++ self.pack_start(self._label) ++ self._label.show() ++ ++ if self._is_entry(): ++ self._entry = gtk.Entry() ++ self.pack_start(self._entry) ++ self._entry.show() ++ elif self._is_liststore(): ++ self._option_store = gtk.ListStore(str, str) ++ for option in auth_param._options: ++ self._option_store.append(option) ++ ++ self._entry = auth_param._options[0][1] ++ self._option_combo = gtk.ComboBox(self._option_store) ++ cell = gtk.CellRendererText() ++ self._option_combo.pack_start(cell, True) ++ self._option_combo.add_attribute(cell, 'text', 0) ++ self._option_combo.set_active(0) ++ self._option_combo.connect('changed', ++ self._option_combo_changed_cb) ++ self.pack_start(self._option_combo) ++ self.show() ++ self._option_combo.show() ++ elif self._is_chooser(): ++ self._chooser_button = gtk.Button(_('Choose..')) ++ self._chooser_button.connect('clicked', ++ self._object_chooser_cb) ++ self.pack_start(self._chooser_button) ++ self._chooser_button.show() ++ self._entry = '' ++ ++ def _is_entry(self): ++ return ( not self._is_chooser() ) and \ ++ ( len(self._auth_param._options) == 0 ) ++ ++ def _is_liststore(self): ++ return ( not self._is_chooser() ) and \ ++ ( len(self._auth_param._options) > 0 ) ++ ++ def _is_chooser(self): ++ return self._key_type == SETTING_TYPE_CHOOSER ++ ++ def _object_chooser_cb(self, chooser_button): ++ self._want_document = True ++ self._show_picker_cb() ++ ++ def _show_picker_cb(self): ++ if not self._want_document: ++ return ++ self._chooser = ObjectChooser() ++ self._chooser._set_callback(self.__process_selected_journal_object) ++ ++ self._chooser.show() ++ ++ def __process_selected_journal_object(self, object_id): ++ jobject = self._chooser.get_selected_object() ++ if jobject and jobject.file_path: ++ file_basename = \ ++ os.path.basename(jobject._metadata._properties['title']) ++ self._chooser_button.set_label(file_basename) ++ ++ ++ ++ # The chooser entries are chosen from the journal. ++ # Now, the journal entries misbehave, and the entries keep ++ # changing their names. Thus, in order to have the same ++ # entry available at later times, we need to store the ++ # selected entries at a 'fixed-name' place. ++ # ++ # DISCOVERY-CUM-REQUIREMENT: ++ # ------------------------- ++ # It is needed that networks auto-connect on reboot; and ++ # the user need not ne re-prompted to enter the ++ # network-parameters. ++ ++ profile_path = env.get_profile_path() ++ self._entry = os.path.join(profile_path, 'nm', ++ file_basename) ++ ++ # Remove (older) file, if it exists. ++ if os.path.exists(self._entry): ++ os.remove(self._entry) ++ ++ # Copy the file. ++ shutil.copy2(jobject.file_path, self._entry) ++ ++ self._chooser.destroy() ++ ++ def _option_combo_changed_cb(self, widget): ++ it = self._option_combo.get_active_iter() ++ (value, ) = self._option_store.get(it, 1) ++ self._entry = value ++ ++ def _get_key(self): ++ return self._key ++ ++ def _get_value(self): ++ if self._is_entry(): ++ return self._entry.get_text() ++ elif self._is_liststore(): ++ return self._entry ++ elif self._is_chooser(): ++ if len(self._entry) > 0: ++ return dbus.ByteArray('file://' + self._entry + '\0') ++ else: ++ return self._entry ++ ++ ++class KeyValuesDialog(gtk.Dialog): ++ def __init__(self, auth_lists, final_callback, uuid, settings): ++ # This must not be "modal", else the "chooser" widgets won't ++ # accept anything !! ++ gtk.Dialog.__init__(self) ++ self.set_title(_('Wireless Parameters required')) ++ ++ self._spacing_between_children_widgets = 5 ++ self._auth_lists = auth_lists ++ self._final_callback = final_callback ++ self._uuid = uuid ++ self._settings = settings ++ ++ label = gtk.Label(_("Please enter parameters\n")) ++ self.vbox.set_spacing(self._spacing_between_children_widgets) ++ self.vbox.pack_start(label) ++ ++ self._auth_type_store = gtk.ListStore(str, str) ++ for auth_list in self._auth_lists: ++ self._auth_type_store.append([auth_list._auth_label, ++ auth_list._auth_type]) ++ ++ self._auth_type_combo = gtk.ComboBox(self._auth_type_store) ++ cell = gtk.CellRendererText() ++ self._auth_type_combo.pack_start(cell, True) ++ self._auth_type_combo.add_attribute(cell, 'text', 0) ++ self._auth_type_combo.set_active(0) ++ self._auth_type_combo.connect('changed', ++ self._auth_type_combo_changed_cb) ++ self._auth_type_box = gtk.HBox(homogeneous=True) ++ self._auth_label = gtk.Label(_('Authentication')) ++ self._auth_type_box.pack_start(self._auth_label, expand=False) ++ self._auth_type_box.pack_start(self._auth_type_combo, ++ expand=False) ++ self.vbox.pack_start(self._auth_type_box) ++ self._auth_label.show() ++ self._auth_type_combo.show() ++ ++ self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK) ++ self.set_default_response(gtk.RESPONSE_OK) ++ self.set_has_separator(True) ++ ++ self.connect('response', self._fetch_values) ++ ++ auth_type = self._auth_lists[0]._auth_type ++ self._selected_auth_list = self._select_auth_list(auth_type) ++ self._add_key_value('eap', auth_type) ++ self._add_container_box() ++ ++ def _auth_type_combo_changed_cb(self, widget): ++ it = self._auth_type_combo.get_active_iter() ++ (auth_type, ) = self._auth_type_store.get(it, 1) ++ self._selected_auth_list = self._select_auth_list(auth_type) ++ self._add_key_value('eap', auth_type) ++ self._reset() ++ ++ def _select_auth_list(self, auth_type): ++ for auth_list in self._auth_lists: ++ if auth_list._params_list[0]._options[0][1] == auth_type: ++ return auth_list ++ ++ def _populate_auth_params(self, auth_list): ++ for auth_param in auth_list._params_list[1:]: ++ obj = NetworkParameters(auth_param) ++ self._key_values_box.pack_start(obj) ++ obj.show() ++ ++ def _reset(self): ++ self.vbox.remove(self._key_values_box) ++ self._add_container_box() ++ ++ def _add_container_box(self): ++ self._key_values_box = \ ++ gtk.VBox(spacing=self._spacing_between_children_widgets) ++ self.vbox.pack_start(self._key_values_box) ++ self._key_values_box.show() ++ self._populate_auth_params(self._selected_auth_list) ++ ++ def _remove_all_params(self): ++ self._key_values_box.remove_all() ++ ++ def _fetch_values(self, key_dialog, response_id): ++ if response_id == gtk.RESPONSE_OK: ++ for child in self._key_values_box.get_children(): ++ key = child._get_key() ++ value = child._get_value() ++ self._add_key_value(key, value) ++ ++ key_dialog.destroy() ++ self._final_callback(self._uuid, self._settings, ++ self._selected_auth_list) ++ ++ def _add_key_value(self, key, value): ++ for auth_param in self._selected_auth_list._params_list: ++ if auth_param._key_name == key: ++ if (auth_param._key_type == SETTING_TYPE_STRING) or \ ++ (auth_param._key_type == SETTING_TYPE_CHOOSER): ++ auth_param._value = value ++ elif auth_param._key_type == SETTING_TYPE_LIST: ++ values = [] ++ values.append(value) ++ auth_param._value = values ++ ++ + class WEPKeyDialog(KeyDialog): + def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, settings, + response): +@@ -218,7 +447,7 @@ class WEPKeyDialog(KeyDialog): + self.set_response_sensitive(gtk.RESPONSE_OK, valid) + + +-class WPAKeyDialog(KeyDialog): ++class WPAPersonalKeyDialog(KeyDialog): + def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, settings, + response): + KeyDialog.__init__(self, ssid, flags, wpa_flags, rsn_flags, +@@ -296,14 +525,26 @@ def create(ssid, flags, wpa_flags, rsn_flags, dev_caps, settings, response): + rsn_flags == network.NM_802_11_AP_SEC_NONE: + key_dialog = WEPKeyDialog(ssid, flags, wpa_flags, rsn_flags, + dev_caps, settings, response) +- else: +- key_dialog = WPAKeyDialog(ssid, flags, wpa_flags, rsn_flags, ++ elif (wpa_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK) or \ ++ (rsn_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK): ++ key_dialog = WPAPersonalKeyDialog(ssid, flags, wpa_flags, rsn_flags, + dev_caps, settings, response) ++ elif (wpa_flags & network.NM_802_11_AP_SEC_KEY_MGMT_802_1X) or \ ++ (rsn_flags & network.NM_802_11_AP_SEC_KEY_MGMT_802_1X): ++ # nothing. All details are asked for WPA/WPA2-Enterprise ++ # networks, before the conneection-activation is done. ++ return + + key_dialog.connect('response', _key_dialog_response_cb) + key_dialog.show_all() + + ++def get_key_values(key_list, final_callback, uuid, settings): ++ key_dialog = KeyValuesDialog(key_list, final_callback, ++ uuid, settings) ++ key_dialog.show_all() ++ ++ + def _key_dialog_response_cb(key_dialog, response_id): + response = key_dialog.get_response_object() + secrets = None +diff --git a/src/jarabe/desktop/networkviews.py b/src/jarabe/desktop/networkviews.py +index 2fb8593..d36aeb3 100644 +--- a/src/jarabe/desktop/networkviews.py ++++ b/src/jarabe/desktop/networkviews.py +@@ -22,6 +22,7 @@ import hashlib + + import dbus + import glib ++import string + + from sugar.graphics.icon import Icon + from sugar.graphics.xocolor import XoColor +@@ -56,6 +57,192 @@ _OLPC_MESH_ICON_NAME = 'network-mesh' + + _FILTERED_ALPHA = 0.33 + ++SETTING_TYPE_STRING = 1 ++SETTING_TYPE_LIST = 2 ++SETTING_TYPE_CHOOSER = 3 ++ ++ ++class AuthenticationType: ++ def __init__(self, auth_label, auth_type, params_list): ++ self._auth_label = auth_label ++ self._auth_type = auth_type ++ self._params_list = params_list ++ ++ ++class AuthenticationParameter: ++ def __init__(self, key_name, key_label, key_type, ++ options): ++ self._key_name = key_name ++ self._key_label = key_label ++ self._key_type = key_type ++ self._options = options ++ self._value = None ++ ++ ++ ++AUTHENTICATION_LIST = \ ++ [ ++ AuthenticationType('TLS', ++ 'tls', ++ [ ++ AuthenticationParameter( ++ 'eap', ++ 'Authentication', ++ SETTING_TYPE_LIST, ++ [['TLS', 'tls']] ++ ), ++ AuthenticationParameter( ++ 'identity', ++ 'Identity', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'client-cert', ++ 'User certificate', ++ SETTING_TYPE_CHOOSER, ++ [] ++ ), ++ AuthenticationParameter( ++ 'ca-cert', ++ 'CA certificate', ++ SETTING_TYPE_CHOOSER, ++ [] ++ ), ++ AuthenticationParameter( ++ 'private-key', ++ 'Private key', ++ SETTING_TYPE_CHOOSER, ++ [] ++ ), ++ AuthenticationParameter( ++ 'private-key-password', ++ 'Private Key password', ++ SETTING_TYPE_STRING, ++ [] ++ ) ++ ] ++ ), ++ AuthenticationType('LEAP', ++ 'leap', ++ [ ++ AuthenticationParameter( ++ 'eap', ++ 'Authentication', ++ SETTING_TYPE_LIST, ++ [['LEAP', 'leap']] ++ ), ++ AuthenticationParameter( ++ 'identity', ++ 'Username', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'password', ++ 'Password', ++ SETTING_TYPE_STRING, ++ [] ++ ) ++ ] ++ ), ++ AuthenticationType('Tunnelled TLS', ++ 'ttls', ++ [ ++ AuthenticationParameter( ++ 'eap', ++ 'Authentication', ++ SETTING_TYPE_LIST, ++ [['Tunnelled TLS', 'ttls']] ++ ), ++ AuthenticationParameter( ++ 'anonymous-identity', ++ 'Anonymous identity', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'ca-cert', ++ 'CA certificate', ++ SETTING_TYPE_CHOOSER, ++ [] ++ ), ++ AuthenticationParameter( ++ 'phase2-auth', ++ 'Inner Authentication', ++ SETTING_TYPE_STRING, ++ [['PAP', 'pap'], ++ ['MSCHAP', 'mschap'], ++ ['MSCHAPv2', 'mschapv2'], ++ ['CHAP', 'chap']] ++ ), ++ AuthenticationParameter( ++ 'identity', ++ 'Username', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'password', ++ 'Password', ++ SETTING_TYPE_STRING, ++ [] ++ ) ++ ] ++ ), ++ AuthenticationType('Protected EAP (PEAP)', ++ 'peap', ++ [ ++ AuthenticationParameter( ++ 'eap', ++ 'Authentication', ++ SETTING_TYPE_LIST, ++ [['Protected EAP (PEAP)', 'peap']] ++ ), ++ AuthenticationParameter( ++ 'anonymous-identity', ++ 'Anonymous identity', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'ca-cert', ++ 'CA certificate', ++ SETTING_TYPE_CHOOSER, ++ [] ++ ), ++ AuthenticationParameter( ++ 'phase1-peapver', ++ 'PEAP version', ++ SETTING_TYPE_STRING, ++ [['Automatic', ''], ++ ['Version 0', '0'], ++ ['Version 1', '1']] ++ ), ++ AuthenticationParameter( ++ 'phase2-auth', ++ 'Inner Authentication', ++ SETTING_TYPE_STRING, ++ [['MSCHAPv2', 'mschapv2'], ++ ['MD5', 'md5'], ++ ['GTC', 'gtc']] ++ ), ++ AuthenticationParameter( ++ 'identity', ++ 'Username', ++ SETTING_TYPE_STRING, ++ [] ++ ), ++ AuthenticationParameter( ++ 'password', ++ 'Password', ++ SETTING_TYPE_STRING, ++ [] ++ ) ++ ] ++ ) ++ ] ++ + + class WirelessNetworkView(CanvasPulsingIcon): + def __init__(self, initial_ap): +@@ -323,7 +510,7 @@ class WirelessNetworkView(CanvasPulsingIcon): + group = self._add_ciphers_from_flags(self._rsn_flags, False) + wireless_security = WirelessSecurity() + wireless_security.key_mgmt = 'wpa-psk' +- wireless_security.proto = 'rsn' ++ wireless_security.proto = ['rsn'] + wireless_security.pairwise = pairwise + wireless_security.group = group + return wireless_security +@@ -335,11 +522,71 @@ class WirelessNetworkView(CanvasPulsingIcon): + group = self._add_ciphers_from_flags(self._wpa_flags, False) + wireless_security = WirelessSecurity() + wireless_security.key_mgmt = 'wpa-psk' +- wireless_security.proto = 'wpa' ++ wireless_security.proto = ['wpa'] + wireless_security.pairwise = pairwise + wireless_security.group = group + return wireless_security + ++ if (self._rsn_flags & network.NM_802_11_AP_SEC_KEY_MGMT_802_1X) and \ ++ (self._device_caps & network.NM_802_11_DEVICE_CAP_RSN): ++ # WPA2 Enterprise ++ pairwise = self._add_ciphers_from_flags(self._rsn_flags, True) ++ group = self._add_ciphers_from_flags(self._rsn_flags, False) ++ wireless_security = WirelessSecurity() ++ wireless_security.key_mgmt = 'wpa-eap' ++ wireless_security.proto = ['rsn'] ++ wireless_security.pairwise = pairwise ++ wireless_security.group = group ++ return wireless_security ++ ++ if (self._wpa_flags & network.NM_802_11_AP_SEC_KEY_MGMT_802_1X) and \ ++ (self._device_caps & network.NM_802_11_DEVICE_CAP_WPA): ++ # WPA Enterprise ++ pairwise = self._add_ciphers_from_flags(self._wpa_flags, True) ++ group = self._add_ciphers_from_flags(self._wpa_flags, False) ++ wireless_security = WirelessSecurity() ++ wireless_security.key_mgmt = 'wpa-eap' ++ wireless_security.proto = ['wpa'] ++ wireless_security.pairwise = pairwise ++ wireless_security.group = group ++ return wireless_security ++ ++ def _enter_additional_settings_and_secrets_and_then_activate(self, ++ uuid, settings, wireless_security): ++ # this is valid, only for "ieee8021x" or "wpa-eap" key ++ # management (provided the network has ANY security at all) ++ if (wireless_security is not None) and \ ++ ( (wireless_security.key_mgmt == 'ieee8021x') or \ ++ (wireless_security.key_mgmt == 'wpa-eap') ): ++ keydialog.get_key_values(AUTHENTICATION_LIST, ++ self.__add_and_activate_connection, ++ uuid, settings) ++ else: ++ self.__add_and_activate_connection(uuid, settings) ++ ++ def __add_and_activate_connection(self, uuid, settings, ++ additional_settings=None): ++ ++ if additional_settings is not None: ++ key_value_dict = {} ++ auth_params_list = additional_settings._params_list ++ ++ for auth_param in auth_params_list: ++ key = auth_param._key_name ++ value = auth_param._value ++ logging.debug('key == %s', key) ++ logging.debug('value == %s', value) ++ if len(value) > 0: ++ key_value_dict[key] = value ++ else: ++ logging.debug('Not setting empty value for key :' ++ ' %s', key) ++ ++ settings.wpa_eap_setting = key_value_dict ++ ++ connection = network.add_connection(uuid, settings) ++ self._activate_connection(connection) ++ + def __connect_activate_cb(self, icon): + self._connect() + +@@ -350,8 +597,10 @@ class WirelessNetworkView(CanvasPulsingIcon): + connection = network.find_connection_by_ssid(self._name) + if connection is None: + settings = Settings() ++ self._settings = settings + settings.connection.id = 'Auto ' + self._name + uuid = settings.connection.uuid = unique_id() ++ self._uuid = uuid + settings.connection.type = '802-11-wireless' + settings.wireless.ssid = self._name + +@@ -370,8 +619,12 @@ class WirelessNetworkView(CanvasPulsingIcon): + if wireless_security is not None: + settings.wireless.security = '802-11-wireless-security' + +- connection = network.add_connection(uuid, settings) ++ self._enter_additional_settings_and_secrets_and_then_activate(uuid, ++ settings, wireless_security) ++ else: ++ self._activate_connection(connection) + ++ def _activate_connection(self, connection): + obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) + netmgr = dbus.Interface(obj, _NM_IFACE) + +diff --git a/src/jarabe/journal/objectchooser.py b/src/jarabe/journal/objectchooser.py +index ecb8ecf..59df14b 100644 +--- a/src/jarabe/journal/objectchooser.py ++++ b/src/jarabe/journal/objectchooser.py +@@ -20,9 +20,12 @@ import logging + import gobject + import gtk + import wnck ++import os + ++from sugar import env + from sugar.graphics import style + from sugar.graphics.toolbutton import ToolButton ++from sugar.datastore import datastore + + from jarabe.journal.listview import BaseListView + from jarabe.journal.listmodel import ListModel +@@ -46,6 +49,7 @@ class ObjectChooser(gtk.Window): + self.set_border_width(style.LINE_WIDTH) + + self._selected_object_id = None ++ self._callback = None + + self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + self.connect('visibility-notify-event', +@@ -109,6 +113,15 @@ class ObjectChooser(gtk.Window): + self._selected_object_id = uid + self.emit('response', gtk.RESPONSE_ACCEPT) + ++ if self._callback is not None: ++ self._callback(self._selected_object_id) ++ ++ def get_selected_object(self): ++ if self._selected_object_id is None: ++ return None ++ else: ++ return datastore.get(self._selected_object_id) ++ + def __delete_event_cb(self, chooser, event): + self.emit('response', gtk.RESPONSE_DELETE_EVENT) + +@@ -120,6 +133,9 @@ class ObjectChooser(gtk.Window): + def __close_button_clicked_cb(self, button): + self.emit('response', gtk.RESPONSE_DELETE_EVENT) + ++ if self._callback is not None: ++ self._callback(self._selected_object_id) ++ + def get_selected_object_id(self): + return self._selected_object_id + +@@ -135,6 +151,9 @@ class ObjectChooser(gtk.Window): + visible = event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED + self._list_view.set_is_visible(visible) + ++ def _set_callback(self, callback): ++ self._callback = callback ++ + + class TitleBox(VolumesToolbar): + __gtype_name__ = 'TitleBox' +diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py +index f265ae4..84e3666 100644 +--- a/src/jarabe/model/network.py ++++ b/src/jarabe/model/network.py +@@ -418,6 +418,7 @@ class Settings(object): + self.connection = Connection() + self.ip4_config = None + self.wireless_security = None ++ self.wpa_eap_setting = None + + if wireless_cfg is not None: + self.wireless = wireless_cfg +@@ -433,6 +434,10 @@ class Settings(object): + self.wireless_security.get_dict() + if self.ip4_config is not None: + settings['ipv4'] = self.ip4_config.get_dict() ++ if self.wpa_eap_setting is not None: ++ settings['802-1x'] = self.wpa_eap_setting ++ ++ + return settings + + +@@ -653,6 +658,9 @@ class NMSettingsConnection(dbus.service.Object): + if self._settings.wireless.security is not None: + config.set(identifier, 'security', + self._settings.wireless.security) ++ if self._settings.wpa_eap_setting is not None: ++ config.set(identifier, 'wpa_eap_setting', ++ self._settings.wpa_eap_setting) + if self._secrets is not None: + if self._settings.wireless_security.key_mgmt == 'none': + config.set(identifier, 'key', self._secrets.wep_key) +@@ -895,21 +903,29 @@ def load_wifi_connections(): + settings.wireless_security.key_mgmt = mgmt + security = config.get(section, 'security') + settings.wireless.security = security +- key = config.get(section, 'key') + if mgmt == 'none': ++ key = config.get(section, 'key') + secrets.wep_key = key + auth_alg = config.get(section, 'auth-alg') + secrets.auth_alg = auth_alg +- elif mgmt == 'wpa-psk': +- secrets.psk = key ++ elif (mgmt == 'wpa-psk') or (mgmt == 'wpa-eap'): ++ if mgmt == 'wpa-psk': ++ key = config.get(section, 'key') ++ secrets.psk = key ++ elif mgmt == 'wpa-eap': ++ if config.has_option(section, ++ 'wpa_eap_setting'): ++ value = eval(config.get(section, ++ 'wpa_eap_setting')) ++ settings.wpa_eap_setting = value + if config.has_option(section, 'proto'): +- value = config.get(section, 'proto') ++ value = eval(config.get(section, 'proto')) + settings.wireless_security.proto = value + if config.has_option(section, 'group'): +- value = config.get(section, 'group') ++ value = eval(config.get(section, 'group')) + settings.wireless_security.group = value + if config.has_option(section, 'pairwise'): +- value = config.get(section, 'pairwise') ++ value = eval(config.get(section, 'pairwise')) + settings.wireless_security.pairwise = value + except ConfigParser.Error: + logging.exception('Error reading section') +-- +1.7.4.4 + diff --git a/rpms/sugar/0070-Connecting-to-Hidden-Wireless-Networks.patch b/rpms/sugar/0070-Connecting-to-Hidden-Wireless-Networks.patch new file mode 100644 index 0000000..82ced1d --- /dev/null +++ b/rpms/sugar/0070-Connecting-to-Hidden-Wireless-Networks.patch @@ -0,0 +1,311 @@ +From cab555f7f972fa9f73661c504eb44d6b6c7e3168 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Thu, 15 Dec 2011 14:30:49 +0530 +Subject: [PATCH 70/82] Connecting to Hidden-Wireless-Networks. +Organization: Sugar Labs Foundation + +Signed-off-by: Ajay Garg <ajay@activitycentral.com> +--- + extensions/cpsection/network/model.py | 48 +++++++++ + extensions/cpsection/network/view.py | 182 +++++++++++++++++++++++++++++++++ + 2 files changed, 230 insertions(+), 0 deletions(-) + +diff --git a/extensions/cpsection/network/model.py b/extensions/cpsection/network/model.py +index 916ce8c..9592fdd 100644 +--- a/extensions/cpsection/network/model.py ++++ b/extensions/cpsection/network/model.py +@@ -18,6 +18,9 @@ + import dbus + from gettext import gettext as _ + import gconf ++import os ++import subprocess ++import logging + + from jarabe.model import network + +@@ -27,7 +30,9 @@ _NM_PATH = '/org/freedesktop/NetworkManager' + _NM_IFACE = 'org.freedesktop.NetworkManager' + + KEYWORDS = ['network', 'jabber', 'radio', 'server'] ++HIDDEN_SSID_FILE = os.path.expanduser('~/.sugar/default/nm/hidden_ssid') + ++_logger = logging.getLogger('ControlPanel - Network') + + class ReadError(Exception): + def __init__(self, value): +@@ -142,3 +147,46 @@ def set_publish_information(value): + client = gconf.client_get_default() + client.set_bool('/desktop/sugar/collaboration/publish_gadget', value) + return 0 ++ ++ ++def is_hidden_network_connect_package_available(): ++ if os.path.exists('/etc/init.d/z-sugar-hidden-network-connect'): ++ return True ++ return False ++ ++ ++def get_ssids(): ++ ssids = [] ++ ++ # If the file does not exist, return. ++ if not os.path.exists(HIDDEN_SSID_FILE): ++ return ssids ++ ++ f = open(HIDDEN_SSID_FILE, 'r') ++ for ssid in f.readlines(): ++ ssids.append(ssid.rstrip('\n')) ++ f.close() ++ ++ return ssids ++ ++ ++def set_ssids(ssids): ++ ++ # First remove the old ssid-file, if it exists. ++ if os.path.exists(HIDDEN_SSID_FILE): ++ try: ++ os.remove(HIDDEN_SSID_FILE) ++ except: ++ _logger.exception('Error removing file.') ++ return ++ ++ # Do nothing and return, if the values-list is empty ++ if len(ssids) == 0: ++ return ++ ++ # If we reach here, we have a non-empty ssid-values-list. ++ f = open(HIDDEN_SSID_FILE, 'w') ++ for ssid in ssids: ++ if len(ssid) > 0: ++ f.write(ssid + '\n') ++ f.close() +diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py +index 381dcb6..9a2444a 100644 +--- a/extensions/cpsection/network/view.py ++++ b/extensions/cpsection/network/view.py +@@ -19,6 +19,8 @@ import gobject + from gettext import gettext as _ + + from sugar.graphics import style ++from sugar.graphics.alert import Alert ++from sugar.graphics.icon import Icon + + from jarabe.controlpanel.sectionview import SectionView + from jarabe.controlpanel.inlinealert import InlineAlert +@@ -30,6 +32,123 @@ TITLE = _('Network') + + _APPLY_TIMEOUT = 3000 + ++class AddRemoveWidget(gtk.HBox): ++ ++ def __init__(self, label, add_button_clicked_cb, ++ remove_button_clicked_cb, index): ++ gtk.Box.__init__(self) ++ self.set_homogeneous(False) ++ self.set_spacing(10) ++ ++ self._index = index ++ self._add_button_added = False ++ self._remove_button_added = False ++ ++ self._entry_box = gtk.Entry() ++ self._entry_box.set_text(label) ++ self.pack_start(self._entry_box, expand=False) ++ self._entry_box.show() ++ ++ add_icon = Icon(icon_name='list-add') ++ self._add_button = gtk.Button() ++ self._add_button.set_image(add_icon) ++ self._add_button.connect('clicked', ++ add_button_clicked_cb, ++ self) ++ ++ remove_icon = Icon(icon_name='list-remove') ++ self._remove_button = gtk.Button() ++ self._remove_button.set_image(remove_icon) ++ self._remove_button.connect('clicked', ++ remove_button_clicked_cb, ++ self) ++ ++ self.__add_add_button() ++ self.__add_remove_button() ++ ++ def _get_index(self): ++ return self._index ++ ++ def _set_index(self, value): ++ self._index = value ++ ++ def _get_entry(self): ++ return self._entry_box.get_text() ++ ++ def __add_add_button(self): ++ self.pack_start(self._add_button, expand=False) ++ self._add_button.show() ++ self._add_button_added = True ++ ++ def _remove_remove_button_if_not_already(self): ++ if self._remove_button_added: ++ self.__remove_remove_button() ++ ++ def __remove_remove_button(self): ++ self.remove(self._remove_button) ++ self._remove_button_added = False ++ ++ def _add_remove_button_if_not_already(self): ++ if not self._remove_button_added: ++ self.__add_remove_button() ++ ++ def __add_remove_button(self): ++ self.pack_start(self._remove_button, expand=False) ++ self._remove_button.show() ++ self._remove_button_added = True ++ ++ ++class MultiWidget(gtk.VBox): ++ ++ def __init__(self): ++ gtk.VBox.__init__(self) ++ ++ def _add_widget(self, label): ++ new_widget = AddRemoveWidget(label, ++ self.__add_button_clicked_cb, ++ self.__remove_button_clicked_cb, ++ len(self.get_children())) ++ self.add(new_widget) ++ new_widget.show() ++ self.show() ++ self._update_remove_button_statuses() ++ ++ def __add_button_clicked_cb(self, add_button, ++ add_button_container): ++ self._add_widget('') ++ self._update_remove_button_statuses() ++ ++ def __remove_button_clicked_cb(self, remove_button, ++ remove_button_container): ++ for child in self.get_children(): ++ if child._get_index() > remove_button_container._get_index(): ++ child._set_index(child._get_index() - 1) ++ ++ self.remove(remove_button_container) ++ self._update_remove_button_statuses() ++ ++ def _update_remove_button_statuses(self): ++ children = self.get_children() ++ ++ # Now, if there is only one entry, remove-button ++ # should not be shown. ++ if len(children) == 1: ++ children[0]._remove_remove_button_if_not_already() ++ ++ # Alternatively, if there are more than 1 entries, ++ # remove-button should be shown for all. ++ if len(children) > 1: ++ for child in children: ++ child._add_remove_button_if_not_already() ++ ++ ++ def _get_entries(self): ++ entries = [] ++ for child in self.get_children(): ++ entries.append(child._get_entry()) ++ ++ return entries ++ + + class Network(SectionView): + def __init__(self, model, alerts): +@@ -50,6 +169,7 @@ class Network(SectionView): + + self._radio_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._jabber_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) ++ self._hidden_network_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + + workspace = gtk.VBox() + workspace.show() +@@ -171,6 +291,55 @@ class Network(SectionView): + workspace.pack_start(box_mesh, expand=False) + box_mesh.show() + ++ if self._model.is_hidden_network_connect_package_available(): ++ separator_hidden_network = gtk.HSeparator() ++ workspace.pack_start(separator_hidden_network, False) ++ separator_hidden_network.show() ++ ++ label_hidden_network = gtk.Label(_('Hidden Networks')) ++ label_hidden_network.set_alignment(0, 0) ++ workspace.pack_start(label_hidden_network, expand=False) ++ label_hidden_network.show() ++ box_hidden_network = gtk.VBox() ++ box_hidden_network.set_border_width(style.DEFAULT_SPACING * 2) ++ box_hidden_network.set_spacing(style.DEFAULT_SPACING) ++ ++ info = gtk.Label(_("Enter the SSIDs of the hidden networks.")) ++ info.set_alignment(0, 0) ++ info.set_line_wrap(True) ++ box_hidden_network.pack_start(info, expand=False) ++ info.show() ++ ++ self._hidden_network_alert = InlineAlert() ++ self.hidden_network_msg = 'XO-reboot is required for the' \ ++ ' changes to take effect.' ++ self._hidden_network_alert.props.msg =_(self.hidden_network_msg) ++ self._hidden_network_alert_box.pack_start(self._hidden_network_alert, ++ expand=False) ++ box_hidden_network.pack_end(self._hidden_network_alert_box, ++ expand=False) ++ self._hidden_network_alert_box.show() ++ self._hidden_network_alert.show() ++ ++ self._widget_table = MultiWidget() ++ box_hidden_network.pack_start(self._widget_table, expand=False) ++ self._widget_table.show() ++ ++ save_button = gtk.Button() ++ save_button.set_alignment(0, 0) ++ save_button.set_label('Save') ++ save_button.connect('clicked', self.__save_button_clicked_cb) ++ box_save_button = gtk.HBox() ++ box_save_button.set_homogeneous(False) ++ box_save_button.pack_start(save_button, expand=False) ++ save_button.show() ++ ++ box_hidden_network.pack_start(box_save_button, expand=False) ++ box_save_button.show() ++ ++ workspace.pack_start(box_hidden_network, expand=False) ++ box_hidden_network.show() ++ + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled.add_with_viewport(workspace) +@@ -200,6 +369,15 @@ class Network(SectionView): + self._clear_history_button.connect( \ + 'clicked', self.__network_configuration_reset_cb) + ++ if self._model.is_hidden_network_connect_package_available(): ++ ++ ssids = self._model.get_ssids() ++ if len(ssids) == 0: ++ self._widget_table._add_widget('') ++ else: ++ for ssid in ssids: ++ self._widget_table._add_widget(ssid) ++ + def undo(self): + self._button.disconnect(self._radio_change_handler) + self._entry.disconnect(self._jabber_change_handler) +@@ -259,3 +437,7 @@ class Network(SectionView): + self._model.clear_networks() + if not self._model.have_networks(): + self._clear_history_button.set_sensitive(False) ++ ++ def __save_button_clicked_cb(self, save_button): ++ self._model.set_ssids(self._widget_table._get_entries()) ++ save_button.set_sensitive(False) +-- +1.7.4.4 + diff --git a/rpms/sugar/0071-Remove-AC-microformat-updater-files.patch b/rpms/sugar/0071-Remove-AC-microformat-updater-files.patch new file mode 100644 index 0000000..3880624 --- /dev/null +++ b/rpms/sugar/0071-Remove-AC-microformat-updater-files.patch @@ -0,0 +1,1155 @@ +From e62a641fe16402d83cfefbae15955043eb35e095 Mon Sep 17 00:00:00 2001 +From: Anish Mangal <anish@activitycentral.com> +Date: Thu, 2 Feb 2012 16:05:03 -0200 +Subject: [PATCH 71/82] Remove AC microformat updater files +Organization: Sugar Labs Foundation + +Dextrose now uses the out-of-tree microformat updater from OLPC. +--- + data/icons/Makefile.am | 3 +- + data/icons/module-updater.svg | 16 - + extensions/cpsection/Makefile.am | 1 - + extensions/cpsection/updater/Makefile.am | 8 - + extensions/cpsection/updater/__init__.py | 22 -- + extensions/cpsection/updater/backends/Makefile.am | 5 - + extensions/cpsection/updater/backends/__init__.py | 16 - + .../cpsection/updater/backends/microformat.py | 203 ---------- + extensions/cpsection/updater/model.py | 383 ------------------- + extensions/cpsection/updater/view.py | 398 -------------------- + 10 files changed, 1 insertions(+), 1054 deletions(-) + delete mode 100644 data/icons/module-updater.svg + delete mode 100644 extensions/cpsection/updater/Makefile.am + delete mode 100644 extensions/cpsection/updater/__init__.py + delete mode 100644 extensions/cpsection/updater/backends/Makefile.am + delete mode 100644 extensions/cpsection/updater/backends/__init__.py + delete mode 100644 extensions/cpsection/updater/backends/microformat.py + delete mode 100755 extensions/cpsection/updater/model.py + delete mode 100644 extensions/cpsection/updater/view.py + +diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am +index 2497c4a..cc6a134 100644 +--- a/data/icons/Makefile.am ++++ b/data/icons/Makefile.am +@@ -10,7 +10,6 @@ sugar_DATA = \ + module-language.svg \ + module-modemconfiguration.svg \ + module-network.svg \ +- module-power.svg \ +- module-updater.svg ++ module-power.svg + + EXTRA_DIST = $(sugar_DATA) +diff --git a/data/icons/module-updater.svg b/data/icons/module-updater.svg +deleted file mode 100644 +index a521f61..0000000 +--- a/data/icons/module-updater.svg ++++ /dev/null +@@ -1,16 +0,0 @@ +-<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ +- <!ENTITY stroke_color "#000"> +- <!ENTITY fill_color "#fff"> +-]><svg height="55px" viewBox="0 0 55 55" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +- <g> +- <g> +- <path d="M 31.752 7.088 C 41.935 9.118 49.609 18.107 49.609 28.887 C 49.609 41.173 39.65 51.129 27.374 51.129 C 15.086 51.129 5.133 41.173 5.133 28.887 C 5.133 19.648 10.768 11.723 18.801 8.365 " fill="none" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" /> +- <path d="M 36.134 15.154 L 31.752 7.088 L 40.439 4.13 " fill="none" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" /> +- </g> +- <g> +- <g> +- <path d="M 38.57 25.886 C 37.597 25.886 36.718 26.282 36.082 26.918 L 31.021 31.979 L 31.02 17.022 C 31.018 16.124 30.675 15.221 29.99 14.533 C 28.613 13.159 26.383 13.159 25.01 14.533 C 24.321 15.222 23.98 16.122 23.979 17.023 L 23.977 31.978 L 18.918 26.918 C 18.281 26.281 17.4 25.886 16.429 25.887 C 14.484 25.885 12.908 27.465 12.908 29.408 C 12.907 30.381 13.304 31.263 13.936 31.899 L 27.5 45.463 L 41.062 31.898 C 41.697 31.262 42.09 30.382 42.093 29.41 C 42.094 27.463 40.516 25.885 38.57 25.886 Z " fill="&fill_color;" stroke="none" /> +- </g> +- </g> +- </g> +-</svg> +diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am +index d623380..1cbf8ea 100644 +--- a/extensions/cpsection/Makefile.am ++++ b/extensions/cpsection/Makefile.am +@@ -9,7 +9,6 @@ SUBDIRS = \ + modemconfiguration \ + network \ + power \ +- updater \ + # + + sugardir = $(pkgdatadir)/extensions/cpsection +diff --git a/extensions/cpsection/updater/Makefile.am b/extensions/cpsection/updater/Makefile.am +deleted file mode 100644 +index 897dbf3..0000000 +--- a/extensions/cpsection/updater/Makefile.am ++++ /dev/null +@@ -1,8 +0,0 @@ +-SUBDIRS = backends +- +-sugardir = $(pkgdatadir)/extensions/cpsection/updater +- +-sugar_PYTHON = \ +- __init__.py \ +- model.py \ +- view.py +diff --git a/extensions/cpsection/updater/__init__.py b/extensions/cpsection/updater/__init__.py +deleted file mode 100644 +index 6010615..0000000 +--- a/extensions/cpsection/updater/__init__.py ++++ /dev/null +@@ -1,22 +0,0 @@ +-# Copyright (C) 2008, OLPC +-# +-# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +- +-from gettext import gettext as _ +- +-CLASS = 'ActivityUpdater' +-ICON = 'module-updater' +-TITLE = _('Software update') +-KEYWORDS = ['software', 'activity', 'update'] +diff --git a/extensions/cpsection/updater/backends/Makefile.am b/extensions/cpsection/updater/backends/Makefile.am +deleted file mode 100644 +index e9c1284..0000000 +--- a/extensions/cpsection/updater/backends/Makefile.am ++++ /dev/null +@@ -1,5 +0,0 @@ +-sugardir = $(pkgdatadir)/extensions/cpsection/updater/backends +- +-sugar_PYTHON = \ +- microformat.py \ +- __init__.py +diff --git a/extensions/cpsection/updater/backends/__init__.py b/extensions/cpsection/updater/backends/__init__.py +deleted file mode 100644 +index 0dd0174..0000000 +--- a/extensions/cpsection/updater/backends/__init__.py ++++ /dev/null +@@ -1,16 +0,0 @@ +-#!/usr/bin/python +-# Copyright (C) 2009, Sugar Labs +-# +-# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +diff --git a/extensions/cpsection/updater/backends/microformat.py b/extensions/cpsection/updater/backends/microformat.py +deleted file mode 100644 +index ca4f1d5..0000000 +--- a/extensions/cpsection/updater/backends/microformat.py ++++ /dev/null +@@ -1,203 +0,0 @@ +-#!/usr/bin/python +-# +-# Copyright (C) 2011, Anish Mangal <anish@sugarlabs.org> +-# +-# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +- +-import logging +-from HTMLParser import HTMLParser +-import urllib +-import re +- +-import gio +-import gobject +-import gconf +- +-from jarabe import config +- +-client = gconf.client_get_default() +-_UPDATE_PATH = client.get_string('/desktop/sugar/updater_url') +-_ACTIVITIES_LIST = {} +-ACTION_CHECKING = 0 +-ACTION_UPDATING = 1 +-ACTION_DOWNLOADING = 2 +- +-class MicroformatParser(HTMLParser): +- +- def __init__(self, data, completion_cb): +- HTMLParser.__init__(self) +- self.reset() +- self._data_to_parse = data +- self._activity_id = '' +- self._activity_url = '' +- self._activity_version = '' +- self._activity_size = 1 +- self._activity_name = '' +- self._inside_activity_block = False +- self._inside_activity_version = False +- self._inside_activity_id = False +- self._inside_activity_url = False +- self._inside_activity_size = False +- self._inside_activity_name = False +- self._activity_block_tag = '' +- self._completion_cb = completion_cb +- +- def parse(self): +- self.feed(self._data_to_parse) +- +- def handle_endtag(self, tag): +- if tag == self._activity_block_tag and self._inside_activity_block: +- self._inside_activity_block = False +- +- _ACTIVITIES_LIST[self._activity_id] = \ +- {'version':self._activity_version, +- 'url':self._activity_url, +- 'size':self._activity_size, +- 'name':self._activity_name} +- +- elif tag == 'a': +- if self._inside_activity_url: +- self._inside_activity_url = False +- +- elif tag == 'body': +- num_bundles = len(_ACTIVITIES_LIST) +- progress = num_bundles +- for bundle, info in _ACTIVITIES_LIST.items(): +- progress = progress + 1 +- if _ACTIVITIES_LIST[bundle]['size'] == 1: +- try: +- _ACTIVITIES_LIST[bundle]['size'] = \ +- gio.File(_ACTIVITIES_LIST[bundle]['url']).\ +- query_info('*').get_size() +- +- except Exception, e: +- logging.exception(e) +- +- if _ACTIVITIES_LIST[bundle]['size'] == 0: +- logging.error('Size of activity %s reported as ' +- '0 bytes. Excluding from update list' % bundle) +- del _ACTIVITIES_LIST[bundle] +- +- elif _ACTIVITIES_LIST[bundle]['name'] == '': +- # Do some regex magic to get the 'probable' +- # activity name. +- activity_name = re.split('\.', +- bundle)[-1] +- activity_name = re.sub('^[\s|\t]*', '', +- activity_name) +- activity_name = re.sub('[\s|\t]*$', '', +- activity_name) +- activity_name = re.sub('[A|a]ctivity$', '', +- activity_name) +- _ACTIVITIES_LIST[bundle]['name'] = \ +- activity_name +- +- self._completion_cb(_ACTIVITIES_LIST, None) +- +- def handle_starttag(self, tag, attrs): +- for attribute, value in attrs: +- if value == 'olpc-activity-info': +- self._inside_activity_block = True +- self._activity_block_tag = tag +- +- if tag == 'span': +- for attribute, value in attrs: +- if value == 'olpc-activity-id': +- self._inside_activity_id = True +- elif value == 'olpc-activity-version': +- self._inside_activity_version = True +- elif value == 'olpc-activity-name': +- self._inside_activity_name = True +- elif value == 'olpc-activity-size': +- self._inside_activity_size = True +- elif value == 'olpc-activity-url': +- self._inside_activity_url = True +- +- elif tag == 'a': +- if self._inside_activity_url: +- for attribute, value in attrs: +- if attribute == 'href': +- self._activity_url = value +- +- def handle_data(self, data): +- if self._inside_activity_version: +- self._activity_version = str(data) +- self._inside_activity_version = False +- +- elif self._inside_activity_id: +- self._activity_id = data +- self._inside_activity_id = False +- +- elif self._inside_activity_name: +- self._activity_name = data +- self._inside_activity_name = False +- +- elif self._inside_activity_size: +- self._activity_size = int(data) +- self._inside_activity_size = False +- +-class _UpdateFetcher(gobject.GObject): +- +- __gsignals__ = { +- 'progress': (gobject.SIGNAL_RUN_FIRST, +- gobject.TYPE_NONE, +- ([int, str, float, int])), +- } +- +- def __init__(self, completion_cb): +- gobject.GObject.__init__(self) +- # ASLO knows only about stable SP releases +- major, minor = config.version.split('.')[0:2] +- sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2) +- self._data = '' +- self._completion_cb = completion_cb +- +- def download_bundle_updates(self): +- self.emit('progress', ACTION_CHECKING, 'Fetching update ' +- 'information', 1, 3) +- self._url = _UPDATE_PATH +- self._file = gio.File(self._url) +- logging.debug('Fetch %s', self._url) +- self._file.read_async(self.__read_async_cb) +- +- def __read_async_cb(self, gfile, result): +- try: +- stream = gfile.read_finish(result) +- except gio.Error, e: +- self.stop() +- logging.exception('Error while fetching content from %s' % +- self._url) +- return +- stream.read_async(4096, self.__stream_read_cb) +- +- def __stream_read_cb(self, stream, result): +- data = stream.read_finish(result) +- if not data: +- self._data_finished() +- return +- self._data_read(data) +- stream.read_async(4096, self.__stream_read_cb) +- +- def _data_read(self, data): +- self._data += data +- +- def read_finish(self): +- pass +- +- def _data_finished(self): +- self.emit('progress', ACTION_CHECKING, 'Fetching update ' +- 'information', 2, 3) +- parser = MicroformatParser(self._data, self._completion_cb) +- gobject.idle_add(parser.parse) +diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py +deleted file mode 100755 +index 1a3941c..0000000 +--- a/extensions/cpsection/updater/model.py ++++ /dev/null +@@ -1,383 +0,0 @@ +-# Copyright (C) 2009, Sugar Labs +-# Copyright (C) 2009, Tomeu Vizoso +-# +-# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +-"""Sugar bundle updater: model. +- +-This module implements the non-GUI portions of the bundle updater, including +-list of installed bundls, whether updates are needed, and the URL at which to +-find the bundle updated. +-""" +- +-import os +-import logging +-import tempfile +-from urlparse import urlparse +-import traceback +- +-import gobject +-import gio +- +-from sugar import env +-from sugar.datastore import datastore +-from sugar.bundle.activitybundle import ActivityBundle +-from sugar.bundle.bundleversion import NormalizedVersion +- +-from jarabe.model import bundleregistry +- +-from backends import microformat +- +-class MetaBundle(): +- +- def __init__(self, bundle_id, version, name): +- self._bundle_id = bundle_id +- self._version = version +- self._name = name +- +- def get_name(self): +- return self._name +- +- def get_bundle_id(self): +- return self._bundle_id +- +- def get_icon(self): +- pass +- +- def get_activity_version(self): +- return self._version +- +-class UpdateModel(gobject.GObject): +- __gtype_name__ = 'SugarUpdateModel' +- +- __gsignals__ = { +- 'progress': (gobject.SIGNAL_RUN_FIRST, +- gobject.TYPE_NONE, +- ([int, str, float, int])), +- } +- +- ACTION_CHECKING = 0 +- ACTION_UPDATING = 1 +- ACTION_DOWNLOADING = 2 +- +- def __init__(self): +- gobject.GObject.__init__(self) +- +- self.updates = None +- self._bundles_to_check = None +- self._bundles_to_update = None +- self._total_bundles_to_update = 0 +- self._downloader = None +- self._cancelling = False +- +- def __progress_cb(self, model, action, description, current, total): +- self.emit('progress', action, description, current, total) +- +- def check_updates(self): +- self.updates = [] +- self._current_bundles = {} +- for bundle in bundleregistry.get_registry(): +- self._current_bundles[bundle.get_bundle_id()] =\ +- {'version':bundle.get_activity_version(), +- 'bundle': bundle} +- self._bundles_to_check = list(bundleregistry.get_registry()) +- self._fetcher = microformat._UpdateFetcher(self.__bundle_info_fetched_cb) +- self._fetcher.connect('progress', self.__progress_cb) +- gobject.idle_add(self._fetcher.download_bundle_updates) +- +- def __bundle_info_fetched_cb(self, new_bundles, error_message): +- if error_message is not None: +- logging.error('Error getting update information from server:\n' +- '%s' % error_message) +- +- if self._cancelling: +- self._cancel_checking() +- else: +- for bundle_id, info in new_bundles.items(): +- if bundle_id in self._current_bundles: +- old_version = self._current_bundles[bundle_id]['version'] +- new_version = new_bundles[bundle_id]['version'] +- if NormalizedVersion(old_version) < \ +- NormalizedVersion(new_version): +- self.updates.append(BundleUpdate( +- self._current_bundles[bundle_id]['bundle'], +- new_bundles[bundle_id]['version'], +- new_bundles[bundle_id]['url'], +- new_bundles[bundle_id]['size'], +- 'update')) +- else: +- bundle = MetaBundle(bundle_id, +- new_bundles[bundle_id]['version'], +- new_bundles[bundle_id]['name']) +- self.updates.append(BundleUpdate(bundle, +- new_bundles[bundle_id]['version'], +- new_bundles[bundle_id]['url'], +- new_bundles[bundle_id]['size'], +- 'new')) +- +- self.emit('progress', UpdateModel.ACTION_CHECKING, 'Fetching update ' +- 'information', 3, 3) +- +- def update(self, bundle_ids): +- self._bundles_to_update = [] +- for bundle_update in self.updates: +- if bundle_update.bundle.get_bundle_id() in bundle_ids: +- self._bundles_to_update.append(bundle_update) +- +- self._total_bundles_to_update = len(self._bundles_to_update) +- self._download_next_update() +- +- def _download_next_update(self): +- if self._cancelling: +- self._cancel_updating() +- return +- +- bundle_update = self._bundles_to_update.pop() +- +- total = self._total_bundles_to_update * 2 +- current = total - len(self._bundles_to_update) * 2 - 2 +- +- self.emit('progress', UpdateModel.ACTION_DOWNLOADING, +- bundle_update.bundle.get_name(), current, total) +- +- self._downloader = _Downloader(bundle_update) +- self._downloader.connect('progress', self.__downloader_progress_cb) +- self._downloader.connect('error', self.__downloader_error_cb) +- +- def __downloader_progress_cb(self, downloader, progress): +- logging.debug('__downloader_progress_cb %r', progress) +- +- if self._cancelling: +- self._cancel_updating() +- return +- +- total = self._total_bundles_to_update * 2 +- current = total - len(self._bundles_to_update) * 2 - 2 + progress +- +- self.emit('progress', UpdateModel.ACTION_DOWNLOADING, +- self._downloader.bundle_update.bundle.get_name(), +- current, total) +- +- if progress == 1: +- self._install_update(self._downloader.bundle_update, +- self._downloader.get_local_file_path()) +- self._downloader = None +- +- def __downloader_error_cb(self, downloader, error_message): +- logging.error('Error downloading update:\n%s', error_message) +- +- if self._cancelling: +- self._cancel_updating() +- return +- +- total = self._total_bundles_to_update +- current = total - len(self._bundles_to_update) +- self.emit('progress', UpdateModel.ACTION_UPDATING, '', current, total) +- +- if self._bundles_to_update: +- # do it in idle so the UI has a chance to refresh +- gobject.idle_add(self._download_next_update) +- +- def _install_update(self, bundle_update, local_file_path): +- +- total = self._total_bundles_to_update +- current = total - len(self._bundles_to_update) - 0.5 +- +- self.emit('progress', UpdateModel.ACTION_UPDATING, +- bundle_update.bundle.get_name(), +- current, total) +- +- # TODO: Should we first expand the zip async so we can provide progress +- # and only then copy to the journal? +- jobject = datastore.create() +- try: +- title = '%s-%s' % (bundle_update.bundle.get_name(), +- bundle_update.version) +- jobject.metadata['title'] = title +- jobject.metadata['mime_type'] = ActivityBundle.MIME_TYPE +- jobject.file_path = local_file_path +- datastore.write(jobject, transfer_ownership=True) +- finally: +- jobject.destroy() +- +- self.emit('progress', UpdateModel.ACTION_UPDATING, +- bundle_update.bundle.get_name(), +- current + 0.5, total) +- +- if self._bundles_to_update: +- # do it in idle so the UI has a chance to refresh +- gobject.idle_add(self._download_next_update) +- +- def cancel(self): +- self._cancelling = True +- +- def _cancel_checking(self): +- logging.debug('UpdateModel._cancel_checking') +- total = len(bundleregistry.get_registry()) +- current = total - len(self._bundles_to_check) +- self.emit('progress', UpdateModel.ACTION_CHECKING, '', current, +- current) +- self._bundles_to_check = None +- self._cancelling = False +- +- def _cancel_updating(self): +- logging.debug('UpdateModel._cancel_updating') +- current = (self._total_bundles_to_update - +- len(self._bundles_to_update) - 1) +- self.emit('progress', UpdateModel.ACTION_UPDATING, '', current, +- current) +- +- if self._downloader is not None: +- self._downloader.cancel() +- file_path = self._downloader.get_local_file_path() +- if file_path is not None and os.path.exists(file_path): +- os.unlink(file_path) +- self._downloader = None +- +- self._total_bundles_to_update = 0 +- self._bundles_to_update = None +- self._cancelling = False +- +- +-class BundleUpdate(object): +- +- def __init__(self, bundle, version, link, size, package_type = None): +- self.bundle = bundle +- self.version = version +- self.link = link +- self.size = size +- +- # Specify whether installing a new bundle or updating an +- # existing one +- self.package_type = package_type +- +-class _Downloader(gobject.GObject): +- _CHUNK_SIZE = 10240 # 10K +- __gsignals__ = { +- 'progress': (gobject.SIGNAL_RUN_FIRST, +- gobject.TYPE_NONE, +- ([float])), +- 'error': (gobject.SIGNAL_RUN_FIRST, +- gobject.TYPE_NONE, +- ([str])), +- } +- +- def __init__(self, bundle_update): +- gobject.GObject.__init__(self) +- +- self.bundle_update = bundle_update +- self._input_stream = None +- self._output_stream = None +- self._pending_buffers = [] +- self._input_file = gio.File(bundle_update.link) +- self._output_file = None +- self._downloaded_size = 0 +- self._cancelling = False +- +- self._input_file.read_async(self.__file_read_async_cb) +- +- def cancel(self): +- self._cancelling = True +- +- def __file_read_async_cb(self, gfile, result): +- if self._cancelling: +- return +- +- try: +- self._input_stream = self._input_file.read_finish(result) +- except: +- self.emit('error', traceback.format_exc()) +- return +- +- temp_file_path = self._get_temp_file_path(self.bundle_update.link) +- self._output_file = gio.File(temp_file_path) +- self._output_stream = self._output_file.create() +- +- self._input_stream.read_async(self._CHUNK_SIZE, self.__read_async_cb, +- gobject.PRIORITY_LOW) +- +- def __read_async_cb(self, input_stream, result): +- if self._cancelling: +- return +- +- data = input_stream.read_finish(result) +- +- if data is None: +- # TODO +- pass +- elif not data: +- logging.debug('closing input stream') +- self._input_stream.close() +- self._check_if_finished_writing() +- else: +- self._pending_buffers.append(data) +- self._input_stream.read_async(self._CHUNK_SIZE, +- self.__read_async_cb, +- gobject.PRIORITY_LOW) +- +- self._write_next_buffer() +- +- def __write_async_cb(self, output_stream, result, user_data): +- if self._cancelling: +- return +- +- count = output_stream.write_finish(result) +- +- self._downloaded_size += count +- progress = self._downloaded_size / float(self.bundle_update.size) +- self.emit('progress', progress) +- +- self._check_if_finished_writing() +- +- if self._pending_buffers: +- self._write_next_buffer() +- +- def _write_next_buffer(self): +- if self._pending_buffers and not self._output_stream.has_pending(): +- data = self._pending_buffers.pop(0) +- # TODO: we pass the buffer as user_data because of +- # http://bugzilla.gnome.org/show_bug.cgi?id=564102 +- self._output_stream.write_async(data, self.__write_async_cb, +- gobject.PRIORITY_LOW, +- user_data=data) +- +- def _get_temp_file_path(self, uri): +- # TODO: Should we use the HTTP headers for the file name? +- scheme_, netloc_, path, params_, query_, fragment_ = \ +- urlparse(uri) +- path = os.path.basename(path) +- +- if not os.path.exists(env.get_user_activities_path()): +- os.makedirs(env.get_user_activities_path()) +- +- base_name, extension_ = os.path.splitext(path) +- fd, file_path = tempfile.mkstemp(dir=env.get_user_activities_path(), +- prefix=base_name, suffix='.xo') +- os.close(fd) +- os.unlink(file_path) +- +- return file_path +- +- def get_local_file_path(self): +- return self._output_file.get_path() +- +- def _check_if_finished_writing(self): +- if not self._pending_buffers and \ +- not self._output_stream.has_pending() and \ +- self._input_stream.is_closed(): +- +- logging.debug('closing output stream') +- self._output_stream.close() +- +- self.emit('progress', 1.0) +diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater/view.py +deleted file mode 100644 +index 891f552..0000000 +--- a/extensions/cpsection/updater/view.py ++++ /dev/null +@@ -1,398 +0,0 @@ +-# Copyright (C) 2008, One Laptop Per Child +-# Copyright (C) 2009, Tomeu Vizoso +-# +-# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +- +-from gettext import gettext as _ +-from gettext import ngettext +-import locale +-import logging +- +-import gobject +-import gtk +- +-from sugar.graphics import style +-from sugar.graphics.icon import Icon, CellRendererIcon +- +-from jarabe.controlpanel.sectionview import SectionView +- +-from model import UpdateModel +- +-_DEBUG_VIEW_ALL = True +- +- +-class ActivityUpdater(SectionView): +- +- def __init__(self, model, alerts): +- SectionView.__init__(self) +- +- self._model = UpdateModel() +- self._model.connect('progress', self.__progress_cb) +- +- self.set_spacing(style.DEFAULT_SPACING) +- self.set_border_width(style.DEFAULT_SPACING * 2) +- +- self._top_label = gtk.Label() +- self._top_label.set_line_wrap(True) +- self._top_label.set_justify(gtk.JUSTIFY_LEFT) +- self._top_label.props.xalign = 0 +- self.pack_start(self._top_label, expand=False) +- self._top_label.show() +- +- separator = gtk.HSeparator() +- self.pack_start(separator, expand=False) +- separator.show() +- +- bottom_label = gtk.Label() +- bottom_label.set_line_wrap(True) +- bottom_label.set_justify(gtk.JUSTIFY_LEFT) +- bottom_label.props.xalign = 0 +- bottom_label.set_markup( +- _('Software updates correct errors, eliminate security ' \ +- 'vulnerabilities, and provide new features.')) +- self.pack_start(bottom_label, expand=False) +- bottom_label.show() +- +- self._update_box = None +- self._progress_pane = None +- +- self._refresh() +- +- def _switch_to_update_box(self): +- if self._update_box in self.get_children(): +- return +- +- if self._progress_pane in self.get_children(): +- self.remove(self._progress_pane) +- self._progress_pane = None +- +- if self._update_box is None: +- self._update_box = UpdateBox(self._model) +- self._update_box.refresh_button.connect('clicked', +- self.__refresh_button_clicked_cb) +- self._update_box.install_button.connect('clicked', +- self.__install_button_clicked_cb) +- +- self.pack_start(self._update_box, expand=True, fill=True) +- self._update_box.show() +- +- def _switch_to_progress_pane(self): +- if self._progress_pane in self.get_children(): +- return +- +- if self._update_box in self.get_children(): +- self.remove(self._update_box) +- self._update_box = None +- +- if self._progress_pane is None: +- self._progress_pane = ProgressPane() +- self._progress_pane.cancel_button.connect('clicked', +- self.__cancel_button_clicked_cb) +- +- self.pack_start(self._progress_pane, expand=True, fill=False) +- self._progress_pane.show() +- +- def _clear_center(self): +- if self._progress_pane in self.get_children(): +- self.remove(self._progress_pane) +- self._progress_pane = None +- +- if self._update_box in self.get_children(): +- self.remove(self._update_box) +- self._update_box = None +- +- def __progress_cb(self, model, action, bundle_name, current, total): +- if current == total and action == UpdateModel.ACTION_CHECKING: +- self._finished_checking() +- return +- elif current == total: +- self._finished_updating(int(current)) +- return +- +- if action == UpdateModel.ACTION_CHECKING: +- message = _('%s...') % bundle_name +- elif action == UpdateModel.ACTION_DOWNLOADING: +- message = _('Downloading %s...') % bundle_name +- elif action == UpdateModel.ACTION_UPDATING: +- message = _('Updating %s...') % bundle_name +- +- self._switch_to_progress_pane() +- self._progress_pane.set_message(message) +- self._progress_pane.set_progress(current / float(total)) +- +- def _finished_checking(self): +- logging.debug('ActivityUpdater._finished_checking') +- available_updates = len(self._model.updates) +- if not available_updates: +- top_message = _('Your software is up-to-date') +- else: +- top_message = ngettext('You can install %s update', +- 'You can install %s updates', +- available_updates) +- top_message = top_message % available_updates +- top_message = gobject.markup_escape_text(top_message) +- +- self._top_label.set_markup('<big>%s</big>' % top_message) +- +- if not available_updates: +- self._clear_center() +- else: +- self._switch_to_update_box() +- self._update_box.refresh() +- +- def __refresh_button_clicked_cb(self, button): +- self._refresh() +- +- def _refresh(self): +- top_message = _('Checking for updates...') +- self._top_label.set_markup('<big>%s</big>' % top_message) +- self._model.check_updates() +- +- def __install_button_clicked_cb(self, button): +- text = '<big>%s</big>' % _('Installing updates...') +- self._top_label.set_markup(text) +- self._model.update(self._update_box.get_bundles_to_update()) +- +- def __cancel_button_clicked_cb(self, button): +- self._model.cancel() +- +- def _finished_updating(self, installed_updates): +- logging.debug('ActivityUpdater._finished_updating') +- top_message = ngettext('%s update was installed', +- '%s updates were installed', installed_updates) +- top_message = top_message % installed_updates +- top_message = gobject.markup_escape_text(top_message) +- self._top_label.set_markup('<big>%s</big>' % top_message) +- self._clear_center() +- +- def undo(self): +- self._model.cancel() +- +- +-class ProgressPane(gtk.VBox): +- """Container which replaces the `ActivityPane` during refresh or +- install.""" +- +- def __init__(self): +- gtk.VBox.__init__(self) +- self.set_spacing(style.DEFAULT_PADDING) +- self.set_border_width(style.DEFAULT_SPACING * 2) +- +- self._progress = gtk.ProgressBar() +- self.pack_start(self._progress) +- self._progress.show() +- +- self._label = gtk.Label() +- self._label.set_line_wrap(True) +- self._label.set_property('xalign', 0.5) +- self._label.modify_fg(gtk.STATE_NORMAL, +- style.COLOR_BUTTON_GREY.get_gdk_color()) +- self.pack_start(self._label) +- self._label.show() +- +- alignment_box = gtk.Alignment(xalign=0.5, yalign=0.5) +- self.pack_start(alignment_box) +- alignment_box.show() +- +- self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL) +- alignment_box.add(self.cancel_button) +- self.cancel_button.show() +- +- def set_message(self, message): +- self._label.set_text(message) +- +- def set_progress(self, fraction): +- self._progress.props.fraction = fraction +- +- +-class UpdateBox(gtk.VBox): +- +- def __init__(self, model): +- gtk.VBox.__init__(self) +- +- self._model = model +- +- self.set_spacing(style.DEFAULT_PADDING) +- +- scrolled_window = gtk.ScrolledWindow() +- scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) +- self.pack_start(scrolled_window) +- scrolled_window.show() +- +- self._update_list = UpdateList(model) +- self._update_list.props.model.connect('row-changed', +- self.__row_changed_cb) +- scrolled_window.add(self._update_list) +- self._update_list.show() +- +- bottom_box = gtk.HBox() +- bottom_box.set_spacing(style.DEFAULT_SPACING) +- self.pack_start(bottom_box, expand=False) +- bottom_box.show() +- +- self._size_label = gtk.Label() +- self._size_label.props.xalign = 0 +- self._size_label.set_justify(gtk.JUSTIFY_LEFT) +- bottom_box.pack_start(self._size_label, expand=True) +- self._size_label.show() +- +- self.refresh_button = gtk.Button(stock=gtk.STOCK_REFRESH) +- bottom_box.pack_start(self.refresh_button, expand=False) +- self.refresh_button.show() +- +- self.install_button = gtk.Button(_('Install selected')) +- self.install_button.props.image = Icon(icon_name='emblem-downloads', +- icon_size=gtk.ICON_SIZE_BUTTON) +- bottom_box.pack_start(self.install_button, expand=False) +- self.install_button.show() +- +- self._update_total_size_label() +- +- def refresh(self): +- self._update_list.refresh() +- +- def __row_changed_cb(self, list_model, path, iterator): +- self._update_total_size_label() +- self._update_install_button() +- +- def _update_total_size_label(self): +- total_size = 0 +- for row in self._update_list.props.model: +- if row[UpdateListModel.SELECTED]: +- total_size += row[UpdateListModel.SIZE] +- +- markup = _('Download size: %s') % _format_size(total_size) +- self._size_label.set_markup(markup) +- +- def _update_install_button(self): +- for row in self._update_list.props.model: +- if row[UpdateListModel.SELECTED]: +- self.install_button.props.sensitive = True +- return +- self.install_button.props.sensitive = False +- +- def get_bundles_to_update(self): +- bundles_to_update = [] +- for row in self._update_list.props.model: +- if row[UpdateListModel.SELECTED]: +- bundles_to_update.append(row[UpdateListModel.BUNDLE_ID]) +- return bundles_to_update +- +- +-class UpdateList(gtk.TreeView): +- +- def __init__(self, model): +- list_model = UpdateListModel(model) +- gtk.TreeView.__init__(self, list_model) +- +- self.set_reorderable(False) +- self.set_enable_search(False) +- self.set_headers_visible(False) +- +- toggle_renderer = gtk.CellRendererToggle() +- toggle_renderer.props.activatable = True +- toggle_renderer.props.xpad = style.DEFAULT_PADDING +- toggle_renderer.props.indicator_size = style.zoom(26) +- toggle_renderer.connect('toggled', self.__toggled_cb) +- +- toggle_column = gtk.TreeViewColumn() +- toggle_column.pack_start(toggle_renderer) +- toggle_column.add_attribute(toggle_renderer, 'active', +- UpdateListModel.SELECTED) +- self.append_column(toggle_column) +- +- icon_renderer = CellRendererIcon(self) +- icon_renderer.props.width = style.STANDARD_ICON_SIZE +- icon_renderer.props.height = style.STANDARD_ICON_SIZE +- icon_renderer.props.size = style.STANDARD_ICON_SIZE +- icon_renderer.props.xpad = style.DEFAULT_PADDING +- icon_renderer.props.ypad = style.DEFAULT_PADDING +- icon_renderer.props.stroke_color = style.COLOR_TOOLBAR_GREY.get_svg() +- icon_renderer.props.fill_color = style.COLOR_TRANSPARENT.get_svg() +- +- icon_column = gtk.TreeViewColumn() +- icon_column.pack_start(icon_renderer) +- icon_column.add_attribute(icon_renderer, 'file-name', +- UpdateListModel.ICON_FILE_NAME) +- self.append_column(icon_column) +- +- text_renderer = gtk.CellRendererText() +- +- description_column = gtk.TreeViewColumn() +- description_column.pack_start(text_renderer) +- description_column.add_attribute(text_renderer, 'markup', +- UpdateListModel.DESCRIPTION) +- self.append_column(description_column) +- +- def __toggled_cb(self, cell_renderer, path): +- row = self.props.model[path] +- row[UpdateListModel.SELECTED] = not row[UpdateListModel.SELECTED] +- +- def refresh(self): +- pass +- +- +-class UpdateListModel(gtk.ListStore): +- +- BUNDLE_ID = 0 +- SELECTED = 1 +- ICON_FILE_NAME = 2 +- DESCRIPTION = 3 +- SIZE = 4 +- +- def __init__(self, model): +- gtk.ListStore.__init__(self, str, bool, str, str, int) +- +- for bundle_update in model.updates: +- row = [None] * 5 +- row[self.BUNDLE_ID] = bundle_update.bundle.get_bundle_id() +- row[self.SELECTED] = False +- row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon() +- +- if bundle_update.package_type == 'update': +- row[self.SELECTED] = True +- details = _('From version %(current)s to %(new)s (Size: %(size)s)') +- details = details % \ +- {'current': bundle_update.bundle.get_activity_version(), +- 'new': bundle_update.version, +- 'size': _format_size(bundle_update.size)} +- elif bundle_update.package_type == 'new': +- details = _('Install new activity version %(new)s (Size: %(size)s)') +- details = details % \ +- {'new': bundle_update.version, +- 'size': _format_size(bundle_update.size)} +- +- row[self.DESCRIPTION] = '<b>%s</b>\n%s' % \ +- (bundle_update.bundle.get_name(), details) +- +- row[self.SIZE] = bundle_update.size +- +- self.append(row) +- +- +-def _format_size(size): +- """Convert a given size in bytes to a nicer better readable unit""" +- if size == 0: +- # TRANS: download size is 0 +- return _('None') +- elif size < 1024: +- # TRANS: download size of very small updates +- return _('1 KB') +- elif size < 1024 * 1024: +- # TRANS: download size of small updates, e.g. '250 KB' +- return locale.format_string(_('%.0f KB'), size / 1024.0) +- else: +- # TRANS: download size of updates, e.g. '2.3 MB' +- return locale.format_string(_('%.1f MB'), size / 1024.0 / 1024) +-- +1.7.4.4 + diff --git a/rpms/sugar/0072-sl-2713-Add-last-updated-on-field-to-CP-About-my-com.patch b/rpms/sugar/0072-sl-2713-Add-last-updated-on-field-to-CP-About-my-com.patch new file mode 100644 index 0000000..cc5caa3 --- /dev/null +++ b/rpms/sugar/0072-sl-2713-Add-last-updated-on-field-to-CP-About-my-com.patch @@ -0,0 +1,124 @@ +From 8a2807bf23bb8f6c25b5ac5f9b5b0ec17306d171 Mon Sep 17 00:00:00 2001 +From: Anish Mangal <anish@sugarlabs.org> +Date: Tue, 8 Nov 2011 22:49:50 +0000 +Subject: [PATCH 72/82] sl#2713: Add last updated on field to CP/About my + computer +Organization: Sugar Labs Foundation + +Signed-off-by: Anish Mangal <anish@sugarlabs.org> +--- + extensions/cpsection/aboutcomputer/model.py | 60 ++++++++++++++++++++++++++- + extensions/cpsection/aboutcomputer/view.py | 22 ++++++++++ + 2 files changed, 81 insertions(+), 1 deletions(-) + +diff --git a/extensions/cpsection/aboutcomputer/model.py b/extensions/cpsection/aboutcomputer/model.py +index 52a1094..753055d 100644 +--- a/extensions/cpsection/aboutcomputer/model.py ++++ b/extensions/cpsection/aboutcomputer/model.py +@@ -27,7 +27,6 @@ import dbus + + from jarabe import config + +- + _NM_SERVICE = 'org.freedesktop.NetworkManager' + _NM_PATH = '/org/freedesktop/NetworkManager' + _NM_IFACE = 'org.freedesktop.NetworkManager' +@@ -254,3 +253,62 @@ def get_license(): + except IOError: + license_text = _not_available + return license_text ++ ++def get_last_updated_on_field(): ++ ++ # Get the number of UNIX seconds of the last update date. ++ last_update_unix_seconds = {} ++ try: ++ SERVICE_NAME = 'org.sugarlabs.client.System' ++ SERVICE_PATH = '/org/sugarlabs/client/System' ++ SERVICE_INTERFACE = 'org.sugarlabs.client.System' ++ ++ bus_class = dbus.SystemBus() ++ system_obj = bus_class.get_object(SERVICE_NAME, SERVICE_PATH) ++ system_iface = dbus.Interface(system_obj, SERVICE_INTERFACE) ++ system_props = dbus.Interface(system_iface, ++ dbus.PROPERTIES_IFACE) ++ last_update_unix_seconds = system_props.Get(SERVICE_INTERFACE, ++ 'LastUpdate') ++ except: ++ msg_str = ('There was some error fetching the last-update-time' ++ '\nfrom sugar-server. Please contact Support.') ++ _logger.exception(msg_str) ++ return _(msg_str) ++ ++ # Check once agin that 'last_update_unix_seconds' is not empty. ++ # You never know ! ++ if not last_update_unix_seconds: ++ return _('No update yet!') ++ ++ # If we reached here, we have the last-update-time, but it's in ++ # timestamp format. ++ # Using python-subprocess-module (no shell involved), to convert ++ # it into readable date-format; the hack being used (after ++ # removing '-u' option) is the first one mentioned at : ++ # http://www.commandlinefu.com/commands/view/3719/convert-unix-timestamp-to-date ++ environment = os.environ.copy() ++ environment['PATH'] = '%s:/usr/sbin' % (environment['PATH'], ) ++ ++ last_update_readable_format = {} ++ try: ++ last_update_readable_format = \ ++ subprocess.Popen(['date', '-d', ++ '1970-01-01 + ' + ++ str(last_update_unix_seconds) + ++ ' seconds'], ++ stdout=subprocess.PIPE, ++ env=environment).stdout.readlines()[0] ++ except: ++ msg_str = ('There was some error in the processing of' ++ '\nlast-update-time. Please contact Support.') ++ _logger.exception(msg_str) ++ return _(msg_str) ++ ++ if not last_update_readable_format: ++ return _('There was some error in the processing of\n' ++ 'last-update-time. Please contact Support.') ++ ++ # Everything should be fine (hopefully :-) ) ++ return last_update_readable_format ++ +diff --git a/extensions/cpsection/aboutcomputer/view.py b/extensions/cpsection/aboutcomputer/view.py +index d719372..dd2f200 100644 +--- a/extensions/cpsection/aboutcomputer/view.py ++++ b/extensions/cpsection/aboutcomputer/view.py +@@ -171,6 +171,28 @@ class AboutComputer(SectionView): + box_software.pack_start(box_wireless_fw, expand=False) + box_wireless_fw.show() + ++ # Try to fetch "Last Updated On" field from the model. ++ # If the field is not empty, display it. ++ # At present, the field is returned empty, only if "root-access" ++ # is disabled on the target-machine. ++ last_updated_on_field = self._model.get_last_updated_on_field() ++ if last_updated_on_field: ++ box_last_updated_on = gtk.HBox(spacing=style.DEFAULT_SPACING) ++ label_last_updated_on = gtk.Label(_('Last Updated On:')) ++ label_last_updated_on.set_alignment(1, 0) ++ label_last_updated_on.modify_fg(gtk.STATE_NORMAL, ++ style.COLOR_SELECTION_GREY.get_gdk_color()) ++ box_last_updated_on.pack_start(label_last_updated_on, expand=False) ++ self._group.add_widget(label_last_updated_on) ++ label_last_updated_on.show() ++ label_last_updated_on_field = \ ++ gtk.Label(last_updated_on_field) ++ label_last_updated_on_field.set_alignment(0, 0) ++ box_last_updated_on.pack_start(label_last_updated_on_field, expand=False) ++ label_last_updated_on_field.show() ++ box_software.pack_start(box_last_updated_on, expand=False) ++ box_last_updated_on.show() ++ + self._vbox.pack_start(box_software, expand=False) + box_software.show() + +-- +1.7.4.4 + diff --git a/rpms/sugar/0073-AU-694-More-friendly-message-when-not-able-to-connec.patch b/rpms/sugar/0073-AU-694-More-friendly-message-when-not-able-to-connec.patch new file mode 100644 index 0000000..bc3df67 --- /dev/null +++ b/rpms/sugar/0073-AU-694-More-friendly-message-when-not-able-to-connec.patch @@ -0,0 +1,32 @@ +From 505ba8bb05eb9ae8b8f070a53431d35595930db5 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@sugarlabs.org> +Date: Tue, 8 Nov 2011 22:51:13 +0000 +Subject: [PATCH 73/82] AU#694: More friendly message when not able to connect + to XS +Organization: Sugar Labs Foundation + +Signed-off-by: Ajay Garg <ajay@sugarlabs.org> +Authored-by: Ajay Garg <ajay@sugarlabs.org> +Signed-off-by: Anish Mangal <anish@sugarlabs.org> +--- + src/jarabe/desktop/schoolserver.py | 4 +++- + 1 files changed, 3 insertions(+), 1 deletions(-) + +diff --git a/src/jarabe/desktop/schoolserver.py b/src/jarabe/desktop/schoolserver.py +index 403897b..b9b8830 100644 +--- a/src/jarabe/desktop/schoolserver.py ++++ b/src/jarabe/desktop/schoolserver.py +@@ -140,7 +140,9 @@ def register_laptop(url=_REGISTER_URL): + data = server.register(sn, nick, uuid_, profile.pubkey) + except (xmlrpclib.Error, TypeError, socket.error): + logging.exception('Registration: cannot connect to server') +- raise RegisterError(_('Cannot connect to the server.')) ++ raise RegisterError(_('Please ensure that you are connected ' \ ++ 'to the correct network and that the ' \ ++ 'server is available.')) + finally: + socket.setdefaulttimeout(None) + +-- +1.7.4.4 + diff --git a/rpms/sugar/0074-sl-3286-race-condition-fixed-by-making-the-read-and-.patch b/rpms/sugar/0074-sl-3286-race-condition-fixed-by-making-the-read-and-.patch new file mode 100644 index 0000000..1a26119 --- /dev/null +++ b/rpms/sugar/0074-sl-3286-race-condition-fixed-by-making-the-read-and-.patch @@ -0,0 +1,82 @@ +From 26fe710b8c087dae179f4a3f01e9bf26e6f0cdd3 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Sat, 28 Jan 2012 14:08:56 +0530 +Subject: [PATCH 74/82] sl#3286: race condition fixed by making the read- and + write- calls pseudo synchronous. +Organization: Sugar Labs Foundation + +--- + src/jarabe/model/filetransfer.py | 37 +++++++++++++++++-------------------- + 1 files changed, 17 insertions(+), 20 deletions(-) + +diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py +index 710c3a4..447a74a 100644 +--- a/src/jarabe/model/filetransfer.py ++++ b/src/jarabe/model/filetransfer.py +@@ -70,46 +70,43 @@ class StreamSplicer(gobject.GObject): + + self._input_stream = input_stream + self._output_stream = output_stream +- self._pending_buffers = [] + + def start(self): + self._input_stream.read_async(self._CHUNK_SIZE, self.__read_async_cb, + gobject.PRIORITY_LOW) + ++ """ ++ Fix for bug sl#3286. ++ ++ The workflow to read and write data has been made ++ pseudo-synchronous. ++ The "write_async" call is executed, after the execution ++ of "read_async" call; and the "next" "read_async" call ++ is executed, after the "current" "write_async" call has ++ been completed. ++ """ + def __read_async_cb(self, input_stream, result): + data = input_stream.read_finish(result) + + if not data: + logging.debug('closing input stream') + self._input_stream.close() +- else: +- self._pending_buffers.append(data) +- self._input_stream.read_async(self._CHUNK_SIZE, +- self.__read_async_cb, +- gobject.PRIORITY_LOW) +- self._write_next_buffer() +- +- def __write_async_cb(self, output_stream, result, user_data): +- count_ = output_stream.write_finish(result) +- +- if not self._pending_buffers and \ +- not self._output_stream.has_pending() and \ +- not self._input_stream.has_pending(): + logging.debug('closing output stream') +- output_stream.close() ++ self._output_stream.close() + self.emit('finished') + else: +- self._write_next_buffer() +- +- def _write_next_buffer(self): +- if self._pending_buffers and not self._output_stream.has_pending(): +- data = self._pending_buffers.pop(0) + # TODO: we pass the buffer as user_data because of + # http://bugzilla.gnome.org/show_bug.cgi?id=564102 + self._output_stream.write_async(data, self.__write_async_cb, + gobject.PRIORITY_LOW, + user_data=data) + ++ def __write_async_cb(self, output_stream, result, user_data): ++ output_stream.write_finish(result) ++ self._input_stream.read_async(self._CHUNK_SIZE, ++ self.__read_async_cb, ++ gobject.PRIORITY_LOW) ++ + + class BaseFileTransfer(gobject.GObject): + +-- +1.7.4.4 + diff --git a/rpms/sugar/0075-sl-2955-Ignore-Buddy-updates-before-receiving-the-in.patch b/rpms/sugar/0075-sl-2955-Ignore-Buddy-updates-before-receiving-the-in.patch new file mode 100644 index 0000000..33798d5 --- /dev/null +++ b/rpms/sugar/0075-sl-2955-Ignore-Buddy-updates-before-receiving-the-in.patch @@ -0,0 +1,28 @@ +From 6ffd55af04030a77cf13b6825bbfcf8d19509bed Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Sat, 28 Jan 2012 14:13:33 +0530 +Subject: [PATCH 75/82] sl#2955: Ignore Buddy updates before receiving the + initial Buddy list +Organization: Sugar Labs Foundation + +--- + src/jarabe/model/neighborhood.py | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/src/jarabe/model/neighborhood.py b/src/jarabe/model/neighborhood.py +index 828cb14..4528a15 100644 +--- a/src/jarabe/model/neighborhood.py ++++ b/src/jarabe/model/neighborhood.py +@@ -427,7 +427,8 @@ class _Account(gobject.GObject): + + def __buddy_info_updated_cb(self, handle, properties): + logging.debug('_Account.__buddy_info_updated_cb %r', handle) +- self.emit('buddy-updated', self._buddy_handles[handle], properties) ++ if handle in self._buddy_handles: ++ self.emit('buddy-updated', self._buddy_handles[handle], properties) + + def __current_activity_changed_cb(self, contact_handle, activity_id, + room_handle): +-- +1.7.4.4 + diff --git a/rpms/sugar/0076-uy-1229-Journal-Entry-transfer-from-1-to-N-users.patch b/rpms/sugar/0076-uy-1229-Journal-Entry-transfer-from-1-to-N-users.patch new file mode 100644 index 0000000..b2eea07 --- /dev/null +++ b/rpms/sugar/0076-uy-1229-Journal-Entry-transfer-from-1-to-N-users.patch @@ -0,0 +1,1674 @@ +From 518d6d6b90ca14a6f0a93648b1166d8a9e616427 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Sun, 29 Jan 2012 14:20:39 +0530 +Subject: [PATCH 76/82] uy#1229: Journal-Entry transfer from 1-to-N users. +Organization: Sugar Labs Foundation + +--- + configure.ac | 1 + + data/icons/module-configuration.svg | 190 +++++++++++ + extensions/cpsection/Makefile.am | 1 + + extensions/cpsection/configuration/Makefile.am | 6 + + extensions/cpsection/configuration/__init__.py | 22 ++ + extensions/cpsection/configuration/model.py | 21 ++ + extensions/cpsection/configuration/view.py | 432 ++++++++++++++++++++++++ + src/jarabe/frame/activitiestray.py | 69 ++++- + src/jarabe/journal/palettes.py | 128 +++++++- + src/jarabe/model/buddy.py | 12 + + src/jarabe/model/filetransfer.py | 18 +- + src/jarabe/model/friends.py | 267 ++++++++++++++- + src/jarabe/view/buddymenu.py | 87 +++++- + 13 files changed, 1221 insertions(+), 33 deletions(-) + create mode 100644 data/icons/module-configuration.svg + create mode 100644 extensions/cpsection/configuration/Makefile.am + create mode 100644 extensions/cpsection/configuration/__init__.py + create mode 100644 extensions/cpsection/configuration/model.py + create mode 100644 extensions/cpsection/configuration/view.py + +diff --git a/configure.ac b/configure.ac +index 8e6d871..c028d14 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -52,6 +52,7 @@ data/sugar-emulator.desktop + extensions/cpsection/aboutcomputer/Makefile + extensions/cpsection/accessibility/Makefile + extensions/cpsection/aboutme/Makefile ++extensions/cpsection/configuration/Makefile + extensions/cpsection/datetime/Makefile + extensions/cpsection/frame/Makefile + extensions/cpsection/keyboard/Makefile +diff --git a/data/icons/module-configuration.svg b/data/icons/module-configuration.svg +new file mode 100644 +index 0000000..16ca355 +--- /dev/null ++++ b/data/icons/module-configuration.svg +@@ -0,0 +1,190 @@ ++<?xml version="1.0" encoding="UTF-8" standalone="no"?> ++<!-- Created with Inkscape (http://www.inkscape.org/) --> ++ ++<svg ++ xmlns:dc="http://purl.org/dc/elements/1.1/" ++ xmlns:cc="http://creativecommons.org/ns#" ++ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ++ xmlns:svg="http://www.w3.org/2000/svg" ++ xmlns="http://www.w3.org/2000/svg" ++ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" ++ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" ++ width="744.09448819" ++ height="1052.3622047" ++ id="svg2" ++ version="1.1" ++ inkscape:version="0.48.1 r9760" ++ sodipodi:docname="module-configuration.svg"> ++ <defs ++ id="defs4"> ++ <inkscape:perspective ++ sodipodi:type="inkscape:persp3d" ++ inkscape:vp_x="0 : 526.18109 : 1" ++ inkscape:vp_y="0 : 1000 : 0" ++ inkscape:vp_z="744.09448 : 526.18109 : 1" ++ inkscape:persp3d-origin="372.04724 : 350.78739 : 1" ++ id="perspective6637" /> ++ <inkscape:perspective ++ id="perspective6615" ++ inkscape:persp3d-origin="0.5 : 0.33333333 : 1" ++ inkscape:vp_z="1 : 0.5 : 1" ++ inkscape:vp_y="0 : 1000 : 0" ++ inkscape:vp_x="0 : 0.5 : 1" ++ sodipodi:type="inkscape:persp3d" /> ++ </defs> ++ <sodipodi:namedview ++ id="base" ++ pagecolor="#ffffff" ++ bordercolor="#666666" ++ borderopacity="1.0" ++ inkscape:pageopacity="0.0" ++ inkscape:pageshadow="2" ++ inkscape:zoom="0.35" ++ inkscape:cx="375" ++ inkscape:cy="514.28571" ++ inkscape:document-units="px" ++ inkscape:current-layer="layer1" ++ showgrid="false" ++ inkscape:window-width="1366" ++ inkscape:window-height="693" ++ inkscape:window-x="0" ++ inkscape:window-y="25" ++ inkscape:window-maximized="1" /> ++ <metadata ++ id="metadata7"> ++ <rdf:RDF> ++ <cc:Work ++ rdf:about=""> ++ <dc:format>image/svg+xml</dc:format> ++ <dc:type ++ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> ++ <dc:title></dc:title> ++ </cc:Work> ++ </rdf:RDF> ++ </metadata> ++ <g ++ inkscape:label="Layer 1" ++ inkscape:groupmode="layer" ++ id="layer1"> ++ <g ++ id="layer1-3" ++ inkscape:label="Layer 1" ++ transform="matrix(2.0112611,0,0,2.8271726,-382.79436,-991.72999)"> ++ <g ++ inkscape:label="Layer 1" ++ id="layer1-1" ++ transform="translate(170.0671,-314.28571)"> ++ <g ++ transform="matrix(9.8137136,0,0,9.8137136,-2250.1262,598.08659)" ++ id="g6596"> ++ <g ++ id="g7197"> ++ <path ++ sodipodi:type="arc" ++ style="fill:none;stroke:#00000f;stroke-width:9.03419971;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" ++ id="path5989" ++ sodipodi:cx="307.3714" ++ sodipodi:cy="24.611456" ++ sodipodi:rx="12.380237" ++ sodipodi:ry="11.953332" ++ d="m 319.75164,24.611456 c 0,6.601643 -5.54283,11.953332 -12.38024,11.953332 -6.83742,0 -12.38024,-5.351689 -12.38024,-11.953332 0,-6.601643 5.54282,-11.953332 12.38024,-11.953332 6.83741,0 12.38024,5.351689 12.38024,11.953332 z" ++ transform="matrix(0.77926228,0,0,0.79380856,10.613376,6.1120011)" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505" ++ width="7.9117804" ++ height="9.2506971" ++ x="246.11057" ++ y="7.1083627" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-9" ++ width="7.9117804" ++ height="9.2506971" ++ x="246.21878" ++ y="35.461403" ++ rx="0.82999998" ++ ry="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-7" ++ width="7.9117804" ++ height="9.2506971" ++ x="-29.96331" ++ y="231.45294" ++ transform="matrix(0,-1,1,0,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-6" ++ width="7.9117804" ++ height="9.2506971" ++ x="-29.848055" ++ y="259.62869" ++ transform="matrix(0,-1,1,0,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-76" ++ width="7.9117804" ++ height="9.2506971" ++ x="154.41437" ++ y="176.47606" ++ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-76-7" ++ width="7.9117804" ++ height="9.2506971" ++ x="-199.0881" ++ y="-177.31622" ++ transform="matrix(-0.70710678,-0.70710678,-0.70710678,0.70710678,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-76-5" ++ width="7.9117804" ++ height="9.2506971" ++ x="154.6358" ++ y="204.75911" ++ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ <rect ++ style="fill:#00000f;fill-opacity:1;stroke:none" ++ id="rect6505-76-0" ++ width="7.9117804" ++ height="9.2506971" ++ x="-199.24612" ++ y="-148.95982" ++ transform="matrix(-0.70710678,-0.70710678,-0.70710678,0.70710678,0,0)" ++ ry="0.82999998" ++ rx="0.82999998" /> ++ </g> ++ </g> ++ </g> ++ <path ++ inkscape:connector-curvature="0" ++ id="path3073" ++ d="M 340.90476,719.02884 C 339.85714,717.98124 339,703.77275 339,687.4545 l 0,-29.6696 -10.32864,-4.3156 -10.32863,-4.31557 -22.38347,21.78237 c -18.44567,17.95034 -23.2886,21.29505 -27.52851,19.01228 -6.09928,-3.28383 -46.57361,-44.63814 -46.57361,-47.5862 0,-1.12068 9.16077,-11.48513 20.35725,-23.03211 l 20.35725,-20.99453 -4.31522,-10.84382 -4.31523,-10.84382 -29.61345,-1.42857 -29.61345,-1.42858 -0.80918,-34.05845 c -0.60171,-25.32665 0.13079,-34.65488 2.85715,-36.38482 2.01647,-1.2795 15.35566,-2.34605 29.64263,-2.37012 l 25.97632,-0.0437 3.54536,-11.83336 3.54537,-11.83336 -18.8074,-18.95263 c -10.34407,-10.42395 -18.8074,-20.23446 -18.8074,-21.80115 0,-1.56668 10.92551,-13.71726 24.2789,-27.00129 l 24.2789,-24.15277 19.97202,19.81898 19.97202,19.81898 12.17765,-4.4318 12.17766,-4.4318 0.71651,-24.02847 c 1.19845,-40.19036 -1.99777,-37.19155 38.64555,-36.25866 L 409,356.6479 l 0.80987,28.88986 0.80986,28.88986 11.95928,4.69773 11.95928,4.69774 18.73113,-18.5876 c 10.30212,-10.22317 20.65581,-18.58759 23.00821,-18.58759 2.3524,0 15.0678,10.84703 28.2564,24.10452 l 23.97931,24.10453 -20.52888,21.17152 -20.52889,21.17153 4.32606,8.29538 c 2.37931,4.56245 4.33394,9.85754 4.34363,11.76685 0.0114,2.44368 8.68823,3.71255 29.30331,4.28572 l 29.28572,0.81423 0,35.71429 0,35.71428 -29.28915,0.81428 -29.28914,0.81428 -4.3258,10.35309 -4.3258,10.3531 20.04351,20.67097 c 11.02395,11.36904 20.04352,22.24137 20.04352,24.16074 0,5.18526 -43.1282,48.54783 -48.28543,48.54783 -2.44054,0 -13.64577,-9.14697 -24.90053,-20.32663 l -20.46319,-20.3266 -10.31828,3.40534 -10.31829,3.40532 0,28.11617 c 0,15.46391 -0.78041,30.14991 -1.73425,32.63557 -1.44498,3.76557 -7.32266,4.5194 -35.23809,4.5194 -18.42712,0 -34.36099,-0.85714 -35.40861,-1.90477 l 0,0 z M 394.3828,591.70551 c 16.14693,-4.82459 33.99418,-23.23962 38.65434,-39.88406 7.17003,-25.60883 -3.3713,-52.07528 -26.41559,-66.32247 -11.46728,-7.08967 -16.74237,-8.49394 -31.90726,-8.49394 -25.11984,0 -42.50709,10.48723 -53.57143,32.31195 -12.59921,24.85226 -8.97102,47.7484 10.68602,67.43544 17.12848,17.15462 38.27107,22.20862 62.55392,14.95308 l 0,0 z" ++ style="fill:#a0a0a0;stroke:none" /> ++ <path ++ inkscape:connector-curvature="0" ++ id="path3075" ++ d="m 340.31812,686.89964 -2.03213,-31.35394 -10.35728,-4.13652 -10.35728,-4.13651 -22.11154,21.11617 c -12.16135,11.61391 -23.18839,21.1162 -24.50454,21.1162 -1.31614,0 -12.4099,-10.29334 -24.65281,-22.87409 -25.3507,-26.05026 -25.50888,-21.71631 1.82765,-50.07477 l 16.61352,-17.23457 -4.608,-11.73082 -4.608,-11.73082 -29.78067,-2.02985 -29.78067,-2.02985 0.80253,-33.29047 0.80253,-33.29047 28.48749,-2.85715 28.48749,-2.85714 3.28334,-11.42857 3.28334,-11.42857 -18.1994,-19.00791 c -10.00967,-10.45436 -18.1994,-20.12738 -18.1994,-21.4956 0,-1.36824 10.28354,-12.71092 22.85231,-25.20598 l 22.85231,-22.71828 19.3964,19.24778 19.3964,19.24777 13.13154,-4.47437 13.13155,-4.47437 1.15055,-26.27381 c 0.63281,-14.45059 2.14472,-27.88095 3.3598,-29.84523 1.55902,-2.52031 11.82996,-3.57143 34.89776,-3.57143 l 32.68852,0 0,22.67114 c 0,30.00654 2.58714,36.16256 17.01434,40.48506 l 11.55808,3.46288 18.32364,-17.59525 c 10.078,-9.67739 19.89161,-17.59526 21.80802,-17.59526 1.9164,0 13.92055,10.23947 26.67586,22.75438 l 23.19149,22.75438 -19.88883,20.06797 -19.88886,20.06796 4.43392,12.17766 c 4.09622,11.25017 5.3934,12.32472 17.03171,14.10873 6.92877,1.06209 19.99063,2.02637 29.02634,2.14285 l 16.42857,0.21178 0,34.28572 0,34.28571 -21.07142,0 c -33.44695,0 -38.35323,1.70239 -42.23369,14.65427 l -3.32149,11.0861 19.02758,19.70916 c 10.4652,10.84003 19.0276,21.34799 19.0276,23.35102 0,4.76402 -40.74506,45.48517 -45.51189,45.48517 -2.01771,0 -12.90991,-8.96083 -24.20489,-19.91295 -20.44586,-19.82525 -20.58136,-19.89914 -30.76296,-16.77431 l -10.22664,3.13866 -2.50396,31.77428 L 409,716.6479 l -33.32487,0.80283 -33.32488,0.80282 -2.03213,-31.35391 z m 58.05936,-96.11759 c 41.48929,-17.33107 50.46415,-70.92953 16.82283,-100.467 -5.72605,-5.02754 -15.82082,-10.5977 -22.43283,-12.37813 -14.95988,-4.02828 -38.40229,-1.33506 -50.05351,5.75047 -22.67204,13.78769 -35.67939,49.00392 -26.7625,72.45711 3.99028,10.49523 19.70634,27.24005 31.3352,33.38637 12.72992,6.72828 36.58078,7.31236 51.09081,1.25118 z" ++ style="fill:#a0a0a0;stroke:none" /> ++ <path ++ inkscape:connector-curvature="0" ++ id="path3077" ++ d="m 340.31239,686.8111 -2.0264,-31.2654 -10.35728,-4.13652 -10.35728,-4.13651 -22.11154,21.11617 c -12.16135,11.61391 -23.18839,21.1162 -24.50454,21.1162 -1.31614,0 -12.4099,-10.29334 -24.65281,-22.87409 -25.3507,-26.05026 -25.50888,-21.71631 1.82765,-50.07477 l 16.61352,-17.23457 -4.73383,-12.05114 c -4.33898,-11.04598 -5.59133,-12.05284 -15.01471,-12.07146 -5.65448,-0.0112 -18.95946,-0.83994 -29.5666,-1.84172 l -19.28571,-1.82142 0,-33.48294 0,-33.48293 29.21025,-2.53248 29.21025,-2.53248 3.27487,-11.42857 3.27486,-11.42857 -18.1994,-19.00791 c -10.00967,-10.45436 -18.1994,-20.12738 -18.1994,-21.4956 0,-1.36824 10.28354,-12.71092 22.85231,-25.20598 l 22.85231,-22.71828 19.3964,19.24778 19.3964,19.24777 13.13154,-4.47437 13.13155,-4.47437 1.15055,-26.27381 c 0.63281,-14.45059 2.14472,-27.88095 3.3598,-29.84523 1.55902,-2.52031 11.82996,-3.57143 34.89776,-3.57143 l 32.68852,0 0.2848,22.14286 c 0.406,31.56613 2.03422,35.66433 16.12267,40.58042 l 11.8983,4.15186 18.45696,-17.72329 c 10.15133,-9.7478 20.02494,-17.72328 21.94135,-17.72328 1.9164,0 13.92055,10.23947 26.67586,22.75438 l 23.19149,22.75438 -19.88883,20.06797 -19.88886,20.06796 4.43392,12.17766 c 4.09622,11.25017 5.3934,12.32472 17.03171,14.10873 6.92877,1.06209 19.99063,2.02637 29.02634,2.14285 l 16.42857,0.21178 0,34.28572 0,34.28571 -23.57142,0.0421 c -27.72475,0.0495 -36.66832,3.7013 -40.32949,16.46697 -2.54951,8.88964 -1.8394,10.14932 16.33206,28.97166 10.44857,10.82287 18.99743,21.31677 18.99743,23.3198 0,4.74805 -40.73712,45.48517 -45.48518,45.48517 -2.00302,0 -12.29835,-8.35715 -22.87852,-18.57143 -18.70259,-18.05583 -23.52162,-20.28709 -36.37094,-16.84 -5.69473,1.52771 -6.31799,4.13083 -8.1997,34.24668 l -2.03651,32.59332 -33.70233,0 -33.70233,0 -2.02639,-31.26537 z m 58.06509,-96.02905 c 31.3829,-13.10939 46.15865,-49.6639 32.35734,-80.05056 -16.96561,-37.35354 -70.38585,-46.62407 -98.95583,-17.17275 -24.62497,25.38461 -24.40007,61.43104 0.53077,85.07441 16.93513,16.06056 44.54805,21.13818 66.06772,12.1489 l 0,0 z" ++ style="fill:#ffffff;stroke:none" /> ++ </g> ++ </g> ++</svg> +diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am +index 1cbf8ea..7324431 100644 +--- a/extensions/cpsection/Makefile.am ++++ b/extensions/cpsection/Makefile.am +@@ -2,6 +2,7 @@ SUBDIRS = \ + aboutme \ + aboutcomputer \ + accessibility \ ++ configuration \ + datetime \ + frame \ + keyboard \ +diff --git a/extensions/cpsection/configuration/Makefile.am b/extensions/cpsection/configuration/Makefile.am +new file mode 100644 +index 0000000..9f3718a +--- /dev/null ++++ b/extensions/cpsection/configuration/Makefile.am +@@ -0,0 +1,6 @@ ++sugardir = $(pkgdatadir)/extensions/cpsection/configuration ++ ++sugar_PYTHON = \ ++ __init__.py \ ++ model.py \ ++ view.py +diff --git a/extensions/cpsection/configuration/__init__.py b/extensions/cpsection/configuration/__init__.py +new file mode 100644 +index 0000000..dd61992 +--- /dev/null ++++ b/extensions/cpsection/configuration/__init__.py +@@ -0,0 +1,22 @@ ++# Copyright (C) 2012 Simon Schampijer <erikos@sugarlabs.org> ++# Copyright (C) 2012 Ajay Garg <ajay@activitycentral.com> ++# ++# 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 St, Fifth Floor, Boston, MA 02110-1301 USA ++ ++from gettext import gettext as _ ++ ++CLASS = 'Configuration' ++ICON = 'module-configuration' ++TITLE = _('Configuration') +diff --git a/extensions/cpsection/configuration/model.py b/extensions/cpsection/configuration/model.py +new file mode 100644 +index 0000000..4fd798d +--- /dev/null ++++ b/extensions/cpsection/configuration/model.py +@@ -0,0 +1,21 @@ ++# Copyright (C) 2012 Simon Schampijer <erikos@sugarlabs.org> ++# Copyright (C) 2012 Ajay Garg <ajay@activitycentral.com> ++# ++# 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 St, Fifth Floor, Boston, MA 02110-1301 USA ++# ++ ++import logging ++ ++_logger = logging.getLogger('ControlPanel - Configuration') +diff --git a/extensions/cpsection/configuration/view.py b/extensions/cpsection/configuration/view.py +new file mode 100644 +index 0000000..8389fd3 +--- /dev/null ++++ b/extensions/cpsection/configuration/view.py +@@ -0,0 +1,432 @@ ++# Copyright (C) 2012 Simon Schampijer <erikos@sugarlabs.org> ++# Copyright (C) 2012 Ajay Garg <ajay@activitycentral.com> ++# ++# 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 St, Fifth Floor, Boston, MA 02110-1301 USA ++ ++import gtk ++import gobject ++from gettext import gettext as _ ++ ++from sugar.graphics import style ++from sugar.graphics.alert import Alert ++from sugar.graphics.icon import Icon ++ ++from jarabe.model import buddy ++from jarabe.model import friends ++from jarabe.model import neighborhood ++ ++from jarabe.controlpanel.sectionview import SectionView ++from jarabe.controlpanel.inlinealert import InlineAlert ++ ++owner_model = buddy.get_owner_instance() ++friends_model = friends.get_model() ++neighborhood_model = neighborhood.get_model() ++ ++class LeftAlignedLabelWidget(gtk.HBox): ++ ++ def __init__(self, label): ++ gtk.HBox.__init__(self) ++ label_widget = gtk.Label(label) ++ label_widget.set_line_wrap(True) ++ self.pack_start(label_widget, expand=False) ++ self.show_all() ++ ++ ++class CenterAlignedLabelWidget(gtk.VBox): ++ ++ def __init__(self, label): ++ gtk.VBox.__init__(self) ++ label_widget = gtk.Label(label) ++ label_widget.set_line_wrap(True) ++ self.pack_start(label_widget, expand=False) ++ self.show_all() ++ ++ ++class BuddyWidget(gtk.HBox): ++ ++ def __init__(self, buddy): ++ gtk.HBox.__init__(self) ++ self._buddy = buddy ++ ++ self._check_button = gtk.CheckButton(label=self._buddy.nick) ++ self._check_button.set_active(False) ++ self.pack_start(self._check_button, expand=False) ++ self.show_all() ++ ++ def _is_buddy_selected(self): ++ return self._check_button.get_active() ++ ++ def _get_buddy_key(self): ++ return self._buddy.key ++ ++ def _get_buddy_nick(self): ++ return self._buddy.nick ++ ++ def _get_buddy_account(self): ++ if hasattr(self._buddy, 'account'): ++ return self._buddy.account ++ return None ++ ++ def _get_buddy_contact_id(self): ++ if hasattr(self._buddy, 'contact_id'): ++ return self._buddy.contact_id ++ return None ++ ++ ++class AddRemoveWidget(gtk.VBox): ++ ++ def __init__(self, label, group_detail, add_button_clicked_cb, ++ remove_button_clicked_cb, index): ++ gtk.VBox.__init__(self) ++ self.set_homogeneous(False) ++ self.set_spacing(10) ++ ++ self._potential_new_group = False ++ self._group_name = label ++ self._group_detail = group_detail ++ ++ self._primary_box = gtk.HBox() ++ self._primary_box.set_homogeneous(False) ++ self._primary_box.set_spacing(10) ++ self.pack_start(self._primary_box, expand=False) ++ self._primary_box.show_all() ++ ++ self._index = index ++ self._add_button_added = False ++ self._remove_button_added = False ++ ++ self._label = gtk.Entry() ++ self._label.set_text(label) ++ ++ # Do not allow an already existing group name to be modified. ++ if len(label) > 0: ++ self._label.set_sensitive(False) ++ # Else, this is a potentially new group. ++ # Mark this is as new. ++ # However, this will ACTUALLY be a new group, ++ # only if it is given a name. ++ # That check will be done, after the user clicks the final save ++ # button. ++ else: ++ self._potential_new_group = True ++ ++ self._primary_box.pack_start(self._label, expand=False) ++ ++ if not self._potential_new_group: ++ self._details_button = gtk.Button(_('View Details')) ++ self._view_id = self._details_button.connect('clicked', self.__view_details_cb) ++ self._primary_box.pack_start(self._details_button, ++ expand=False) ++ ++ add_icon = Icon(icon_name='list-add') ++ self._add_button = gtk.Button() ++ self._add_button.set_image(add_icon) ++ self._add_button.connect('clicked', ++ add_button_clicked_cb, ++ self) ++ ++ remove_icon = Icon(icon_name='list-remove') ++ self._remove_button = gtk.Button() ++ self._remove_button.set_image(remove_icon) ++ self._remove_button.connect('clicked', ++ remove_button_clicked_cb, ++ self) ++ ++ self.__add_add_button() ++ self.__add_remove_button() ++ ++ self._details_table = gtk.VBox() ++ self._details_table.set_spacing(20) ++ self._details_table.show_all() ++ self.pack_start(self._details_table, expand=False) ++ ++ if self._potential_new_group: ++ info_label = LeftAlignedLabelWidget(_('You may batch add' ++ ' from the following available online buddies. Any' ++ ' currently offline buddy may be added later from the' ++ ' neighborhood view, when it comes online.')) ++ self.pack_start(info_label, expand=False) ++ self._friends_list_box = gtk.VBox() ++ self._friends_list_box.set_homogeneous(True) ++ self._friends_list_box.set_spacing(20) ++ self.pack_start(self._friends_list_box, expand=False) ++ self._populate_friends_list() ++ ++ self.pack_start(gtk.HSeparator()) ++ ++ self._primary_box.show_all() ++ self.show_all() ++ ++ def _populate_friends_list(self): ++ buddies = neighborhood_model.get_buddies() ++ for buddy in buddies: ++ ++ # Only make the buddy visible, if it has a valid key at ++ # this time. ++ if ((hasattr(buddy, 'key')) and (buddy.key is not None)): ++ ++ # Do not show self :) ++ if buddy.key == owner_model.get_key(): ++ continue ++ ++ self._friends_list_box.pack_start(BuddyWidget(buddy)) ++ ++ self._friends_list_box.show_all() ++ ++ def _get_buddy_widgets(self): ++ return self._friends_list_box.get_children() ++ ++ def _get_index(self): ++ return self._index ++ ++ def _set_index(self, value): ++ self._index = value ++ ++ def _get_entry(self): ++ return self._label.get_text() ++ ++ def __add_add_button(self): ++ self._primary_box.pack_start(self._add_button, expand=False) ++ self._add_button_added = True ++ ++ def _remove_remove_button_if_not_already(self): ++ if self._remove_button_added: ++ self.__remove_remove_button() ++ ++ def __remove_remove_button(self): ++ self._primary_box.remove(self._remove_button) ++ self._remove_button_added = False ++ ++ def _add_remove_button_if_not_already(self): ++ if not self._remove_button_added: ++ self.__add_remove_button() ++ ++ def __add_remove_button(self): ++ self._primary_box.pack_start(self._remove_button, expand=False) ++ self._remove_button_added = True ++ ++ def __activate_view_id(self): ++ self._details_button.disconnect(self._hide_id) ++ self._view_id = self._details_button.connect('clicked', ++ self.__view_details_cb) ++ ++ def __activate_hide_id(self): ++ self._details_button.disconnect(self._view_id) ++ self._hide_id = self._details_button.connect('clicked', ++ self.__hide_details_cb) ++ ++ def __view_details_cb(self, widget): ++ if self._group_detail is None: ++ return ++ ++ last_operation_value = friends_model._get_last_group_operation(self._group_name) ++ ++ self._last_operation_box = gtk.VBox() ++ self._last_operation_box.pack_start(LeftAlignedLabelWidget(_('Last' ++ ' Operation On This Group :: ')), expand=False) ++ self._last_operation_box.pack_start(CenterAlignedLabelWidget( ++ last_operation_value), expand=False) ++ self._last_operation_box.show_all() ++ ++ self._details_table.pack_start(self._last_operation_box, ++ expand=False) ++ ++ self._container = gtk.Table() ++ self._details_table.pack_start(self._container, expand=False) ++ ++ headings_list = [_('Nick'), _('Last Operation Status')] ++ for i in range(0, len(headings_list)): ++ self._container.attach(gtk.Label(headings_list[i]), i, i+1, ++ 0, 1) ++ for i in range(0, len(headings_list)): ++ self._container.attach(gtk.Label(''), i, i+1, 1, 2) ++ ++ index = 2 ++ friend_keys_of_group = \ ++ friends_model._get_friend_keys_of_group(self._group_name) ++ ++ for friend_key in friend_keys_of_group: ++ friend_model = friends_model._get_friend_by_key(friend_key) ++ label_widgets = [] ++ nick = friend_model.get_nick() ++ label_widgets.append(CenterAlignedLabelWidget(nick)) ++ ++ last_operation_status = \ ++ friends_model._get_last_operation_status_of_friend_in_group(self._group_name, ++ friend_key) ++ label_widgets.append(CenterAlignedLabelWidget(last_operation_status)) ++ ++ for i in range(0, len(label_widgets)): ++ self._container.attach(label_widgets[i], i, i+1, index, ++ index+1) ++ index = index + 1 ++ ++ self._container.show_all() ++ ++ self._details_button.set_label(_('Hide Details')) ++ self.__activate_hide_id() ++ ++ def __hide_details_cb(self, widget): ++ self._details_table.remove(self._last_operation_box) ++ self._details_table.remove(self._container) ++ ++ self._details_button.set_label(_('View Details')) ++ self.__activate_view_id() ++ ++ ++class MultiWidget(gtk.VBox): ++ ++ def __init__(self): ++ gtk.VBox.__init__(self) ++ self.set_spacing(10) ++ ++ def _add_widget(self, label, metadata): ++ new_widget = AddRemoveWidget(label, ++ metadata, ++ self.__add_button_clicked_cb, ++ self.__remove_button_clicked_cb, ++ len(self.get_children())) ++ self.add(new_widget) ++ self.show_all() ++ self._update_remove_button_statuses() ++ ++ def _add_blank_entry(self): ++ self._add_widget('', None) ++ ++ def __add_button_clicked_cb(self, add_button, ++ add_button_container): ++ self._add_blank_entry() ++ self._update_remove_button_statuses() ++ ++ def __remove_button_clicked_cb(self, remove_button, ++ remove_button_container): ++ # Remove group from the model. ++ group_name = remove_button_container._get_entry() ++ friends_model.remove_group(group_name) ++ ++ # Remove group from the view. ++ self.remove(remove_button_container) ++ ++ self._update_remove_button_statuses() ++ ++ def _update_remove_button_statuses(self): ++ children = self.get_children() ++ ++ # Now, if there is only one entry, remove-button ++ # should not be shown. ++ if len(children) == 1: ++ children[0]._remove_remove_button_if_not_already() ++ ++ # Alternatively, if there are more than 1 entries, ++ # remove-button should be shown for all. ++ if len(children) > 1: ++ for child in children: ++ child._add_remove_button_if_not_already() ++ ++ def set_groups(self, groups): ++ self._groups = groups ++ ++ def _pre_save_operations(self): ++ for child in self.get_children(): ++ if child._potential_new_group: ++ group_name = child._get_entry() ++ if len(group_name) > 0: ++ friends_model.add_group(group_name, False) ++ ++ # Also, add all the selected buddies as friends. ++ buddy_widgets = child._get_buddy_widgets() ++ for widget in buddy_widgets: ++ if widget._is_buddy_selected(): ++ # Add as friend. ++ friends_model.make_friend_by_parameters( ++ widget._get_buddy_key(), ++ widget._get_buddy_nick(), ++ widget._get_buddy_account(), ++ widget._get_buddy_contact_id()) ++ ++ # Add as friend in group. ++ friends_model.add_friend_to_group(widget._get_buddy_key(), ++ group_name, ++ False) ++ ++ # Perform just one disk-write for groups. ++ friends_model.save_groups() ++ ++ ++class Configuration(SectionView): ++ def __init__(self, model, alerts=None): ++ SectionView.__init__(self) ++ ++ self._model = model ++ ++ self.set_border_width(style.DEFAULT_SPACING * 2) ++ self.set_spacing(style.DEFAULT_SPACING) ++ group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) ++ ++ workspace = gtk.VBox() ++ workspace.show() ++ ++ separator = gtk.HSeparator() ++ workspace.pack_start(separator, expand=False) ++ ++ label_friend_groups = gtk.Label(_('Groups configuration')) ++ label_friend_groups.set_alignment(0, 0) ++ workspace.pack_start(label_friend_groups, expand=False) ++ ++ box_friend_groups = gtk.VBox() ++ box_friend_groups.set_border_width(style.DEFAULT_SPACING * 2) ++ box_friend_groups.set_spacing(style.DEFAULT_SPACING) ++ ++ self._widget_table = MultiWidget() ++ box_friend_groups.pack_start(self._widget_table, expand=False) ++ ++ save_button = gtk.Button() ++ save_button.set_alignment(0, 0) ++ save_button.set_label('Save') ++ save_button.connect('clicked', self.__save_button_clicked_cb) ++ box_save_button = gtk.HBox() ++ box_save_button.set_homogeneous(False) ++ box_save_button.pack_start(save_button, expand=False) ++ box_save_button.show_all() ++ ++ box_friend_groups.pack_start(box_save_button, expand=False) ++ ++ box_friend_groups.show_all() ++ workspace.pack_start(box_friend_groups, expand=False) ++ ++ scrolled = gtk.ScrolledWindow() ++ scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) ++ scrolled.add_with_viewport(workspace) ++ scrolled.show() ++ self.add(scrolled) ++ ++ workspace.show_all() ++ self.setup() ++ ++ def setup(self): ++ groups = friends_model._get_groups() ++ groups.sort() ++ ++ if len(groups) == 0: ++ self._widget_table._add_blank_entry() ++ else: ++ for group_name in groups: ++ group_detail = \ ++ friends_model._get_group_by_key_name(group_name) ++ self._widget_table._add_widget(group_name, group_detail) ++ ++ def __save_button_clicked_cb(self, save_button): ++ save_button.set_sensitive(False) ++ self._widget_table._pre_save_operations() +diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py +index 941b174..d2902be 100644 +--- a/src/jarabe/frame/activitiestray.py ++++ b/src/jarabe/frame/activitiestray.py +@@ -354,7 +354,9 @@ class BaseTransferButton(ToolButton): + def remove(self): + frame = jarabe.frame.get_view() + frame.remove_notification(self.notif_icon) +- self.props.parent.remove(self) ++ if (self.props.parent is not None) and \ ++ (self in self.props.parent.get_children()): ++ self.props.parent.remove(self) + + def __notify_state_cb(self, file_transfer, pspec): + logging.debug('_update state: %r %r', file_transfer.props.state, +@@ -471,6 +473,13 @@ class OutgoingTransferButton(BaseTransferButton): + frame.add_notification(self.notif_icon, + gtk.CORNER_TOP_LEFT) + ++ # TODO: figure out why this is necessary to do. ++ # if this step is not done, then invoking ++ # "__dismiss_clicked_cb()" WITHOUT clicking the "Dismiss" ++ # option (as in the case of auto-dismiss for bulk ++ # operations), DOES NOT WORK. ++ self.create_palette() ++ + def create_palette(self): + palette = OutgoingTransferPalette(self.file_transfer) + palette.connect('dismiss-clicked', self.__dismiss_clicked_cb) +@@ -480,6 +489,14 @@ class OutgoingTransferButton(BaseTransferButton): + + def __dismiss_clicked_cb(self, palette): + self.remove() ++ bulk_operation_details = \ ++ self.file_transfer._get_bulk_operation_details() ++ if bulk_operation_details is not None: ++ group_name = bulk_operation_details._get_group_name() ++ friend_keys = bulk_operation_details._get_friend_keys() ++ counter = bulk_operation_details._get_counter() ++ proceed_cb = bulk_operation_details._get_proceed_cb() ++ proceed_cb(group_name, friend_keys, counter) + + + class BaseTransferPalette(Palette): +@@ -565,6 +582,7 @@ class IncomingTransferPalette(BaseTransferPalette): + + def _update(self): + logging.debug('_update state: %r', self.file_transfer.props.state) ++ + if self.file_transfer.props.state == filetransfer.FT_STATE_PENDING: + menu_item = MenuItem(_('Accept'), icon_name='dialog-ok') + menu_item.connect('activate', self.__accept_activate_cb) +@@ -595,7 +613,6 @@ class IncomingTransferPalette(BaseTransferPalette): + + elif self.file_transfer.props.state in \ + [filetransfer.FT_STATE_ACCEPTED, filetransfer.FT_STATE_OPEN]: +- + for item in self.menu.get_children(): + self.menu.remove(item) + +@@ -619,7 +636,6 @@ class IncomingTransferPalette(BaseTransferPalette): + self.update_progress() + + elif self.file_transfer.props.state == filetransfer.FT_STATE_COMPLETED: +- + for item in self.menu.get_children(): + self.menu.remove(item) + +@@ -629,8 +645,8 @@ class IncomingTransferPalette(BaseTransferPalette): + menu_item.show() + + self.update_progress() +- elif self.file_transfer.props.state == filetransfer.FT_STATE_CANCELLED: + ++ elif self.file_transfer.props.state == filetransfer.FT_STATE_CANCELLED: + for item in self.menu.get_children(): + self.menu.remove(item) + +@@ -684,11 +700,21 @@ class OutgoingTransferPalette(BaseTransferPalette): + + self.progress_bar = None + self.progress_label = None ++ self._bulk_operation_details = \ ++ file_transfer._get_bulk_operation_details() + + self.file_transfer.connect('notify::state', self.__notify_state_cb) + + nick = str(file_transfer.buddy.props.nick) +- label = glib.markup_escape_text(_('Transfer to %s') % (nick,)) ++ ++ label = None ++ if self._bulk_operation_details is None: ++ label = glib.markup_escape_text(_('Transfer to %s') % (nick,)) ++ else: ++ counter = self._bulk_operation_details._get_counter() ++ total = self._bulk_operation_details._get_total() ++ label = glib.markup_escape_text(_('( %d / %d ) Transfer to %s') \ ++ % (counter, total, nick,)) + self.props.secondary_text = label + + self._update() +@@ -699,8 +725,8 @@ class OutgoingTransferPalette(BaseTransferPalette): + def _update(self): + new_state = self.file_transfer.props.state + logging.debug('_update state: %r', new_state) +- if new_state == filetransfer.FT_STATE_PENDING: + ++ if new_state == filetransfer.FT_STATE_PENDING: + menu_item = MenuItem(_('Cancel'), icon_name='dialog-cancel') + menu_item.connect('activate', self.__cancel_activate_cb) + self.menu.append(menu_item) +@@ -725,7 +751,6 @@ class OutgoingTransferPalette(BaseTransferPalette): + + elif new_state in [filetransfer.FT_STATE_ACCEPTED, + filetransfer.FT_STATE_OPEN]: +- + for item in self.menu.get_children(): + self.menu.remove(item) + +@@ -750,7 +775,6 @@ class OutgoingTransferPalette(BaseTransferPalette): + + elif new_state in [filetransfer.FT_STATE_COMPLETED, + filetransfer.FT_STATE_CANCELLED]: +- + for item in self.menu.get_children(): + self.menu.remove(item) + +@@ -758,9 +782,36 @@ class OutgoingTransferPalette(BaseTransferPalette): + menu_item.connect('activate', self.__dismiss_activate_cb) + self.menu.append(menu_item) + menu_item.show() +- + self.update_progress() + ++ # Perform actions specific to bulk-operation. ++ if self._bulk_operation_details is not None: ++ ++ # Update the peration status. ++ status = None ++ if new_state == filetransfer.FT_STATE_COMPLETED: ++ status = _('Success.') ++ elif new_state == filetransfer.FT_STATE_CANCELLED: ++ if self.file_transfer.reason_last_change == \ ++ filetransfer.FT_REASON_REMOTE_STOPPED: ++ status = _('FAILURE:\tOperation Cancelled Remotely.') ++ elif self.file_transfer.reason_last_change == \ ++ filetransfer.FT_REASON_LOCAL_STOPPED: ++ status = _('FAILURE:\tOperation Cancelled Locally.') ++ ++ self._set_bulk_operation_status_if_applicable(status) ++ ++ # "Dismiss' automatically for all, except the last of ++ # the bulk-operation. ++ counter = self._bulk_operation_details._get_counter() ++ total = self._bulk_operation_details._get_total() ++ if counter < total: ++ self.__dismiss_activate_cb(None) ++ ++ def _set_bulk_operation_status_if_applicable(self, status): ++ if self._bulk_operation_details is not None: ++ self._bulk_operation_details._set_operation_status(status) ++ + def __cancel_activate_cb(self, menu_item): + self.file_transfer.cancel() + +diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py +index 8fc1e5d..e1d1e7d 100644 +--- a/src/jarabe/journal/palettes.py ++++ b/src/jarabe/journal/palettes.py +@@ -37,6 +37,39 @@ from jarabe.model import mimeregistry + from jarabe.journal import misc + from jarabe.journal import model + ++friends_model = friends.get_model() ++ ++ ++class BulkOperationDetails(): ++ ++ def __init__(self, group_name, friend, friend_keys, total, counter, proceed_cb): ++ self._group_name = group_name ++ self._friend = friend ++ self._friend_keys = friend_keys ++ self._counter = counter ++ self._total = total ++ self._proceed_cb = proceed_cb ++ ++ def _get_group_name(self): ++ return self._group_name ++ ++ def _get_friend_keys(self): ++ return self._friend_keys ++ ++ def _get_counter(self): ++ return self._counter ++ ++ def _get_total(self): ++ return self._total ++ ++ def _get_proceed_cb(self): ++ return self._proceed_cb ++ ++ def _set_operation_status(self, status): ++ friends_model._set_last_operation_status_of_friend_in_group(self._group_name, ++ self._friend.get_key(), ++ status) ++ + + class ObjectPalette(Palette): + +@@ -117,6 +150,10 @@ class ObjectPalette(Palette): + friends_menu.connect('friend-selected', self.__friend_selected_cb) + menu_item.set_submenu(friends_menu) + ++ groups_menu = GroupsMenu() ++ groups_menu.connect('group-selected', self.__group_selected_cb) ++ friends_menu._set_group_menu(groups_menu) ++ + if detail == True: + menu_item = MenuItem(_('View Details'), 'go-right') + menu_item.connect('activate', self.__detail_activate_cb) +@@ -150,7 +187,8 @@ class ObjectPalette(Palette): + def __volume_error_cb(self, menu_item, message, severity): + self.emit('volume-error', message, severity) + +- def __friend_selected_cb(self, menu_item, buddy): ++ def __friend_selected_cb(self, menu_item, buddy, ++ bulk_operation_details=None): + logging.debug('__friend_selected_cb') + file_name = model.get_file(self._metadata['uid']) + +@@ -167,9 +205,51 @@ class ObjectPalette(Palette): + + if not mime_type: + mime_type = mime.get_for_file(file_name) +- + filetransfer.start_transfer(buddy, file_name, title, description, +- mime_type) ++ mime_type, bulk_operation_details) ++ ++ def __group_selected_cb(self, menu_item, group_name): ++ logging.debug('__group_selected_cb') ++ if group_name is not None: ++ friends_model._set_last_group_operation(group_name, ++ _('(TRANSFER) %s') % (self._metadata['title'],)) ++ friends_model._set_last_operation_status_of_friends_in_group_with_common_status( ++ group_name, _('PENDING ...')) ++ friend_keys = \ ++ friends_model._get_friend_keys_of_group(group_name) ++ ++ self._proceed_with_next_friend(group_name, friend_keys, 0) ++ ++ """ ++ This is the (callback) function that needs to be called per friend. ++ Note that this function is a callback (and not a looped one), since ++ the "next" friend iteration begins, only when the current iteration ++ has finished - which is asynchronous. ++ """ ++ def _proceed_with_next_friend(self, group_name, friend_keys, counter): ++ counter = counter + 1 ++ ++ if counter <= len(friend_keys): ++ friend_key = friend_keys[counter-1] ++ friend = friends_model._get_friend_by_key(friend_key) ++ ++ bulk_operation_details = \ ++ BulkOperationDetails(group_name, ++ friend, ++ friend_keys, ++ len(friend_keys), ++ counter, ++ self._proceed_with_next_friend) ++ ++ # Only proceed if the friend is online. ++ # Else, set the failure-status, and move forward. ++ if friend.is_present(): ++ self.__friend_selected_cb(None, friend, bulk_operation_details) ++ else: ++ bulk_operation_details._set_operation_status(_('FAILURE:\tFriend' ++ ' is offline.')) ++ self._proceed_with_next_friend(group_name, friend_keys, ++ counter) + + + class CopyMenu(gtk.Menu): +@@ -295,6 +375,41 @@ class ClipboardMenu(MenuItem): + self._temp_file_path = None + + ++class GroupsMenu(gtk.Menu): ++ __gtype_name__ = 'GroupsMenu' ++ ++ __gsignals__ = { ++ 'group-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ++ ([object])), ++ } ++ ++ def __init__(self): ++ gobject.GObject.__init__(self) ++ ++ if filetransfer.file_transfer_available(): ++ for group in friends_model._get_groups(): ++ menu_item = MenuItem(text_label=group, ++ icon_name='zoom-groups') ++ menu_item.connect('activate', self.__item_activate_cb, ++ group) ++ self.append(menu_item) ++ menu_item.show() ++ ++ if not self.get_children(): ++ menu_item = MenuItem(_('No groups present')) ++ menu_item.set_sensitive(False) ++ self.append(menu_item) ++ menu_item.show() ++ else: ++ menu_item = MenuItem(_('No valid connection found')) ++ menu_item.set_sensitive(False) ++ self.append(menu_item) ++ menu_item.show() ++ ++ def __item_activate_cb(self, menu_item, group): ++ self.emit('group-selected', group) ++ ++ + class FriendsMenu(gtk.Menu): + __gtype_name__ = 'JournalFriendsMenu' + +@@ -307,7 +422,6 @@ class FriendsMenu(gtk.Menu): + gobject.GObject.__init__(self) + + if filetransfer.file_transfer_available(): +- friends_model = friends.get_model() + for friend in friends_model: + if friend.is_present(): + menu_item = MenuItem(text_label=friend.get_nick(), +@@ -332,6 +446,12 @@ class FriendsMenu(gtk.Menu): + def __item_activate_cb(self, menu_item, friend): + self.emit('friend-selected', friend) + ++ def _set_group_menu(self, group_menu): ++ menu_item = MenuItem(_('Select a group, to send')) ++ menu_item.set_submenu(group_menu) ++ self.append(menu_item) ++ menu_item.show() ++ + + class StartWithMenu(gtk.Menu): + __gtype_name__ = 'JournalStartWithMenu' +diff --git a/src/jarabe/model/buddy.py b/src/jarabe/model/buddy.py +index 8f17d7e..c6c6a4c 100644 +--- a/src/jarabe/model/buddy.py ++++ b/src/jarabe/model/buddy.py +@@ -43,6 +43,7 @@ class BaseBuddyModel(gobject.GObject): + self._color = None + self._tags = None + self._current_activity = None ++ self._groups = None + + gobject.GObject.__init__(self, **kwargs) + +@@ -87,6 +88,16 @@ class BaseBuddyModel(gobject.GObject): + getter=get_current_activity, + setter=set_current_activity) + ++ def get_groups(self): ++ return self._groups ++ ++ def set_groups(self, groups): ++ self._groups = groups ++ ++ groups = gobject.property(type=object, ++ getter=get_groups, ++ setter=set_groups) ++ + def is_owner(self): + raise NotImplementedError + +@@ -179,6 +190,7 @@ class BuddyModel(BaseBuddyModel): + self._account = None + self._contact_id = None + self._handle = None ++ self._groups_list = [] + + BaseBuddyModel.__init__(self, **kwargs) + +diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py +index 447a74a..3de8c20 100644 +--- a/src/jarabe/model/filetransfer.py ++++ b/src/jarabe/model/filetransfer.py +@@ -124,6 +124,7 @@ class BaseFileTransfer(gobject.GObject): + self.mime_type = None + self.initial_offset = 0 + self.reason_last_change = FT_REASON_NONE ++ self._bulk_operation_details = None + + def set_channel(self, channel): + self.channel = channel +@@ -156,6 +157,12 @@ class BaseFileTransfer(gobject.GObject): + def _get_transferred_bytes(self): + return self._transferred_bytes + ++ def _get_bulk_operation_details(self): ++ return self._bulk_operation_details ++ ++ def _set_bulk_operation_details(self, bulk_operation_details): ++ self._bulk_operation_details = bulk_operation_details ++ + transferred_bytes = gobject.property(type=int, default=0, + getter=_get_transferred_bytes, setter=_set_transferred_bytes) + +@@ -226,7 +233,8 @@ class IncomingFileTransfer(BaseFileTransfer): + + + class OutgoingFileTransfer(BaseFileTransfer): +- def __init__(self, buddy, file_name, title, description, mime_type): ++ def __init__(self, buddy, file_name, title, description, mime_type, ++ bulk_operation_details): + + presence_service = presenceservice.get_instance() + name, path = presence_service.get_preferred_connection() +@@ -241,6 +249,7 @@ class OutgoingFileTransfer(BaseFileTransfer): + self._socket = None + self._splicer = None + self._output_stream = None ++ self._set_bulk_operation_details(bulk_operation_details) + + self.buddy = buddy + self.title = title +@@ -324,9 +333,12 @@ def init(): + _monitor_connection(connection) + + +-def start_transfer(buddy, file_name, title, description, mime_type): ++def start_transfer(buddy, file_name, title, description, mime_type, ++ bulk_operation_details): + outgoing_file_transfer = OutgoingFileTransfer(buddy, file_name, title, +- description, mime_type) ++ description, ++ mime_type, ++ bulk_operation_details) + new_file_transfer.send(None, file_transfer=outgoing_file_transfer) + + +diff --git a/src/jarabe/model/friends.py b/src/jarabe/model/friends.py +index 7605af1..448a36e 100644 +--- a/src/jarabe/model/friends.py ++++ b/src/jarabe/model/friends.py +@@ -14,6 +14,8 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ++from gettext import gettext as _ ++ + import os + import logging + from ConfigParser import ConfigParser +@@ -22,6 +24,7 @@ import gobject + import dbus + + from sugar import env ++from sugar.util import unique_id + from sugar.graphics.xocolor import XoColor + + from jarabe.model.buddy import BuddyModel +@@ -36,11 +39,11 @@ class FriendBuddyModel(BuddyModel): + + _NOT_PRESENT_COLOR = '#D5D5D5,#FFFFFF' + +- def __init__(self, nick, key, account=None, contact_id=None): ++ def __init__(self, nick, key, account=None, contact_id=None, groups=[]): + self._online_buddy = None + + BuddyModel.__init__(self, nick=nick, key=key, account=account, +- contact_id=contact_id) ++ contact_id=contact_id, groups=groups) + + neighborhood_model = neighborhood.get_model() + neighborhood_model.connect('buddy-added', self.__buddy_added_cb) +@@ -110,35 +113,239 @@ class Friends(gobject.GObject): + + def __init__(self): + gobject.GObject.__init__(self) +- +- self._friends = {} + self._path = os.path.join(env.get_profile_path(), 'friends') ++ self._groups_path = os.path.join(env.get_profile_path(), 'groups') ++ self.reinit() + ++ def reinit(self): ++ self._friends = {} ++ self._groups = {} + self.load() + + def has_buddy(self, buddy): +- return buddy.get_key() in self._friends ++ return self.check_buddy_existence_by_key(buddy.get_key()) ++ ++ """ ++ Ideally, this should be the only publically exposed API, since ++ only the buddy_key is the deciding factor of the existence of ++ a friend. There can never be two friends of the same key. ++ """ ++ def check_buddy_existence_by_key(self, buddy_key): ++ return buddy_key in self._friends + + def add_friend(self, buddy_info): + self._friends[buddy_info.get_key()] = buddy_info + self.emit('friend-added', buddy_info) + +- def make_friend(self, buddy): +- if not self.has_buddy(buddy): +- buddy = FriendBuddyModel(key=buddy.key, nick=buddy.nick, +- account=buddy.account, +- contact_id=buddy.contact_id) ++ def add_friend_to_group(self, buddy_key, group_name, ++ save_group=True): ++ if not self.__group_exists(group_name): ++ return _('Cannot add to group !! Create this group first !!') ++ ++ if self.__friend_exists_in_group(group_name, buddy_key): ++ return _('Friend already exists in this group') ++ ++ # If we reach here, it is safe to add this buddy :) ++ self._groups[group_name]['friends'][buddy_key] = {} ++ ++ # Also, update the association from the buddy side, and save. ++ groups = self._get_groups_of_a_friend(buddy_key) ++ if group_name not in groups: ++ groups.append(group_name) ++ groups.sort() ++ self.save() ++ ++ if save_group: ++ self.save_groups() ++ ++ def remove_friend_group_assoc_using_friend_key(self, group_name, ++ friend_key, save_friend=True, save_group=True): ++ if not self.__group_exists(group_name): ++ return _('No such group exists !!') ++ ++ # Remove association from friend. ++ if group_name in self._friends[friend_key].get_groups(): ++ self._friends[friend_key].get_groups().remove(group_name) ++ ++ # Remove association from group. ++ if friend_key in self._groups[group_name]['friends'].keys(): ++ del self._groups[group_name]['friends'][friend_key] ++ ++ if save_friend: ++ self.save() ++ if save_group: ++ self.save_groups() ++ ++ def _get_groups_of_a_friend(self, friend_key): ++ if friend_key not in self._friends.keys(): ++ return [] ++ ++ return self._friends[friend_key].get_groups() ++ ++ def add_group(self, group_name, save_group=True): ++ if self.__group_exists(group_name): ++ return _('A group with the same name already exists !!' ++ 'Choose a different group-name.') ++ ++ # If we reach here, it is safe to add a group of this name. ++ self._groups[group_name] = {} ++ self._groups[group_name]['friends'] = {} ++ self._groups[group_name]['last-operation'] = 'NONE.' ++ if save_group: ++ self.save_groups() ++ ++ def remove_group(self, group_name, save_friend=True, ++ save_group=True): ++ if not self.__group_exists(group_name): ++ return _('No group exists of this name !!') ++ ++ # Remove this group, from all the friend-associations. ++ for friend_key in self._friends.keys(): ++ friend_groups_list = self._friends[friend_key].get_groups() ++ if group_name in friend_groups_list: ++ friend_groups_list.remove(group_name) ++ if save_friend: ++ self.save() ++ ++ # Remove the group itself. ++ del self._groups[group_name] ++ if save_group: ++ self.save_groups() ++ ++ def __group_exists(self, group_name): ++ if group_name in self._groups.keys(): ++ return True ++ return False ++ ++ def __friend_exists_in_group(self, group_name, friend_key): ++ if friend_key in \ ++ self._groups[group_name]['friends'].keys(): ++ return True ++ return False ++ ++ """ ++ This method is useful in the case, when the buddy is to be added as ++ a friend in deferred sense. For example, the buddies list may first be ++ fetched by virtue of their being online, but may go offline by the ++ time they are about to be saved. So, in that case, we retrieve the ++ parameters that are required for saving in the early stages itself. ++ """ ++ def make_friend_by_parameters(self, buddy_key, buddy_nick, buddy_account, buddy_contact_id): ++ if not self.check_buddy_existence_by_key(buddy_key): ++ buddy = FriendBuddyModel(key=buddy_key, ++ nick=buddy_nick, ++ account=buddy_account, ++ contact_id=buddy_contact_id) + self.add_friend(buddy) + self.save() + ++ def make_friend(self, buddy): ++ self.make_friend_by_parameters(buddy.key, buddy.nick, ++ buddy.account, buddy.contact_id) ++ ++ def remove_groups_of_friend(self, buddy_info, save_friend=True, ++ save_group=True): ++ groups = self._get_groups_of_a_friend(buddy_info.get_key()) ++ for group in groups: ++ self.remove_friend_group_assoc_using_friend_key(group, ++ buddy_info.get_key(), False, False) ++ ++ if save_friend: ++ self.save() ++ if save_group: ++ self.save_groups() ++ + def remove(self, buddy_info): ++ # First remove its association from all its groups ++ self.remove_groups_of_friend(buddy_info, False, True) ++ ++ # Now, remove the friend-entity itself. + del self._friends[buddy_info.get_key()] + self.save() ++ + self.emit('friend-removed', buddy_info.get_key()) + + def __iter__(self): + return self._friends.values().__iter__() + ++ def _get_friend_by_key(self, key): ++ if not (key in self._friends.keys()): ++ return _('No friend found !!') ++ return self._friends[key] ++ ++ def _get_friend_keys_of_group(self, group_name): ++ if not self.__group_exists(group_name): ++ return [] ++ ++ return self._groups[group_name]['friends'].keys() ++ ++ def _get_groups(self): ++ # Sascha's wonderful feedback: always export only the thing ++ # required. Here, we required only the group-names; so export ++ # just the keys ++ return self._groups.keys() ++ ++ def _set_groups(self, groups): ++ self._groups = groups ++ ++ def _get_group_by_key_name(self, group_name): ++ if not self.__group_exists(group_name): ++ return None ++ return self._groups[group_name] ++ ++ def _get_last_group_operation(self, group_name): ++ if not self.__group_exists(group_name): ++ return _('No group of this name exists !!') ++ return self._groups[group_name]['last-operation'] ++ ++ def _set_last_group_operation(self, group_name, operation): ++ if not self.__group_exists(group_name): ++ return _('No group exists.') ++ self._groups[group_name]['last-operation'] = operation ++ self.save_groups ++ ++ def _get_last_operation_status_of_friend_in_group(self, group_name, ++ friend_key): ++ if not self.__group_exists(group_name): ++ return _('No group exists.') ++ ++ if not self.__friend_exists_in_group(group_name, friend_key): ++ return _('Friend does not exist in group') ++ ++ if 'last-operation-status' in \ ++ self._groups[group_name]['friends'][friend_key].keys(): ++ return self._groups[group_name]['friends'][friend_key]['last-operation-status'] ++ ++ def _set_last_operation_status_of_friend_in_group(self, group_name, ++ friend_key, status, ++ save_group=True): ++ if not self.__group_exists(group_name): ++ return _('No group exists.') ++ ++ if not self.__friend_exists_in_group(group_name, friend_key): ++ return _('Friend does not exist in group') ++ ++ self._groups[group_name]['friends'][friend_key]['last-operation-status'] = \ ++ status ++ ++ if save_group: ++ self.save_groups() ++ ++ def _set_last_operation_status_of_friends_in_group_with_common_status( ++ self, group_name, status): ++ if not self.__group_exists(group_name): ++ return _('No group exists.') ++ ++ friend_keys = self._get_friend_keys_of_group(group_name) ++ for friend_key in friend_keys: ++ self._set_last_operation_status_of_friend_in_group(group_name, ++ friend_key, ++ status, ++ False) ++ ++ # Save the groups in one go. ++ self.save_groups() ++ + def load(self): + cp = ConfigParser() + +@@ -149,11 +356,36 @@ class Friends(gobject.GObject): + # HACK: don't screw up on old friends files + if len(key) < 20: + continue +- buddy = FriendBuddyModel(key=key, nick=cp.get(key, 'nick')) ++ ++ # Check for the existence of 'groups' option (for ++ # backwards compatability) ++ groups = [] ++ if cp.has_option(key, 'groups'): ++ groups = eval(cp.get(key, 'groups')) ++ ++ buddy = FriendBuddyModel(key=key, nick=cp.get(key, ++ 'nick'), groups=groups) + self.add_friend(buddy) + except Exception: + logging.exception('Error parsing friends file') + ++ self.__load_groups() ++ ++ def __load_groups(self): ++ cp = ConfigParser() ++ ++ try: ++ success = cp.read([self._groups_path]) ++ if success: ++ for group_name in cp.sections(): ++ self._groups[group_name] = {} ++ self._groups[group_name]['friends'] = \ ++ eval(cp.get(group_name, 'friends')) ++ self._groups[group_name]['last-operation'] = \ ++ cp.get(group_name, 'last-operation') ++ except: ++ logging.exception('Error while loading config') ++ + def save(self): + cp = ConfigParser() + +@@ -161,11 +393,24 @@ class Friends(gobject.GObject): + section = friend.get_key() + cp.add_section(section) + cp.set(section, 'nick', friend.get_nick()) ++ cp.set(section, 'groups', friend.get_groups()) + + fileobject = open(self._path, 'w') + cp.write(fileobject) + fileobject.close() + ++ def save_groups(self): ++ cp = ConfigParser() ++ ++ for group in self._groups.keys(): ++ cp.add_section(group) ++ cp.set(group, 'friends', self._groups[group]['friends']) ++ cp.set(group, 'last-operation', ++ self._groups[group]['last-operation']) ++ ++ fileobject = open(self._groups_path, 'w') ++ cp.write(fileobject) ++ fileobject.close() + + def get_model(): + global _model +diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py +index de5a772..544faeb 100644 +--- a/src/jarabe/view/buddymenu.py ++++ b/src/jarabe/view/buddymenu.py +@@ -22,6 +22,7 @@ import gtk + import gconf + import glib + import dbus ++import gobject + + from sugar.graphics.palette import Palette + from sugar.graphics.menuitem import MenuItem +@@ -33,6 +34,34 @@ from jarabe.model.session import get_session_manager + from jarabe.controlpanel.gui import ControlPanel + import jarabe.desktop.homewindow + ++friends_model = friends.get_model() ++ ++class GroupsMenu(gtk.Menu): ++ __gtype_name__ = 'NeighbourhoodGroupsMenu' ++ ++ __gsignals__ = { ++ 'group-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ++ ([object])), ++ } ++ ++ def __init__(self, groups, no_groups_label): ++ gobject.GObject.__init__(self) ++ ++ for group in groups: ++ menu_item = MenuItem(text_label=group, icon_name='zoom-groups') ++ menu_item.connect('activate', self.__item_activate_cb, group) ++ self.append(menu_item) ++ menu_item.show() ++ ++ if not self.get_children(): ++ menu_item = MenuItem(no_groups_label) ++ menu_item.set_sensitive(False) ++ self.append(menu_item) ++ menu_item.show() ++ ++ def __item_activate_cb(self, menu_item, group): ++ self.emit('group-selected', group) ++ + + class BuddyMenu(Palette): + def __init__(self, buddy): +@@ -64,19 +93,57 @@ class BuddyMenu(Palette): + + def _add_buddy_items(self): + if friends.get_model().has_buddy(self._buddy): +- menu_item = MenuItem(_('Remove friend'), 'list-remove') ++ menu_item = MenuItem(_('Remove friend, and its association' ++ ' from all groups'), 'list-remove') + menu_item.connect('activate', self._remove_friend_cb) ++ self.menu.append(menu_item) + else: +- menu_item = MenuItem(_('Make friend'), 'list-add') ++ menu_item = MenuItem(_('Make friend (without associating' ++ ' to any group)'), 'list-add') + menu_item.connect('activate', self._make_friend_cb) +- +- self.menu.append(menu_item) +- menu_item.show() ++ self.menu.append(menu_item) ++ ++ menu_item_2 = MenuItem(_('Make friend (if not already), and add to group'), ++ 'list-add') ++ ++ all_groups = friends_model._get_groups() ++ groups_of_friend = \ ++ friends_model._get_groups_of_a_friend(self._buddy.get_key()) ++ ++ # Make a local copy of groups, of which the friend is not ++ # associated with. ++ groups_not_of_friend = [] ++ for group in all_groups: ++ groups_not_of_friend.append(group) ++ for group in groups_of_friend: ++ groups_not_of_friend.remove(group) ++ ++ groups_of_friend.sort() ++ groups_not_of_friend.sort() ++ ++ add_groups_menu = GroupsMenu(groups_not_of_friend, _('No groups to' ++ ' associate')) ++ add_groups_menu.connect('group-selected', ++ self.__make_friend_and_add_to_group) ++ self.menu.append(menu_item_2) ++ menu_item_2.set_submenu(add_groups_menu) ++ add_groups_menu.show() ++ ++ menu_item_3 = MenuItem(_('Remove friend from group'), 'list-remove') ++ remove_groups_menu = GroupsMenu(groups_of_friend, _('No groups to' ++ ' disassociate')) ++ remove_groups_menu.connect('group-selected', ++ self.__remove_friend_from_group) ++ self.menu.append(menu_item_3) ++ menu_item_3.set_submenu(remove_groups_menu) ++ remove_groups_menu.show() + + self._invite_menu = MenuItem('') + self._invite_menu.connect('activate', self._invite_friend_cb) + self.menu.append(self._invite_menu) + ++ self.menu.show_all() ++ + home_model = shell.get_model() + self._active_activity_changed_hid = home_model.connect( + 'active-activity-changed', self._cur_activity_changed_cb) +@@ -154,7 +221,7 @@ class BuddyMenu(Palette): + def __buddy_notify_nick_cb(self, buddy, pspec): + self.set_primary_text(glib.markup_escape_text(buddy.props.nick)) + +- def _make_friend_cb(self, menuitem): ++ def _make_friend_cb(self, menuitem=None): + friends.get_model().make_friend(self._buddy) + + def _remove_friend_cb(self, menuitem): +@@ -178,3 +245,11 @@ class BuddyMenu(Palette): + raise + else: + logging.error('Invite failed, activity service not ') ++ ++ def __make_friend_and_add_to_group(self, menu_item, group): ++ self._make_friend_cb() ++ friends_model.add_friend_to_group(self._buddy.get_key(), group) ++ ++ def __remove_friend_from_group(self, menu_item, group): ++ friends_model.remove_friend_group_assoc_using_friend_key(group, ++ self._buddy.get_key()) +-- +1.7.4.4 + diff --git a/rpms/sugar/0077-uy-1223-Add-a-submenu-in-journal-palette-to-copy-a-j.patch b/rpms/sugar/0077-uy-1223-Add-a-submenu-in-journal-palette-to-copy-a-j.patch new file mode 100644 index 0000000..7172519 --- /dev/null +++ b/rpms/sugar/0077-uy-1223-Add-a-submenu-in-journal-palette-to-copy-a-j.patch @@ -0,0 +1,100 @@ +From 1964a246e39196252b376427544cbee157553bb6 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Sun, 29 Jan 2012 18:45:22 +0530 +Subject: [PATCH 77/82] uy#1223: Add a submenu in journal-palette, to copy a + journal entry to the "Documents" folder. +Organization: Sugar Labs Foundation + +--- + src/jarabe/journal/journalactivity.py | 5 ++++ + src/jarabe/journal/palettes.py | 37 +++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+), 0 deletions(-) + +diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py +index bb1c7f6..8cafef0 100644 +--- a/src/jarabe/journal/journalactivity.py ++++ b/src/jarabe/journal/journalactivity.py +@@ -184,6 +184,7 @@ class JournalActivity(JournalWindow): + search_toolbar = self._main_toolbox.search_toolbar + search_toolbar.connect('query-changed', self._query_changed_cb) + search_toolbar.set_mount_point('/') ++ self._mount_point = '/' + + def _setup_secondary_view(self): + self._secondary_view = gtk.VBox() +@@ -253,6 +254,7 @@ class JournalActivity(JournalWindow): + def __volume_changed_cb(self, volume_toolbar, mount_point): + logging.debug('Selected volume: %r.', mount_point) + self._main_toolbox.search_toolbar.set_mount_point(mount_point) ++ self._mount_point = mount_point + self._main_toolbox.set_current_toolbar(0) + + def __model_created_cb(self, sender, **kwargs): +@@ -362,6 +364,9 @@ class JournalActivity(JournalWindow): + self.show_main_view() + self.search_grab_focus() + ++ def get_mount_point(self): ++ return self._mount_point ++ + + def get_journal(): + global _journal +diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py +index e1d1e7d..27b0b54 100644 +--- a/src/jarabe/journal/palettes.py ++++ b/src/jarabe/journal/palettes.py +@@ -272,6 +272,16 @@ class CopyMenu(gtk.Menu): + self.append(clipboard_menu) + clipboard_menu.show() + ++ from jarabe.journal import journalactivity ++ journal_model = journalactivity.get_journal() ++ if journal_model.get_mount_point() != model.get_documents_path(): ++ documents_menu = DocumentsMenu(self._metadata) ++ documents_menu.set_image(Icon(icon_name='user-documents', ++ icon_size=gtk.ICON_SIZE_MENU)) ++ documents_menu.connect('volume-error', self.__volume_error_cb) ++ self.append(documents_menu) ++ documents_menu.show() ++ + if self._metadata['mountpoint'] != '/': + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) +@@ -375,6 +385,33 @@ class ClipboardMenu(MenuItem): + self._temp_file_path = None + + ++class DocumentsMenu(MenuItem): ++ __gtype_name__ = 'JournalDocumentsMenu' ++ ++ __gsignals__ = { ++ 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ++ ([str, str])), ++ } ++ ++ def __init__(self, metadata): ++ MenuItem.__init__(self, _('Documents')) ++ ++ self._temp_file_path = None ++ self._metadata = metadata ++ self.connect('activate', self.__copy_to_documents_cb) ++ ++ def __copy_to_documents_cb(self, menu_item): ++ file_path = model.get_file(self._metadata['uid']) ++ if not file_path or not os.path.exists(file_path): ++ logging.warn('Entries without a file cannot be copied.') ++ self.emit('volume-error', ++ _('Entries without a file cannot be copied.'), ++ _('Warning')) ++ return ++ ++ model.copy(self._metadata, model.get_documents_path()) ++ ++ + class GroupsMenu(gtk.Menu): + __gtype_name__ = 'GroupsMenu' + +-- +1.7.4.4 + diff --git a/rpms/sugar/0078-Aleksey-s-PROXY-feature-merged-into-Network-CP.patch b/rpms/sugar/0078-Aleksey-s-PROXY-feature-merged-into-Network-CP.patch new file mode 100644 index 0000000..e06f260 --- /dev/null +++ b/rpms/sugar/0078-Aleksey-s-PROXY-feature-merged-into-Network-CP.patch @@ -0,0 +1,450 @@ +From 01b2bc2168e2ee8adb07b2bd28147781d47dc388 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Wed, 1 Feb 2012 23:41:49 +0530 +Subject: [PATCH 78/82] Aleksey's PROXY feature merged into Network CP. +Organization: Sugar Labs Foundation + +Signed-off-by: Ajay Garg <ajay@activitycentral.com> +--- + extensions/cpsection/network/view.py | 418 ++++++++++++++++++++++++++++++++++ + 1 files changed, 418 insertions(+), 0 deletions(-) + +diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py +index 9a2444a..e55a48d 100644 +--- a/extensions/cpsection/network/view.py ++++ b/extensions/cpsection/network/view.py +@@ -32,6 +32,399 @@ TITLE = _('Network') + + _APPLY_TIMEOUT = 3000 + ++ ++# "Proxy" feature merged into "Network" ++################################################################################################################# ++ ++# Copyright (C) 2011, Aleksey Lim ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <http://www.gnu.org/licenses/>. ++ ++from gettext import gettext as _ ++import re ++ ++import gconf ++ ++_widget_sensitivies = {} ++_gconf_origin_values = {} ++ ++ ++class Proxy(SectionView): ++ ++ def __init__(self): ++ SectionView.__init__(self) ++ self.set_border_width(style.DEFAULT_SPACING * 2) ++ self.set_spacing(style.DEFAULT_SPACING) ++ self.setup() ++ ++ def setup(self): ++ for i in self.get_children(): ++ self.remove(i) ++ # Destroy all widgets and connection to avoid any interfering ++ i.destroy() ++ ++ _widget_sensitivies.clear() ++ ++ workspace = gtk.VBox() ++ workspace.show() ++ self.add(workspace) ++ ++ def add_section(section, label_text): ++ separator = gtk.HSeparator() ++ separator.show() ++ workspace.pack_start(separator, expand=False) ++ ++ label = gtk.Label(label_text) ++ label.set_alignment(0, 0) ++ label.show() ++ workspace.pack_start(label, expand=False) ++ ++ section.set_border_width(style.DEFAULT_SPACING * 2) ++ section.show() ++ workspace.pack_start(section, expand=False) ++ ++ add_section(_ProxySection(), ++ _('Configure proxies to access the internet')) ++ add_section(_IgnoreSection(), _('Ignore host list')) ++ ++ def undo(self): ++ conf = gconf.client_get_default() ++ for key, value in _gconf_origin_values.items(): ++ if value is None: ++ conf.unset(key) ++ else: ++ conf.set(key, value) ++ ++ @property ++ def needs_restart(self): ++ conf = gconf.client_get_default() ++ for key, value in _gconf_origin_values.items(): ++ if ((value is None and conf.get_without_default(key) is not None) or ++ (value is not None and value.to_string() != conf.get(key).to_string())): ++ return True ++ ++ return False ++ ++ @needs_restart.setter ++ def needs_restart(self, value): ++ # needs_restart is a property (i.e. gets calculated) in this Control ++ # Panel, but SectionView.__init__() wants to initialise it to False, ++ # so we need to provide a (fake) setter. ++ pass ++ ++ ++class _ProxySection(gtk.VBox): ++ ++ def __init__(self): ++ gtk.VBox.__init__(self) ++ self._common_hosts = {} ++ self._common_ports = {} ++ ++ group = gtk.RadioButton() ++ group.props.label = _('Direct internet connection') ++ group.show() ++ self.pack_start(group, expand=False) ++ _register_selector_key('/system/proxy/mode', group, 'none') ++ _register_bool_key('/system/http_proxy/use_http_proxy', group, True) ++ ++ manual_proxy = gtk.RadioButton(group) ++ manual_proxy.props.label = _('Manual proxy configuration') ++ manual_proxy.show() ++ self.pack_start(manual_proxy, expand=False) ++ _register_selector_key('/system/proxy/mode', manual_proxy, 'manual') ++ ++ widgets = self._add_protos() ++ manual_proxy.connect('toggled', _set_sensitive, False, widgets) ++ _set_sensitive(manual_proxy, False, widgets) ++ ++ auto_proxy = gtk.RadioButton(group) ++ auto_proxy.props.label = _('Automatic proxy configuration') ++ auto_proxy.show() ++ self.pack_start(auto_proxy, expand=False) ++ _register_selector_key('/system/proxy/mode', auto_proxy, 'auto') ++ ++ grid = self._sub_section_new() ++ grid.attach_label(_('Autoconfiguration URL (leave empty to use WPAD):'), ++ 0, 1, 0, 1) ++ entry = grid.attach_entry(1, 2, 0, 1) ++ _register_string_key('/system/proxy/autoconfig_url', entry) ++ auto_proxy.connect('toggled', _set_sensitive, False, [grid]) ++ _set_sensitive(auto_proxy, False, [grid]) ++ ++ print 'aa toh raha hai' ++ ++ def _add_protos(self): ++ commons = gtk.CheckButton() ++ commons.props.label = _('Use the same proxy for all protocols') ++ commons.show() ++ self.pack_start(commons) ++ _register_bool_key('/system/http_proxy/use_same_proxy', commons) ++ ++ grid = self._sub_section_new() ++ ++ def add_proto(row, is_common, label_text, host_key, port_key): ++ host_label = grid.attach_label(label_text, 0, 1, row, row + 1) ++ host = grid.attach_entry(1, 2, row, row + 1) ++ ++ port_label = grid.attach_label(_('Port:'), 2, 3, row, row + 1) ++ port_value = gtk.Adjustment(8080, 0, 65536, 1, 10) ++ port = gtk.SpinButton() ++ port.configure(port_value, .1, 0) ++ port.show() ++ grid.attach(port, 3, 4, row, row + 1, ++ gtk.SHRINK | gtk.FILL, gtk.SHRINK) ++ ++ if is_common: ++ _widget_sensitivies.update([ ++ (host_label, None), (host, None), ++ (port_label, None), (port, None)]) ++ self._common_hosts[host] = host.props.buffer ++ self._common_ports[port] = port.props.adjustment ++ ++ _register_string_key(host_key, host) ++ _register_int_key(port_key, port) ++ ++ return host, port ++ ++ http_host, http_port = add_proto(1, False, _('HTTP proxy:'), ++ '/system/http_proxy/host', '/system/http_proxy/port') ++ ++ auth_widget = _AuthWidget() ++ auth_widget.show() ++ grid.attach(auth_widget, 1, 2, 2, 3, gtk.SHRINK | gtk.FILL, gtk.SHRINK) ++ ++ add_proto(3, True, _('Secure HTTP proxy:'), ++ '/system/proxy/secure_host', '/system/proxy/secure_port') ++ add_proto(4, True, _('FTP proxy:'), ++ '/system/proxy/ftp_host', '/system/proxy/ftp_port') ++ add_proto(5, True, _('Socks proxy:'), ++ '/system/proxy/socks_host', '/system/proxy/socks_port') ++ ++ def commons_toggled_cb(sender): ++ for widget in _widget_sensitivies.keys(): ++ _widget_sensitivies[widget] = not sender.props.active ++ _set_sensitive(sender, True, _widget_sensitivies.keys()) ++ ++ for widget, orig_buffer in self._common_hosts.items(): ++ widget.props.buffer = http_host.props.buffer if \ ++ sender.props.active else orig_buffer ++ ++ for widget, orig_adjustment in self._common_ports.items(): ++ widget.props.adjustment = http_port.props.adjustment if \ ++ sender.props.active else orig_adjustment ++ widget.props.value = widget.props.adjustment.value ++ ++ commons.connect('toggled', commons_toggled_cb) ++ commons_toggled_cb(commons) ++ ++ return [commons, grid] ++ ++ def _sub_section_new(self): ++ grid = _Grid(1, 1, False) ++ grid.props.column_spacing = style.DEFAULT_SPACING ++ grid.props.row_spacing = style.DEFAULT_SPACING ++ grid.show() ++ ++ alignment = gtk.Alignment(0, 0, 1, 1) ++ alignment.props.left_padding = style.STANDARD_ICON_SIZE ++ alignment.props.right_padding = style.GRID_CELL_SIZE ++ alignment.add(grid) ++ alignment.show() ++ self.pack_start(alignment) ++ ++ return grid ++ ++ ++class _IgnoreSection(gtk.VBox): ++ ++ def __init__(self): ++ gtk.VBox.__init__(self) ++ ++ entry = gtk.Entry() ++ entry.show() ++ self.pack_start(entry, expand=False) ++ _register_list_key('/system/http_proxy/ignore_hosts', entry) ++ ++ ++class _AuthWidget(gtk.VBox): ++ ++ def __init__(self): ++ gtk.VBox.__init__(self) ++ ++ enable = gtk.CheckButton() ++ enable.props.label = _('Use authentication') ++ enable.show() ++ self.pack_start(enable, expand=False) ++ _register_bool_key('/system/http_proxy/use_authentication', enable) ++ ++ grid = _Grid(2, 2, False) ++ grid.props.column_spacing = style.DEFAULT_SPACING ++ grid.props.row_spacing = style.DEFAULT_SPACING ++ self.pack_start(grid) ++ ++ grid.attach_label(_('Username:'), 0, 1, 0, 1) ++ entry = grid.attach_entry(1, 2, 0, 1) ++ _register_string_key('/system/http_proxy/authentication_user', entry) ++ ++ grid.attach_label(_('Password:'), 0, 1, 1, 2) ++ entry = grid.attach_entry(1, 2, 1, 2) ++ entry.props.visibility = False ++ _register_string_key( ++ '/system/http_proxy/authentication_password', entry) ++ ++ enable.connect('toggled', lambda sender: ++ grid.show() if sender.props.active else grid.hide()) ++ if enable.props.active: ++ grid.show() ++ ++ ++class _Grid(gtk.Table): ++ ++ def attach_label(self, label, left_attach, right_attach, ++ top_attach, bottom_attach): ++ widget = gtk.Label(label) ++ widget.set_alignment(0, 0) ++ widget.set_line_wrap(True) ++ self.attach(widget, left_attach, right_attach, ++ top_attach, bottom_attach, gtk.SHRINK | gtk.FILL, gtk.SHRINK) ++ widget.show() ++ return widget ++ ++ def attach_entry(self, left_attach, right_attach, ++ top_attach, bottom_attach): ++ widget = gtk.Entry() ++ self.attach(widget, left_attach, right_attach, ++ top_attach, bottom_attach, gtk.EXPAND | gtk.FILL, gtk.SHRINK) ++ widget.show() ++ return widget ++ ++ ++def _set_sensitive(sender, reverse, widgets): ++ is_sensitive = sender.props.active ++ if reverse: ++ is_sensitive = not is_sensitive ++ ++ for i in widgets: ++ if isinstance(i, gtk.Container): ++ _set_sensitive(sender, reverse, i.get_children()) ++ i.props.sensitive = is_sensitive and _widget_sensitivies.get(i, True) ++ ++ ++def _register_bool_key(key, widget, reverse=False): ++ ++ def set_cb(widget, x, reverse): ++ value = x.get_bool() ++ if reverse: ++ value = not value ++ widget.props.active = value ++ ++ def get_cb(widget, reverse): ++ x = gconf.Value(gconf.VALUE_BOOL) ++ value = widget.props.active ++ if reverse: ++ value = not value ++ x.set_bool(value) ++ return x ++ ++ _register_key(key, widget, 'toggled', set_cb, get_cb, reverse) ++ ++ ++def _register_string_key(key, widget): ++ ++ def set_cb(widget, x): ++ widget.props.text = x.get_string() ++ ++ def get_cb(widget): ++ x = gconf.Value(gconf.VALUE_STRING) ++ x.set_string(widget.props.text) ++ return x ++ ++ _register_key(key, widget, 'changed', set_cb, get_cb) ++ ++ ++def _register_int_key(key, widget): ++ ++ def set_cb(widget, x): ++ widget.props.value = x.get_int() ++ ++ def get_cb(widget): ++ x = gconf.Value(gconf.VALUE_INT) ++ x.set_int(int(widget.props.value)) ++ return x ++ ++ _register_key(key, widget.props.adjustment, 'value_changed', ++ set_cb, get_cb) ++ ++ ++def _register_selector_key(key, widget, value): ++ ++ def set_cb(widget, x, value): ++ widget.props.active = x.get_string() == value ++ ++ def get_cb(widget, value): ++ if not widget.props.active: ++ return None ++ x = gconf.Value(gconf.VALUE_STRING) ++ x.set_string(value) ++ return x ++ ++ _register_key(key, widget, 'toggled', set_cb, get_cb, value) ++ ++ ++def _register_list_key(key, widget): ++ ++ def set_cb(widget, x): ++ hosts = [i.get_string() for i in x.get_list()] ++ widget.props.text = ', '.join(hosts) ++ ++ def get_cb(widget): ++ hosts = [] ++ for i in re.split('[\s,;:]+', widget.props.text or ''): ++ if not i.strip(): ++ continue ++ value = gconf.Value(gconf.VALUE_STRING) ++ value.set_string(i.strip()) ++ hosts.append(value) ++ x = gconf.Value(gconf.VALUE_LIST) ++ x.set_list_type(gconf.VALUE_STRING) ++ x.set_list(hosts) ++ return x ++ ++ _register_key(key, widget, 'changed', set_cb, get_cb) ++ ++ ++def _register_key(key, widget, signal, set_cb, get_cb, *args): ++ conf = gconf.client_get_default() ++ value = conf.get(key) ++ if value is not None: ++ set_cb(widget, value, *args) ++ ++ _gconf_origin_values[key] = value ++ ++ def signal_cb(sender, key, widget, get_cb, *args): ++ value = get_cb(widget, *args) ++ if value is not None: ++ conf = gconf.client_get_default() ++ conf.set(key, value) ++ ++ widget.connect(signal, signal_cb, key, widget, get_cb, *args) ++################################################################################################################# ++# "Proxy" code-merging finished !! ++ ++ ++ ++ + class AddRemoveWidget(gtk.HBox): + + def __init__(self, label, add_button_clicked_cb, +@@ -340,6 +733,31 @@ class Network(SectionView): + workspace.pack_start(box_hidden_network, expand=False) + box_hidden_network.show() + ++ ++ separator_proxy = gtk.HSeparator() ++ workspace.pack_start(separator_proxy, False) ++ separator_proxy.show() ++ ++ label_proxy = gtk.Label(_('Proxy')) ++ label_proxy.set_alignment(0, 0) ++ workspace.pack_start(label_proxy, expand=False) ++ label_proxy.show() ++ ++ box_proxy = gtk.VBox() ++ box_proxy.set_border_width(style.DEFAULT_SPACING * 2) ++ box_proxy.set_spacing(style.DEFAULT_SPACING) ++ proxy_info = gtk.Label(_("<Enter suitable info>")) ++ proxy_info.set_alignment(0, 0) ++ proxy_info.set_line_wrap(True) ++ box_proxy.pack_start(proxy_info, expand=False) ++ proxy_info.show() ++ workspace.pack_start(box_proxy, expand=False) ++ box_proxy.show() ++ ++ proxy = Proxy() ++ workspace.pack_start(proxy, expand=False) ++ proxy.show() ++ + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled.add_with_viewport(workspace) +-- +1.7.4.4 + diff --git a/rpms/sugar/0079-sl-3316-Wireless-modem-error-should-refer-to-My-Sett.patch b/rpms/sugar/0079-sl-3316-Wireless-modem-error-should-refer-to-My-Sett.patch new file mode 100644 index 0000000..4e02ce9 --- /dev/null +++ b/rpms/sugar/0079-sl-3316-Wireless-modem-error-should-refer-to-My-Sett.patch @@ -0,0 +1,36 @@ +From 1d1e000a8935e82714a8598fd64d879a6dd0d9ae Mon Sep 17 00:00:00 2001 +From: Anish Mangal <anish@activitycentral.com> +Date: Fri, 3 Feb 2012 13:41:00 -0200 +Subject: [PATCH 79/82] sl#3316: Wireless modem error should refer to My + Settings +Organization: Sugar Labs Foundation + +The wireless modem error refers currently to Control Panel. + +Error: No GSM connection available +Suggestion: Create a connection in the control panel + +However, english translation of Control Panel now refer to it as My +Settings. This patch reflects that change. +--- + extensions/deviceicon/network.py | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py +index c70db70..df52e87 100644 +--- a/extensions/deviceicon/network.py ++++ b/extensions/deviceicon/network.py +@@ -835,8 +835,8 @@ class GsmDeviceView(TrayIcon): + error_handler=self.__connect_error_cb) + else: + self._palette.add_alert(_('No GSM connection available.'), \ +- _('Create a connection in the ' \ +- 'control panel.')) ++ _('Create a connection in ' \ ++ 'My Settings.')) + + def __connect_cb(self, active_connection): + logging.debug('Connected successfully to gsm device, %s', +-- +1.7.4.4 + diff --git a/rpms/sugar/0080-au-911-Add-launcher-for-nm-connection-edirot-in-My-S.patch b/rpms/sugar/0080-au-911-Add-launcher-for-nm-connection-edirot-in-My-S.patch new file mode 100644 index 0000000..bc060c9 --- /dev/null +++ b/rpms/sugar/0080-au-911-Add-launcher-for-nm-connection-edirot-in-My-S.patch @@ -0,0 +1,89 @@ +From e4256c8928cc68fa909e3f2f36236eededf54d57 Mon Sep 17 00:00:00 2001 +From: Anish Mangal <anish@activitycentral.com> +Date: Fri, 3 Feb 2012 14:49:56 -0200 +Subject: [PATCH 80/82] au#911: Add launcher for nm-connection-edirot in My + Settings +Organization: Sugar Labs Foundation + +This patch adds adds a launcher which upon excuting launches the +nm-connection-editor. This 'hack' is included as a last-ditch workaround +incase network setting tools provided within sugar are unable to handle +a wierd connection type. +--- + extensions/cpsection/network/model.py | 10 +++++++++ + extensions/cpsection/network/view.py | 37 +++++++++++++++++++++++++++++++++ + 2 files changed, 47 insertions(+), 0 deletions(-) + +diff --git a/extensions/cpsection/network/model.py b/extensions/cpsection/network/model.py +index 9592fdd..75f1d4c 100644 +--- a/extensions/cpsection/network/model.py ++++ b/extensions/cpsection/network/model.py +@@ -190,3 +190,13 @@ def set_ssids(ssids): + if len(ssid) > 0: + f.write(ssid + '\n') + f.close() ++ ++def launch_nm_connection_editor(): ++ environment = os.environ.copy() ++ environment['PATH'] = '%s:/usr/sbin' % (environment['PATH'], ) ++ ++ try: ++ subprocess.Popen(['-c', 'sudo nm-connection-editor'], ++ shell=True) ++ except: ++ _logger.exception('Error running nm-connection-editor') +diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py +index e55a48d..48ecfd1 100644 +--- a/extensions/cpsection/network/view.py ++++ b/extensions/cpsection/network/view.py +@@ -758,6 +758,40 @@ class Network(SectionView): + workspace.pack_start(proxy, expand=False) + proxy.show() + ++ separator_nm_connection_editor = gtk.HSeparator() ++ workspace.pack_start(separator_nm_connection_editor, False) ++ separator_nm_connection_editor.show() ++ ++ label_nm_connection_editor = gtk.Label(_('NetworkManager' ++ ' Connection Editor')) ++ label_nm_connection_editor.set_alignment(0, 0) ++ workspace.pack_start(label_nm_connection_editor, expand=False) ++ label_nm_connection_editor.show() ++ ++ box_nm_connection_editor = gtk.VBox() ++ box_nm_connection_editor.set_border_width(style.DEFAULT_SPACING * 2) ++ box_nm_connection_editor.set_spacing(style.DEFAULT_SPACING) ++ ++ info = gtk.Label(_("Press the 'Launch' button, to launch the" ++ " Connection Editor.")) ++ info.set_alignment(0, 0) ++ info.set_line_wrap(True) ++ box_nm_connection_editor.pack_start(info, expand=False) ++ ++ launch_button = gtk.Button() ++ launch_button.set_alignment(0, 0) ++ launch_button.set_label(_('Launch')) ++ launch_button.connect('clicked', self.__launch_button_clicked_cb) ++ box_launch_button = gtk.HBox() ++ box_launch_button.set_homogeneous(False) ++ box_launch_button.pack_start(launch_button, expand=False) ++ box_launch_button.show_all() ++ ++ box_nm_connection_editor.pack_start(box_launch_button, expand=False) ++ workspace.pack_start(box_nm_connection_editor, expand=False) ++ box_nm_connection_editor.show_all() ++ ++ + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled.add_with_viewport(workspace) +@@ -859,3 +893,6 @@ class Network(SectionView): + def __save_button_clicked_cb(self, save_button): + self._model.set_ssids(self._widget_table._get_entries()) + save_button.set_sensitive(False) ++ ++ def __launch_button_clicked_cb(self, launch_button): ++ self._model.launch_nm_connection_editor() +-- +1.7.4.4 + diff --git a/rpms/sugar/0081-au-1018-Pick-up-whatever-Modem-Parameters-are-entere.patch b/rpms/sugar/0081-au-1018-Pick-up-whatever-Modem-Parameters-are-entere.patch new file mode 100644 index 0000000..a9a93aa --- /dev/null +++ b/rpms/sugar/0081-au-1018-Pick-up-whatever-Modem-Parameters-are-entere.patch @@ -0,0 +1,74 @@ +From 1121ebc279df86f3b5d398d1b6a60ed3aad333d5 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Mon, 6 Feb 2012 19:21:14 +0530 +Subject: [PATCH 81/82] au#1018: Pick up whatever Modem-Parameters are entered + (in "modemconfiguration" CP). +Organization: Sugar Labs Foundation + +--- + src/jarabe/model/network.py | 48 ++++++++++++++++++++++-------------------- + 1 files changed, 25 insertions(+), 23 deletions(-) + +diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py +index 84e3666..0f727e6 100644 +--- a/src/jarabe/model/network.py ++++ b/src/jarabe/model/network.py +@@ -945,30 +945,32 @@ def load_gsm_connection(): + pin = client.get_string(GSM_PIN_PATH) or '' + puk = client.get_string(GSM_PUK_PATH) or '' + +- if username and number and apn: +- settings = SettingsGsm() +- settings.gsm.username = username +- settings.gsm.number = number +- settings.gsm.apn = apn +- +- secrets = SecretsGsm() +- secrets.pin = pin +- secrets.puk = puk +- secrets.password = password +- +- settings.connection.id = 'gsm' +- settings.connection.type = NM_CONNECTION_TYPE_GSM +- uuid = settings.connection.uuid = unique_id() +- settings.connection.autoconnect = False +- settings.ip4_config.method = 'auto' +- settings.serial.baud = _BAUD_RATE ++ logging.warning('Trying to establish 3G GSM connection. However,' ++ ' the connection may fail due to invalid/missing' ++ ' parameters, with NetworkManager giving' ++ ' error-code. For more details, see' ++ ' "http://dev.laptop.org.au/issues/1018"') ++ settings = SettingsGsm() ++ settings.gsm.username = username ++ settings.gsm.number = number ++ settings.gsm.apn = apn ++ ++ secrets = SecretsGsm() ++ secrets.pin = pin ++ secrets.puk = puk ++ secrets.password = password ++ ++ settings.connection.id = 'gsm' ++ settings.connection.type = NM_CONNECTION_TYPE_GSM ++ uuid = settings.connection.uuid = unique_id() ++ settings.connection.autoconnect = False ++ settings.ip4_config.method = 'auto' ++ settings.serial.baud = _BAUD_RATE + +- try: +- add_connection(uuid, settings, secrets) +- except Exception: +- logging.exception('Error adding gsm connection to NMSettings.') +- else: +- logging.warning('No gsm connection was set in GConf.') ++ try: ++ add_connection(uuid, settings, secrets) ++ except Exception: ++ logging.exception('Error adding gsm connection to NMSettings.') + + + def load_connections(): +-- +1.7.4.4 + diff --git a/rpms/sugar/0082-uy-1242-Batch-Operations-on-Journal-Entries-Copy-Era.patch b/rpms/sugar/0082-uy-1242-Batch-Operations-on-Journal-Entries-Copy-Era.patch new file mode 100644 index 0000000..7ca7118 --- /dev/null +++ b/rpms/sugar/0082-uy-1242-Batch-Operations-on-Journal-Entries-Copy-Era.patch @@ -0,0 +1,1301 @@ +From 11e637a70285331df58fca51af573d5761828374 Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Mon, 6 Feb 2012 01:29:12 +0530 +Subject: [PATCH 82/82] uy#1242: Batch Operations on Journal Entries (Copy, + Erase) +Organization: Sugar Labs Foundation + +--- + src/jarabe/journal/journalactivity.py | 135 ++++++++- + src/jarabe/journal/journaltoolbox.py | 176 ++++++++++- + src/jarabe/journal/listmodel.py | 13 + + src/jarabe/journal/listview.py | 48 +++ + src/jarabe/journal/model.py | 13 +- + src/jarabe/journal/palettes.py | 574 +++++++++++++++++++++++++++------ + 6 files changed, 844 insertions(+), 115 deletions(-) + +diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py +index 8cafef0..6f03a73 100644 +--- a/src/jarabe/journal/journalactivity.py ++++ b/src/jarabe/journal/journalactivity.py +@@ -1,5 +1,9 @@ + # Copyright (C) 2006, Red Hat, Inc. + # Copyright (C) 2007, One Laptop Per Child ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # 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 +@@ -17,15 +21,18 @@ + + import logging + from gettext import gettext as _ ++from gettext import ngettext + import uuid + + import gtk + import dbus + import statvfs + import os ++import gobject + + from sugar.graphics.window import Window +-from sugar.graphics.alert import ErrorAlert ++from sugar.graphics.alert import Alert, ErrorAlert ++from sugar.graphics.icon import Icon + + from sugar.bundle.bundle import ZipExtractException, RegistrationException + from sugar import env +@@ -34,7 +41,9 @@ from sugar import wm + + from jarabe.model import bundleregistry + from jarabe.journal.journaltoolbox import MainToolbox, DetailToolbox ++from jarabe.journal.journaltoolbox import EditToolbox + from jarabe.journal.listview import ListView ++from jarabe.journal.listmodel import ListModel + from jarabe.journal.detailview import DetailView + from jarabe.journal.volumestoolbar import VolumesToolbar + from jarabe.journal import misc +@@ -53,6 +62,7 @@ _SPACE_TRESHOLD = 52428800 + _BUNDLE_ID = 'org.laptop.JournalActivity' + + _journal = None ++_mount_point = None + + + class JournalActivityDBusService(dbus.service.Object): +@@ -119,8 +129,15 @@ class JournalActivity(JournalWindow): + self._list_view = None + self._detail_view = None + self._main_toolbox = None ++ self._edit_toolbox = None + self._detail_toolbox = None + self._volumes_toolbar = None ++ self._editing_mode = False ++ self._editing_alert = None ++ self._info_alert = None ++ self._selected_entries = [] ++ ++ set_mount_point('/') + + self._setup_main_view() + self._setup_secondary_view() +@@ -184,7 +201,7 @@ class JournalActivity(JournalWindow): + search_toolbar = self._main_toolbox.search_toolbar + search_toolbar.connect('query-changed', self._query_changed_cb) + search_toolbar.set_mount_point('/') +- self._mount_point = '/' ++ set_mount_point('/') + + def _setup_secondary_view(self): + self._secondary_view = gtk.VBox() +@@ -217,9 +234,13 @@ class JournalActivity(JournalWindow): + self.show_main_view() + + def show_main_view(self): +- if self.toolbar_box != self._main_toolbox: +- self.set_toolbar_box(self._main_toolbox) +- self._main_toolbox.show() ++ if self._editing_mode: ++ toolbox = EditToolbox() ++ else: ++ toolbox = self._main_toolbox ++ ++ self.set_toolbar_box(toolbox) ++ toolbox.show() + + if self.canvas != self._main_view: + self.set_canvas(self._main_view) +@@ -254,7 +275,7 @@ class JournalActivity(JournalWindow): + def __volume_changed_cb(self, volume_toolbar, mount_point): + logging.debug('Selected volume: %r.', mount_point) + self._main_toolbox.search_toolbar.set_mount_point(mount_point) +- self._mount_point = mount_point ++ set_mount_point(mount_point) + self._main_toolbox.set_current_toolbar(0) + + def __model_created_cb(self, sender, **kwargs): +@@ -364,8 +385,98 @@ class JournalActivity(JournalWindow): + self.show_main_view() + self.search_grab_focus() + +- def get_mount_point(self): +- return self._mount_point ++ def switch_to_editing_mode(self, switch): ++ # (re)-switch, only if not already. ++ if (switch) and (not self._editing_mode): ++ self._editing_mode = True ++ self.show_main_view() ++ elif (not switch) and (self._editing_mode): ++ self._editing_mode = False ++ self.show_main_view() ++ ++ def get_list_view(self): ++ return self._list_view ++ ++ def remove_editing_alert(self): ++ if self._editing_alert is not None: ++ self.remove_alert(self._editing_alert) ++ self._editing_alert = None ++ ++ def add_editing_alert(self, widget_clicked, title, message, operation, ++ callback): ++ cancel_icon = Icon(icon_name='dialog-cancel') ++ ok_icon = Icon(icon_name='dialog-ok') ++ ++ alert = Alert() ++ alert.props.title = title ++ alert.props.msg = message ++ alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), cancel_icon) ++ alert.add_button(gtk.RESPONSE_OK, operation, ok_icon) ++ alert.connect('response', self.__check_for_action, callback) ++ alert.show() ++ ++ self.remove_editing_alert() ++ ++ self._editing_alert = alert ++ self.add_alert(alert) ++ ++ def __check_for_action(self, alert, response_id, callback): ++ self.remove_editing_alert() ++ if response_id == gtk.RESPONSE_OK: ++ gobject.idle_add(callback, None) ++ ++ def remove_info_alert(self): ++ if self._info_alert is not None: ++ self.remove_alert(self._info_alert) ++ logging.debug('alert removed') ++ self._info_alert = None ++ ++ def add_info_alert(self, button, title, message, show_skip_options, ++ callback, data): ++ skip_icon = Icon(icon_name='dialog-cancel') ++ skip_all_icon = Icon(icon_name='dialog-cancel') ++ ++ alert = Alert() ++ alert.props.title = title ++ alert.props.msg = message ++ ++ if show_skip_options: ++ alert.add_button(gtk.RESPONSE_CANCEL, _('OK'), skip_icon) ++ ++ # Let the user explicitly see each message of the ++ # non-operatable entry. ++ #alert.add_button(gtk.RESPONSE_OK, _('Do not notify again'), skip_all_icon) ++ ++ alert.connect('response', self.__check_for_skip_action, ++ callback, data) ++ ++ alert.show() ++ self.remove_info_alert() ++ self._info_alert = alert ++ ++ if show_skip_options: ++ self.add_alert(alert) ++ else: ++ self.add_alert_and_callback(alert, callback, data) ++ ++ def __check_for_skip_action(self, alert, response_id, callback, ++ metadata): ++ self.remove_editing_alert() ++ if response_id == gtk.RESPONSE_OK: ++ gobject.idle_add(callback, True, metadata) ++ elif response_id == gtk.RESPONSE_CANCEL: ++ gobject.idle_add(callback, False, metadata) ++ ++ def get_metadata_list(self, selected_state): ++ metadata_list = [] ++ ++ list_view_model = self.get_list_view().get_model() ++ for index in range(0, len(list_view_model)): ++ metadata = list_view_model.get_metadata(index) ++ if metadata.get('selected', '0') == selected_state: ++ metadata_list.append(metadata) ++ ++ return metadata_list + + + def get_journal(): +@@ -378,3 +489,11 @@ def get_journal(): + + def start(): + get_journal() ++ ++ ++def set_mount_point(mount_point): ++ global _mount_point ++ _mount_point = mount_point ++ ++def get_mount_point(): ++ return _mount_point +diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py +index 2aa4153..7e1419a 100644 +--- a/src/jarabe/journal/journaltoolbox.py ++++ b/src/jarabe/journal/journaltoolbox.py +@@ -1,5 +1,9 @@ + # Copyright (C) 2007, One Laptop Per Child + # Copyright (C) 2009, Walter Bender ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by +@@ -16,6 +20,7 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + from gettext import gettext as _ ++from gettext import ngettext + import logging + from datetime import datetime, timedelta + import os +@@ -43,8 +48,7 @@ from sugar import mime + from jarabe.model import bundleregistry + from jarabe.journal import misc + from jarabe.journal import model +-from jarabe.journal.palettes import ClipboardMenu +-from jarabe.journal.palettes import VolumeMenu ++from jarabe.journal import palettes + + + _AUTOSEARCH_TIMEOUT = 1000 +@@ -63,6 +67,8 @@ _ACTION_MY_FRIENDS = 1 + _ACTION_MY_CLASS = 2 + + ++COPY_MENU_HELPER = palettes.get_copy_menu_helper() ++ + class MainToolbox(Toolbox): + def __init__(self): + Toolbox.__init__(self) +@@ -527,6 +533,172 @@ class EntryToolbar(gtk.Toolbar): + menu_item.show() + + ++class EditToolbox(Toolbox): ++ def __init__(self): ++ Toolbox.__init__(self) ++ ++ self.edit_toolbar = EditToolbar() ++ self.add_toolbar('', self.edit_toolbar) ++ self.edit_toolbar.show() ++ ++ ++class EditToolbar(gtk.Toolbar): ++ def __init__(self): ++ gtk.Toolbar.__init__(self) ++ ++ self.add(SelectNoneButton()) ++ self.add(SelectAllButton()) ++ self.add(gtk.SeparatorToolItem()) ++ self.add(BatchEraseButton()) ++ self.add(BatchCopyButton()) ++ ++ self.show_all() ++ ++ ++class SelectNoneButton(ToolButton, palettes.ActionItem): ++ def __init__(self): ++ ToolButton.__init__(self, 'select-none') ++ palettes.ActionItem.__init__(self, '', None, ++ show_editing_alert=False, ++ show_progress_info_alert=True, ++ batch_mode=True, ++ need_to_popup_options=False, ++ operate_on_deselected_entries=False, ++ switch_to_normal_mode_after_completion=True) ++ self.props.tooltip = _('Select none') ++ ++ def _get_actionable_signal(self): ++ return 'clicked' ++ ++ def _get_info_alert_title(self): ++ return _('Deselecting') ++ ++ def _operate(self, metadata): ++ metadata['selected'] = '0' ++ model.write(metadata, update_mtime=False) ++ ++ # This is sync-operation. Thus, call the callback. ++ self._post_operate_per_metadata_per_action(metadata) ++ ++ ++class SelectAllButton(ToolButton, palettes.ActionItem): ++ def __init__(self): ++ ToolButton.__init__(self, 'select-all') ++ palettes.ActionItem.__init__(self, '', None, ++ show_editing_alert=False, ++ show_progress_info_alert=True, ++ batch_mode=True, ++ need_to_popup_options=False, ++ operate_on_deselected_entries=True, ++ switch_to_normal_mode_after_completion=False) ++ self.props.tooltip = _('Select all') ++ ++ def _get_actionable_signal(self): ++ return 'clicked' ++ ++ def _get_info_alert_title(self): ++ return _('Selecting') ++ ++ def _operate(self, metadata): ++ metadata['selected'] = '1' ++ ++ file_path = model.get_file(metadata['uid']) ++ if not file_path or not os.path.exists(file_path): ++ logging.warn('Entries without a file cannot be copied.') ++ error_message = _('Entries without a file cannot be copied.') ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self.emit('volume-error', error_message, _('Warning')) ++ return ++ ++ model.write(metadata, update_mtime=False) ++ ++ # This is sync-operation. Thus, call the callback. ++ self._post_operate_per_metadata_per_action(metadata) ++ ++ ++class BatchEraseButton(ToolButton, palettes.ActionItem): ++ def __init__(self): ++ ToolButton.__init__(self, 'edit-delete') ++ palettes.ActionItem.__init__(self, '', None, ++ show_editing_alert=True, ++ show_progress_info_alert=True, ++ batch_mode=True, ++ need_to_popup_options=False, ++ operate_on_deselected_entries=False, ++ switch_to_normal_mode_after_completion=True) ++ self.props.tooltip = _('Erase') ++ ++ def _get_actionable_signal(self): ++ return 'clicked' ++ ++ def _get_editing_alert_title(self): ++ return _('Erase') ++ ++ def _get_editing_alert_message(self, entries_len): ++ return ngettext('Do you want to erase %d entry?', ++ 'Do you want to erase %d entries?', ++ entries_len) % (entries_len) ++ ++ def _get_editing_alert_operation(self): ++ return _('Erase') ++ ++ def _get_info_alert_title(self): ++ return _('Erasing') ++ ++ def _operate(self, metadata): ++ model.delete(metadata['uid']) ++ ++ # This is sync-operation. Thus, call the callback. ++ self._post_operate_per_metadata_per_action(metadata) ++ ++ ++class BatchCopyButton(ToolButton, palettes.ActionItem): ++ def __init__(self): ++ ToolButton.__init__(self, 'edit-copy') ++ palettes.ActionItem.__init__(self, '', None, ++ show_editing_alert=True, ++ show_progress_info_alert=True, ++ batch_mode=True, ++ need_to_popup_options=True, ++ operate_on_deselected_entries=False, ++ switch_to_normal_mode_after_completion=True) ++ ++ self.props.tooltip = _('Copy') ++ ++ self._metadata_list = None ++ ++ def _get_actionable_signal(self): ++ return 'clicked' ++ ++ def _fill_and_pop_up_options(self, widget_clicked): ++ for child in self.props.palette.menu.get_children(): ++ self.props.palette.menu.remove(child) ++ ++ COPY_MENU_HELPER.insert_copy_to_menu_items(self.props.palette.menu, ++ None, ++ show_editing_alert=True, ++ show_progress_info_alert=True, ++ batch_mode=True) ++ self.props.palette.popup(immediate=True, state=1) ++ ++ ++ ++ ++ ++ ++ ++ ++ ++class EditCopyItem(MenuItem): ++ __gtype_name__ = 'JournalEditCopyItem' ++ ++ def __init__(self, icon_name, text_label, mount_path): ++ MenuItem.__init__(self, icon_name=icon_name, text_label=text_label) ++ self.mount_path = mount_path ++ self.mount_info = text_label ++ + class SortingButton(ToolButton): + __gtype_name__ = 'JournalSortingButton' + +diff --git a/src/jarabe/journal/listmodel.py b/src/jarabe/journal/listmodel.py +index 417ff61..a07f897 100644 +--- a/src/jarabe/journal/listmodel.py ++++ b/src/jarabe/journal/listmodel.py +@@ -1,4 +1,8 @@ + # Copyright (C) 2009, Tomeu Vizoso ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # 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 +@@ -54,6 +58,7 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource): + COLUMN_BUDDY_1 = 9 + COLUMN_BUDDY_2 = 10 + COLUMN_BUDDY_3 = 11 ++ COLUMN_SELECT = 12 + + _COLUMN_TYPES = { + COLUMN_UID: str, +@@ -68,6 +73,7 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource): + COLUMN_BUDDY_1: object, + COLUMN_BUDDY_3: object, + COLUMN_BUDDY_2: object, ++ COLUMN_SELECT: bool, + } + + _PAGE_SIZE = 10 +@@ -198,6 +204,13 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource): + + self._cached_row.append(None) + ++ # If an entry was already selected, switch to editing mode. ++ if metadata.get('selected', '0') == '1': ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().switch_to_editing_mode(True) ++ ++ self._cached_row.append(metadata.get('selected', '0') == '1') ++ + return self._cached_row[column] + + def on_iter_nth_child(self, iterator, n): +diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py +index a0ceccc..2aa85ae 100644 +--- a/src/jarabe/journal/listview.py ++++ b/src/jarabe/journal/listview.py +@@ -1,4 +1,8 @@ + # Copyright (C) 2009, Tomeu Vizoso ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # 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 +@@ -98,6 +102,8 @@ class BaseListView(gtk.Bin): + self._title_column = None + self.sort_column = None + self._add_columns() ++ self._inhibit_refresh = False ++ self._selected_entries = 0 + + self.tree_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, + [('text/uri-list', 0, 0), +@@ -134,6 +140,18 @@ class BaseListView(gtk.Bin): + return object_id.startswith(self._query['mountpoints'][0]) + + def _add_columns(self): ++ cell_select = gtk.CellRendererToggle() ++ cell_select.props.indicator_size = style.zoom(26) ++ cell_select.props.activatable = True ++ cell_select.connect('toggled', self.__selected_cb) ++ ++ column = gtk.TreeViewColumn() ++ column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED ++ column.props.fixed_width = style.GRID_CELL_SIZE ++ column.pack_start(cell_select) ++ column.add_attribute(cell_select, "active", ListModel.COLUMN_SELECT) ++ self.tree_view.append_column(column) ++ + cell_favorite = CellRendererFavorite(self.tree_view) + cell_favorite.connect('clicked', self.__favorite_clicked_cb) + +@@ -251,6 +269,25 @@ class BaseListView(gtk.Bin): + else: + cell.props.xo_color = None + ++ def __selected_cb(self, cell, path): ++ from jarabe.journal.journalactivity import get_journal ++ journal = get_journal() ++ ++ row = self._model[path] ++ metadata = model.get(row[ListModel.COLUMN_UID]) ++ if metadata.get('selected', '0') == '1': ++ metadata['selected'] = '0' ++ self._selected_entries = self._selected_entries - 1 ++ if self._selected_entries < 1: ++ journal.switch_to_editing_mode(False) ++ else: ++ metadata['selected'] = '1' ++ self._selected_entries = self._selected_entries + 1 ++ if self._selected_entries > 0: ++ journal.switch_to_editing_mode(True) ++ ++ model.write(metadata, update_mtime=False) ++ + def __favorite_clicked_cb(self, cell, path): + row = self._model[path] + metadata = model.get(row[ListModel.COLUMN_UID]) +@@ -274,9 +311,14 @@ class BaseListView(gtk.Bin): + ListModel.COLUMN_TIMESTAMP)) + self._query = query_dict + ++ # This refresh is always needed, since the query has changed. + self.refresh() + + def refresh(self): ++ if not self._inhibit_refresh: ++ self.proceed_with_refresh() ++ ++ def proceed_with_refresh(self): + logging.debug('ListView.refresh query %r', self._query) + self._stop_progress_bar() + +@@ -466,6 +508,12 @@ class BaseListView(gtk.Bin): + self.update_dates() + return True + ++ def get_model(self): ++ return self._model ++ ++ def inhibit_refresh(self, inhibit): ++ self._inhibit_refresh = inhibit ++ + + class ListView(BaseListView): + __gtype_name__ = 'JournalListView' +diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py +index 5285a7c..39547a4 100644 +--- a/src/jarabe/journal/model.py ++++ b/src/jarabe/journal/model.py +@@ -1,4 +1,8 @@ + # Copyright (C) 2007-2011, One Laptop per Child ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # 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 +@@ -37,7 +41,6 @@ from sugar import dispatch + from sugar import mime + from sugar import util + +- + DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore' + DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore' + DS_DBUS_PATH = '/org/laptop/sugar/DataStore' +@@ -45,7 +48,8 @@ DS_DBUS_PATH = '/org/laptop/sugar/DataStore' + # Properties the journal cares about. + PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id', + 'creation_time', 'filesize', 'icon-color', 'keep', 'mime_type', +- 'mountpoint', 'mtime', 'progress', 'timestamp', 'title', 'uid'] ++ 'mountpoint', 'mtime', 'progress', 'timestamp', 'title', ++ 'uid', 'selected'] + + MIN_PAGES_TO_CACHE = 3 + MAX_PAGES_TO_CACHE = 5 +@@ -651,6 +655,11 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): + file_path, + transfer_ownership) + else: ++ # HACK: For documents: modify the mount-point ++ from jarabe.journal.journalactivity import get_mount_point ++ if get_mount_point() == get_documents_path(): ++ metadata['mountpoint'] = get_documents_path() ++ + object_id = _write_entry_on_external_device(metadata, file_path) + + return object_id +diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py +index 27b0b54..2916a14 100644 +--- a/src/jarabe/journal/palettes.py ++++ b/src/jarabe/journal/palettes.py +@@ -1,4 +1,8 @@ + # Copyright (C) 2008 One Laptop Per Child ++# Copyright (C) 2012, Walter Bender <walter@sugarlabs.org> ++# Copyright (C) 2012, Gonzalo Odiard <gonzalo@laptop.org> ++# Copyright (C) 2012, Martin Abente <tch@sugarlabs.org> ++# Copyright (C) 2012, Ajay Garg <ajay@activitycentral.com> + # + # 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 +@@ -15,6 +19,7 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + from gettext import gettext as _ ++from gettext import ngettext + import logging + import os + +@@ -23,6 +28,9 @@ import gtk + import gconf + import gio + import glib ++import time ++ ++from sugar import _sugarext + + from sugar.graphics import style + from sugar.graphics.palette import Palette +@@ -39,6 +47,8 @@ from jarabe.journal import model + + friends_model = friends.get_model() + ++_copy_menu_helper = None ++ + + class BulkOperationDetails(): + +@@ -129,8 +139,18 @@ class ObjectPalette(Palette): + menu_item.set_image(icon) + self.menu.append(menu_item) + menu_item.show() +- copy_menu = CopyMenu(metadata) ++ copy_menu = CopyMenu() ++ copy_menu_helper = get_copy_menu_helper() ++ ++ metadata_list = [] ++ metadata_list.append(metadata) ++ copy_menu_helper.insert_copy_to_menu_items(copy_menu, ++ metadata_list, ++ False, ++ False, ++ False) + copy_menu.connect('volume-error', self.__volume_error_cb) ++ copy_menu_helper.connect('volume-error', self.__volume_error_cb) + menu_item.set_submenu(copy_menu) + + if self._metadata['mountpoint'] == '/': +@@ -260,156 +280,419 @@ class CopyMenu(gtk.Menu): + ([str, str])), + } + +- def __init__(self, metadata): ++ def __init__(self): + gobject.GObject.__init__(self) + +- self._metadata = metadata + +- clipboard_menu = ClipboardMenu(self._metadata) +- clipboard_menu.set_image(Icon(icon_name='toolbar-edit', +- icon_size=gtk.ICON_SIZE_MENU)) +- clipboard_menu.connect('volume-error', self.__volume_error_cb) +- self.append(clipboard_menu) +- clipboard_menu.show() ++class ActionItem(gobject.GObject): ++ """ ++ This class implements the course of actions that happens when clicking ++ upon an Action-Item (eg. Batch-Copy-Toolbar-button; ++ Actual-Batch-Copy-To-Journal-button; ++ Actual-Batch-Copy-To-Documents-button; ++ Actual-Batch-Copy-To-Mounted-Drive-button; ++ Actual-Batch-Copy-To-Clipboard-button; ++ Single-Copy-To-Journal-button; ++ Single-Copy-To-Documents-button; ++ Single-Copy-To-Mounted-Drive-button; ++ Single-Copy-To-Clipboard-button; ++ Batch-Erase-Button; ++ Select-None-Toolbar-button; ++ Select-All-Toolbar-button ++ """ + +- from jarabe.journal import journalactivity +- journal_model = journalactivity.get_journal() +- if journal_model.get_mount_point() != model.get_documents_path(): +- documents_menu = DocumentsMenu(self._metadata) +- documents_menu.set_image(Icon(icon_name='user-documents', +- icon_size=gtk.ICON_SIZE_MENU)) +- documents_menu.connect('volume-error', self.__volume_error_cb) +- self.append(documents_menu) +- documents_menu.show() ++ def __init__(self, label, metadata_list, show_editing_alert, ++ show_progress_info_alert, batch_mode, ++ need_to_popup_options, ++ operate_on_deselected_entries, ++ switch_to_normal_mode_after_completion): ++ gobject.GObject.__init__(self) + +- if self._metadata['mountpoint'] != '/': +- client = gconf.client_get_default() +- color = XoColor(client.get_string('/desktop/sugar/user/color')) +- journal_menu = VolumeMenu(self._metadata, _('Journal'), '/') +- journal_menu.set_image(Icon(icon_name='activity-journal', +- xo_color=color, +- icon_size=gtk.ICON_SIZE_MENU)) +- journal_menu.connect('volume-error', self.__volume_error_cb) +- self.append(journal_menu) +- journal_menu.show() ++ self._label = label ++ self._metadata_list = metadata_list ++ self._show_progress_info_alert = show_progress_info_alert ++ self._batch_mode = batch_mode ++ self._operate_on_deselected_entries = \ ++ operate_on_deselected_entries ++ self._switch_to_normal_mode_after_completion = \ ++ switch_to_normal_mode_after_completion + +- volume_monitor = gio.volume_monitor_get() +- icon_theme = gtk.icon_theme_get_default() +- for mount in volume_monitor.get_mounts(): +- if self._metadata['mountpoint'] == mount.get_root().get_path(): +- continue +- volume_menu = VolumeMenu(self._metadata, mount.get_name(), +- mount.get_root().get_path()) +- for name in mount.get_icon().props.names: +- if icon_theme.has_icon(name): +- volume_menu.set_image(Icon(icon_name=name, +- icon_size=gtk.ICON_SIZE_MENU)) +- break +- volume_menu.connect('volume-error', self.__volume_error_cb) +- self.append(volume_menu) +- volume_menu.show() ++ actionable_signal = self._get_actionable_signal() + +- def __volume_error_cb(self, menu_item, message, severity): +- self.emit('volume-error', message, severity) ++ if need_to_popup_options: ++ self.connect(actionable_signal, self._fill_and_pop_up_options) ++ else: ++ if show_editing_alert: ++ self.connect(actionable_signal, self._show_editing_alert) ++ else: ++ self.connect(actionable_signal, self._pre_operate_per_action) ++ ++ def _get_actionable_signal(self): ++ """ ++ Some widgets like 'buttons' have 'clicked' as actionable signal; ++ some like 'menuitems' have 'activate' as actionable signal. ++ """ ++ ++ raise NotImplementedError ++ ++ def _fill_and_pop_up_options(self): ++ """ ++ Eg. Batch-Copy-Toolbar-button does not do anything by itself ++ useful; but rather pops-up the actual 'copy-to' options. ++ """ ++ ++ raise NotImplementedError ++ ++ def _show_editing_alert(self, widget_clicked): ++ """ ++ Upon clicking the actual operation button (eg. ++ Batch-Erase-Button and Batch-Copy-To-Clipboard button; BUT NOT ++ Batch-Copy-Toolbar-button, since it does not do anything ++ actually useful, but only pops-up the actual 'copy-to' options. ++ """ ++ ++ alert_parameters = self._get_editing_alert_parameters() ++ title = alert_parameters[0] ++ message = alert_parameters[1] ++ operation = alert_parameters[2] ++ ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().add_editing_alert(None, title, message, operation, ++ self._pre_operate_per_action) ++ ++ def _get_editing_alert_parameters(self): ++ """ ++ Get the alert parameters for widgets that can show editing ++ alert. ++ """ ++ ++ # For batch-operations, fetch the metadata-list. ++ if self._batch_mode: ++ self._metadata_list = self._get_metadata_list() ++ entries_len = len(self._metadata_list) ++ ++ title = self._get_editing_alert_title() ++ message = self._get_editing_alert_message(entries_len) ++ operation = self._get_editing_alert_operation() ++ ++ return (title, message, operation) ++ ++ def _get_metadata_list(self): ++ """ ++ Get the metadata list, according to button-type. For eg, ++ Select-All-Toolbar-button operates on non-selected entries; ++ while othere operate on selected-entries. ++ """ ++ ++ from jarabe.journal.journalactivity import get_journal ++ journal = get_journal() ++ ++ if self._operate_on_deselected_entries: ++ return journal.get_metadata_list('0') ++ else: ++ return journal.get_metadata_list('1') ++ ++ def _get_editing_alert_title(self): ++ raise NotImplementedError ++ ++ def _get_editing_alert_message(self, entries_len): ++ raise NotImplementedError ++ ++ def _get_editing_alert_operation(self): ++ raise NotImplementedError ++ ++ def _is_metadata_list_empty(self): ++ return (self._metadata_list is None) or \ ++ (len(self._metadata_list) == 0) ++ ++ def _pre_operate_per_action(self, obj): ++ """ ++ This is the stage, just before the FIRST metadata gets into its ++ processing cycle. ++ """ ++ ++ self._skip_all = False ++ ++ # For batch-operations, fetch the metadata list again. ++ if (self._batch_mode): ++ self._metadata_list = self._get_metadata_list() ++ ++ # Set the initial length of metadata-list. ++ self._metadata_list_initial_len = len(self._metadata_list) ++ ++ # Next, proceed with the metadata ++ self._pre_operate_per_metadata_per_action() ++ ++ def _pre_operate_per_metadata_per_action(self): ++ """ ++ This is the stage, just before EVERY metadata gets into doing ++ its actual work. ++ """ ++ ++ # If there is still some metadata left, proceed with the ++ # metadata operation. ++ # Else, proceed to post-operations. ++ if len(self._metadata_list) > 0: ++ metadata = self._metadata_list.pop(0) ++ ++ # De-select the entry. ++ metadata['selected'] = '0' ++ model.write(metadata, update_mtime=False) ++ ++ # If info-alert needs to be shown, show the alert, and ++ # arrange for actual operation. ++ # Else, proceed to actual operation directly. ++ if self._show_progress_info_alert: ++ current_len = len(self._metadata_list) ++ ++ # TRANS: Do not translate the two %d, and the %s. ++ info_alert_message = _('( %d / %d ) %s') % ( ++ self._metadata_list_initial_len - current_len, ++ self._metadata_list_initial_len, metadata['title']) ++ ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().add_info_alert(None, ++ self._get_info_alert_title() + ' ...', ++ info_alert_message, False, ++ self._operate_per_metadata_per_action, ++ metadata) ++ else: ++ self._operate_per_metadata_per_action(metadata) ++ else: ++ self._post_operate_per_action() ++ ++ def _get_info_alert_title(self): ++ raise NotImplementedError ++ ++ def _operate_per_metadata_per_action(self, metadata): ++ """ ++ This is just a code-convenient-function, which allows ++ runtime-overriding. It just delegates to the actual ++ "self._operate" method, the actual which is determined at ++ runtime. ++ """ ++ ++ # Pass the callback for the post-operation-for-metadata. This ++ # will ensure that async-operations on the metadata are taken ++ # care of. ++ self._operate(metadata) ++ ++ def _operate(self, metadata): ++ """ ++ Actual, core, productive stage for EVERY metadata. ++ """ ++ ++ raise NotImplementedError ++ ++ def _post_operate_per_metadata_per_action(self, metadata): ++ """ ++ This is the stage, just after EVERY metadata has been ++ processed. ++ """ ++ ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().remove_info_alert() ++ ++ # Call the next ... ++ self._pre_operate_per_metadata_per_action() ++ ++ def _post_operate_per_action(self): ++ """ ++ This is the stage, just after the LAST metadata has been ++ processed. ++ """ ++ ++ # Switch to non-editing mode (if applicable), after the operation is complete ++ if self._switch_to_normal_mode_after_completion: ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().switch_to_editing_mode(False) ++ ++ def _inhibit_refresh(self, inhibit): ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().get_list_view().inhibit_refresh(inhibit) ++ ++ def _refresh(self): ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().get_list_view().refresh() ++ ++ def _handle_error_alert(self, error_message, metadata): ++ """ ++ This handles any error scenarios. Examples are of entries that ++ display the message "Entries without a file cannot be copied." ++ This is kind of controller-functionl the model-function is ++ "self._set_error_info_alert". ++ """ ++ ++ if self._skip_all: ++ self._post_operate_per_metadata_per_action(metadata) ++ else: ++ self._set_error_info_alert(error_message, metadata) ++ ++ def _set_error_info_alert(self, error_message, metadata): ++ """ ++ This method displays the error alert. ++ """ ++ ++ current_len = len(self._metadata_list) + ++ # TRANS: Do not translate the two %d, and the three %s. ++ info_alert_message = _('( %d / %d ) Error while %s %s : %s') % ( ++ self._metadata_list_initial_len - current_len, ++ self._metadata_list_initial_len, ++ self._get_info_alert_title(), ++ metadata['title'], ++ error_message) + +-class VolumeMenu(MenuItem): +- __gtype_name__ = 'JournalVolumeMenu' ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().add_info_alert(None, ++ self._get_info_alert_title() + ' ...', ++ info_alert_message, True, ++ self._process_error_skipping, ++ metadata) + ++ def _process_error_skipping(self, skip_all, metadata): ++ if skip_all: ++ self._skip_all = True ++ ++ # The operation for the current metadata is finished (kinda ++ # pseudo ...) ++ self._post_operate_per_metadata_per_action(metadata) ++ ++ ++class BaseCopyMenuItem(MenuItem, ActionItem): + __gsignals__ = { +- 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, +- ([str, str])), +- } ++ 'volume-error': (gobject.SIGNAL_RUN_FIRST, ++ gobject.TYPE_NONE, ([str, str])), ++ } + +- def __init__(self, metadata, label, mount_point): ++ ++ def __init__(self, metadata_list, label, show_editing_alert, ++ show_progress_info_alert, batch_mode): + MenuItem.__init__(self, label) +- self._metadata = metadata +- self.connect('activate', self.__copy_to_volume_cb, mount_point) ++ ActionItem.__init__(self, label, metadata_list, show_editing_alert, ++ show_progress_info_alert, batch_mode, ++ need_to_popup_options=False, ++ operate_on_deselected_entries=False, ++ switch_to_normal_mode_after_completion=True) + +- def __copy_to_volume_cb(self, menu_item, mount_point): +- file_path = model.get_file(self._metadata['uid']) ++ def _get_actionable_signal(self): ++ return 'activate' ++ ++ def _get_editing_alert_title(self): ++ return _('Copy') ++ ++ def _get_editing_alert_message(self, entries_len): ++ return ngettext('Do you want to copy %d entry to %s?', ++ 'Do you want to copy %d entries to %s?', ++ entries_len) % (entries_len, self._label) ++ ++ def _get_editing_alert_operation(self): ++ return _('Copy') ++ ++ def _get_info_alert_title(self): ++ return _('Copying') ++ ++ ++class VolumeMenu(BaseCopyMenuItem): ++ def __init__(self, metadata_list, label, mount_point, ++ show_editing_alert, show_progress_info_alert, ++ batch_mode): ++ BaseCopyMenuItem.__init__(self, metadata_list, label, ++ show_editing_alert, ++ show_progress_info_alert, batch_mode) ++ self._mount_point = mount_point ++ ++ def _operate(self, metadata): ++ file_path = model.get_file(metadata['uid']) + + if not file_path or not os.path.exists(file_path): + logging.warn('Entries without a file cannot be copied.') +- self.emit('volume-error', +- _('Entries without a file cannot be copied.'), +- _('Warning')) ++ error_message = _('Entries without a file cannot be copied.') ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self.emit('volume-error', error_message, _('Warning')) + return + + try: +- model.copy(self._metadata, mount_point) ++ model.copy(metadata, self._mount_point) + except IOError, e: + logging.exception('Error while copying the entry. %s', e.strerror) +- self.emit('volume-error', +- _('Error while copying the entry. %s') % e.strerror, +- _('Error')) +- +- +-class ClipboardMenu(MenuItem): +- __gtype_name__ = 'JournalClipboardMenu' ++ error_message = _('Error while copying the entry. %s') % e.strerror ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self.emit('volume-error', error_message, _('Error')) ++ return + +- __gsignals__ = { +- 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, +- ([str, str])), +- } ++ # This is sync-operation. Thus, call the callback. ++ self._post_operate_per_metadata_per_action(metadata) + +- def __init__(self, metadata): +- MenuItem.__init__(self, _('Clipboard')) + +- self._temp_file_path = None +- self._metadata = metadata +- self.connect('activate', self.__copy_to_clipboard_cb) ++class ClipboardMenu(BaseCopyMenuItem): ++ def __init__(self, metadata_list, show_editing_alert, ++ show_progress_info_alert, batch_mode): ++ BaseCopyMenuItem.__init__(self, metadata_list, _('Clipboard'), ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ self._temp_file_path_list = [] + +- def __copy_to_clipboard_cb(self, menu_item): +- file_path = model.get_file(self._metadata['uid']) ++ def _operate(self, metadata): ++ file_path = model.get_file(metadata['uid']) + if not file_path or not os.path.exists(file_path): + logging.warn('Entries without a file cannot be copied.') +- self.emit('volume-error', +- _('Entries without a file cannot be copied.'), +- _('Warning')) ++ error_message = _('Entries without a file cannot be copied.') ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self.emit('volume-error', error_message, _('Warning')) + return + + clipboard = gtk.Clipboard() + clipboard.set_with_data([('text/uri-list', 0, 0)], + self.__clipboard_get_func_cb, +- self.__clipboard_clear_func_cb) ++ self.__clipboard_clear_func_cb, ++ metadata) + +- def __clipboard_get_func_cb(self, clipboard, selection_data, info, data): ++ def __clipboard_get_func_cb(self, clipboard, selection_data, info, ++ metadata): + # Get hold of a reference so the temp file doesn't get deleted +- self._temp_file_path = model.get_file(self._metadata['uid']) ++ self._temp_file_path = model.get_file(metadata['uid']) + logging.debug('__clipboard_get_func_cb %r', self._temp_file_path) + selection_data.set_uris(['file://' + self._temp_file_path]) + +- def __clipboard_clear_func_cb(self, clipboard, data): ++ def __clipboard_clear_func_cb(self, clipboard, metadata): + # Release and delete the temp file + self._temp_file_path = None + ++ # This is async-operation; and this is the ending point. ++ self._post_operate_per_metadata_per_action(metadata) + +-class DocumentsMenu(MenuItem): +- __gtype_name__ = 'JournalDocumentsMenu' +- +- __gsignals__ = { +- 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, +- ([str, str])), +- } +- +- def __init__(self, metadata): +- MenuItem.__init__(self, _('Documents')) + +- self._temp_file_path = None +- self._metadata = metadata +- self.connect('activate', self.__copy_to_documents_cb) ++class DocumentsMenu(BaseCopyMenuItem): ++ def __init__(self, metadata_list, show_editing_alert, ++ show_progress_info_alert, batch_mode): ++ BaseCopyMenuItem.__init__(self, metadata_list, _('Documents'), ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) + +- def __copy_to_documents_cb(self, menu_item): +- file_path = model.get_file(self._metadata['uid']) ++ def _operate(self, metadata): ++ file_path = model.get_file(metadata['uid']) + if not file_path or not os.path.exists(file_path): + logging.warn('Entries without a file cannot be copied.') +- self.emit('volume-error', +- _('Entries without a file cannot be copied.'), +- _('Warning')) ++ error_message = _('Entries without a file cannot be copied.') ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self.emit('volume-error', error_message, _('Warning')) + return + +- model.copy(self._metadata, model.get_documents_path()) ++ model.copy(metadata, model.get_documents_path()) ++ ++ # This is sync-operation. Call the post-operation now. ++ self._post_operate_per_metadata_per_action(metadata) + + + class GroupsMenu(gtk.Menu): +@@ -538,3 +821,88 @@ class BuddyPalette(Palette): + icon=buddy_icon) + + # TODO: Support actions on buddies, like make friend, invite, etc. ++ ++ ++ ++class CopyMenuHelper(gtk.Menu): ++ __gtype_name__ = 'JournalCopyMenuHelper' ++ ++ __gsignals__ = { ++ 'volume-error': (gobject.SIGNAL_RUN_FIRST, ++ gobject.TYPE_NONE, ++ ([str, str])), ++ } ++ ++ def insert_copy_to_menu_items(self, menu, metadata_list, ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode): ++ self._metadata_list = metadata_list ++ ++ clipboard_menu = ClipboardMenu(metadata_list, ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ clipboard_menu.set_image(Icon(icon_name='toolbar-edit', ++ icon_size=gtk.ICON_SIZE_MENU)) ++ clipboard_menu.connect('volume-error', self.__volume_error_cb) ++ menu.append(clipboard_menu) ++ clipboard_menu.show() ++ ++ from jarabe.journal.journalactivity import get_mount_point ++ ++ if get_mount_point() != model.get_documents_path(): ++ documents_menu = DocumentsMenu(metadata_list, ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ documents_menu.set_image(Icon(icon_name='user-documents', ++ icon_size=gtk.ICON_SIZE_MENU)) ++ documents_menu.connect('volume-error', self.__volume_error_cb) ++ menu.append(documents_menu) ++ documents_menu.show() ++ ++ if get_mount_point() != '/': ++ client = gconf.client_get_default() ++ color = XoColor(client.get_string('/desktop/sugar/user/color')) ++ journal_menu = VolumeMenu(metadata_list, _('Journal'), '/', ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ journal_menu.set_image(Icon(icon_name='activity-journal', ++ xo_color=color, ++ icon_size=gtk.ICON_SIZE_MENU)) ++ journal_menu.connect('volume-error', self.__volume_error_cb) ++ menu.append(journal_menu) ++ journal_menu.show() ++ ++ volume_monitor = gio.volume_monitor_get() ++ icon_theme = gtk.icon_theme_get_default() ++ for mount in volume_monitor.get_mounts(): ++ if get_mount_point() == mount.get_root().get_path(): ++ continue ++ ++ volume_menu = VolumeMenu(metadata_list, mount.get_name(), ++ mount.get_root().get_path(), ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ for name in mount.get_icon().props.names: ++ if icon_theme.has_icon(name): ++ volume_menu.set_image(Icon(icon_name=name, ++ icon_size=gtk.ICON_SIZE_MENU)) ++ break ++ ++ volume_menu.connect('volume-error', self.__volume_error_cb) ++ menu.insert(volume_menu, -1) ++ volume_menu.show() ++ ++ def __volume_error_cb(self, menu_item, message, severity): ++ self.emit('volume-error', message, severity) ++ ++ ++def get_copy_menu_helper(): ++ global _copy_menu_helper ++ if _copy_menu_helper is None: ++ _copy_menu_helper = CopyMenuHelper() ++ return _copy_menu_helper +-- +1.7.4.4 + |