Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnish Mangal <anish@activitycentral.com>2012-02-06 16:51:17 (GMT)
committer Anish Mangal <anish@activitycentral.com>2012-02-06 16:51:17 (GMT)
commitb56201b47ee251d898752c0fc744820fbc001504 (patch)
tree09b1074ee0be130819929c7b3fcf335bbe51e2b1
parent5560fbe670ffeb187ee4a95fa7118610a9459093 (diff)
bump to sugar development patchset 20120206
-rw-r--r--rpms/sugar/0001-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch10
-rw-r--r--rpms/sugar/0002-Commit-from-Sugar-Labs-Translation-System-by-user-go.patch10
-rw-r--r--rpms/sugar/0003-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0004-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0005-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0006-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0007-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0008-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0009-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0010-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0011-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0012-Commit-from-Sugar-Labs-Translation-System-by-user-cj.patch10
-rw-r--r--rpms/sugar/0013-Control-Panel-Power-section-check-if-powerd-is-avail.patch11
-rw-r--r--rpms/sugar/0014-Commit-from-Sugar-Labs-Translation-System-by-user-an.patch11
-rw-r--r--rpms/sugar/0015-Add-cpu-and-memory-resource-indicator-to-frame.patch7
-rw-r--r--rpms/sugar/0016-set-default-scaling-to-100.patch.patch7
-rw-r--r--rpms/sugar/0017-Add-font-dpi-schema.patch7
-rw-r--r--rpms/sugar/0018-Change-the-method-to-add-new-actitivities-in-the-act.patch9
-rw-r--r--rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch15
-rw-r--r--rpms/sugar/0020-Journal-XS-backup-and-restore.patch27
-rw-r--r--rpms/sugar/0021-Processdialog-prerequisite-check-support.patch19
-rw-r--r--rpms/sugar/0022-Journal-Backup-save-lease.patch7
-rw-r--r--rpms/sugar/0023-backup-translations.patch.patch7
-rw-r--r--rpms/sugar/0024-accessibility_0001_cp_accessibility_keyboard.patch.patch7
-rw-r--r--rpms/sugar/0025-accessibility_0002_magnifier_extension.patch.patch7
-rw-r--r--rpms/sugar/0026-accessibility_0003_cp_accessibility_contrast.patch.patch27
-rw-r--r--rpms/sugar/0027-accessibility_0004_cp_accessibility_mouse.patch.patch25
-rw-r--r--rpms/sugar/0028-accessibility_0005_cp_accessibility_capital_letters..patch23
-rw-r--r--rpms/sugar/0029-accessibility_0006_virtualkeyboard.patch.patch7
-rw-r--r--rpms/sugar/0030-accessibility_0008_cp_show-virtualkeyboard-for-acces.patch25
-rw-r--r--rpms/sugar/0031-accessibility_0007_cp_translations.patch.patch7
-rw-r--r--rpms/sugar/0032-accessibility_0009_cp_show-virtualkeyboard-for-acces.patch7
-rw-r--r--rpms/sugar/0033-accessibility_0012_add-accel-mouse-default.patch.patch7
-rw-r--r--rpms/sugar/0034-accessibility_0013_add-theme-mouse-default.patch.patch9
-rw-r--r--rpms/sugar/0035-Share-3g-connection.patch31
-rw-r--r--rpms/sugar/0036-Make-sure-Adhoc-is-Sugar-Adhoc.patch9
-rw-r--r--rpms/sugar/0037-Simple-messages-notification-extension.patch37
-rw-r--r--rpms/sugar/0038-Improve-message-notification-behaviour.patch15
-rw-r--r--rpms/sugar/0039-S-non-intrusive-NamingAlert.patch7
-rw-r--r--rpms/sugar/0040-Globalkey-for-touchpad-device-icon.patch13
-rw-r--r--rpms/sugar/0041-Dextrose-Escape-all-text-passed-to-Palette.primary_t.patch7
-rw-r--r--rpms/sugar/0042-Database-support-for-3G-control-panel.patch17
-rw-r--r--rpms/sugar/0043-Fix-no-restart-after-provider-info-selection.patch9
-rw-r--r--rpms/sugar/0044-MAY-CONTAIN-TYPO-Flickering-and-unknown-icons-in-the.patch15
-rw-r--r--rpms/sugar/0045-Clipboard-menu-off-screen-2201.patch9
-rw-r--r--rpms/sugar/0046-Add-lease-duration-information-in-about-my-computer.patch14
-rw-r--r--rpms/sugar/0047-Extend-sugar-launch-with-more-options.patch7
-rw-r--r--rpms/sugar/0048-Show-register-gconf-value.patch11
-rw-r--r--rpms/sugar/0049-Yum-updater-notifications-integration.patch15
-rw-r--r--rpms/sugar/0050-Patch-to-add-feedback-icon-to-frame.patch7
-rw-r--r--rpms/sugar/0051-Two-kinds-of-feedback-submits.patch7
-rw-r--r--rpms/sugar/0052-Initial-client-implementation-for-feedback-feature.patch16
-rw-r--r--rpms/sugar/0053-Message-Notifications-second-behaviour-enhancement.patch10
-rw-r--r--rpms/sugar/0054-Enable-disable-personalized-and-anonymous-feedback-s.patch11
-rw-r--r--rpms/sugar/0055-Send-XO-serial-numbers-with-anonymous-reports.patch7
-rw-r--r--rpms/sugar/0056-Let-call-the-frame-from-arbitrary-code-avoiding-curc.patch7
-rw-r--r--rpms/sugar/0057-Notify-on-not-sent-feedbacks-fix-issue-with-not-auto.patch13
-rw-r--r--rpms/sugar/0058-Switch-default-feedback-serve-to-feedback.sl.o-clean.patch9
-rw-r--r--rpms/sugar/0059-Do-not-send-empty-feedback-reports-if-anonymous_with.patch7
-rw-r--r--rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch19
-rw-r--r--rpms/sugar/0061-Don-t-choke-on-non-integer-activities.patch13
-rw-r--r--rpms/sugar/0062-updater-Only-pre-select-already-installed-activities.patch11
-rw-r--r--rpms/sugar/0063-Add-quz-and-aym-to-ALL_LINGUAS-take-2.patch7
-rw-r--r--rpms/sugar/0064-sugar-session-export-manual-Gnome-proxy-settings-as-.patch9
-rw-r--r--rpms/sugar/0065-Localise-sugar-install-bundle.patch83
-rw-r--r--rpms/sugar/0066-sugar-install-bundle-skip-older-bundles-by-default-a.patch83
-rw-r--r--rpms/sugar/0067-Partial-fix-for-GSM-connection-time-see-SL-2992.patch67
-rw-r--r--rpms/sugar/0068-Journal-detail-view-don-t-choke-on-invalid-keep-prop.patch48
-rw-r--r--rpms/sugar/0069-Add-capability-to-connect-to-WPA-WPA2-Enterprise-Net.patch811
-rw-r--r--rpms/sugar/0070-Connecting-to-Hidden-Wireless-Networks.patch311
-rw-r--r--rpms/sugar/0071-Remove-AC-microformat-updater-files.patch1155
-rw-r--r--rpms/sugar/0072-sl-2713-Add-last-updated-on-field-to-CP-About-my-com.patch124
-rw-r--r--rpms/sugar/0073-AU-694-More-friendly-message-when-not-able-to-connec.patch32
-rw-r--r--rpms/sugar/0074-sl-3286-race-condition-fixed-by-making-the-read-and-.patch82
-rw-r--r--rpms/sugar/0075-sl-2955-Ignore-Buddy-updates-before-receiving-the-in.patch28
-rw-r--r--rpms/sugar/0076-uy-1229-Journal-Entry-transfer-from-1-to-N-users.patch1674
-rw-r--r--rpms/sugar/0077-uy-1223-Add-a-submenu-in-journal-palette-to-copy-a-j.patch100
-rw-r--r--rpms/sugar/0078-Aleksey-s-PROXY-feature-merged-into-Network-CP.patch450
-rw-r--r--rpms/sugar/0079-sl-3316-Wireless-modem-error-should-refer-to-My-Sett.patch36
-rw-r--r--rpms/sugar/0080-au-911-Add-launcher-for-nm-connection-edirot-in-My-S.patch89
-rw-r--r--rpms/sugar/0081-au-1018-Pick-up-whatever-Modem-Parameters-are-entere.patch74
-rw-r--r--rpms/sugar/0082-uy-1242-Batch-Operations-on-Journal-Entries-Copy-Era.patch1301
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
+