[Debian-islamic-commits] [othman] 02/07: Imported Upstream version 0.3.0
أحمد المحمودي (Ahmed El-Mahmoudy)
aelmahmoudy at sabily.org
Mon Mar 31 15:28:31 UTC 2014
This is an automated email from the git hooks/post-receive script.
aelmahmoudy-guest pushed a commit to branch master
in repository othman.
commit c089f8ed4c7a77fce845eee62629bd6aae3d4320
Author: أحمد المحمودي (Ahmed El-Mahmoudy) <aelmahmoudy at sabily.org>
Date: Mon Mar 31 14:14:46 2014 +0200
Imported Upstream version 0.3.0
---
LICENSE-ar.txt | 138 ---------
LICENSE-en | 222 --------------
TODO | 4 +
exe-setup.py | 51 ++--
gen-index.py | 12 +-
othman.spec | 56 ++--
othman/core.py | 397 +++++++++++++------------
othman/gtkUi.py | 779 ++++++++++++++++++++++++++------------------------
othman/univaruints.py | 302 +++++++++++++++++++
othman/varuints.py | 170 -----------
po/ar.po | 34 ++-
po/de.po | 37 ++-
po/othman.pot | 33 ++-
waqf2-ar.pdf | Bin 0 -> 69252 bytes
14 files changed, 1057 insertions(+), 1178 deletions(-)
diff --git a/LICENSE-ar.txt b/LICENSE-ar.txt
deleted file mode 100644
index 8f8ce80..0000000
--- a/LICENSE-ar.txt
+++ /dev/null
@@ -1,138 +0,0 @@
- بسم الله الرحمن الرحيم
-
- رخصة "وقف" العامة
-
- مقدمة
-
- إن نشر أي عمل فكري (برنامج حاسوبي أو كتاب على سبيل المثال لا الحصر) لا يكون بالبيع وإن بدا
- كذلك بل يكون باتفاقية ضمنية لا قيمة لها لولا مفهوم الملكية الفكرية الذي رسخته القوانين الوضعة
- التي يفترض أنها وضعت لحفز نشر الأعمال الفكرية النافعة. ترتكز الملكية الفكرية على أن أول مودع
- العمل (يسمى المالك Owner أو copyright holder وسنشير له في هذه الوثيقة باسم صاحب العمل) يملك
- الحق في العمل بصورته المعنوية على أي وسيط مادي كان وله حق التصرف في ملكه المزعوم. إن أي
- استعمال للعمل الفكري دون الإذن المسبق الصريح من مالك العمل يعد مكافئا أخلاقيا لسرقة السفن أو
- “قرصنة” بنظرهم. هذا الإذن يسمى “رخصة License” وتهدف تلك الرخص غالبا لإعطاء المالك (غالبا
- الناشر) أفضلية في السوق أمام الناشرين الآخرين من خلال احتكار العمل وذلك بفرض قيود على
- المستخدمين لا الناشرين. تسمى مثل تلك الأعمال بالأعمال “المملوكة Proprietary”.
-
- إننا نرى أن هذه الاتفاقيات (الرخص المملوكة) مجحفة جدا لكننا ندرك إن قبول اتفاقية معينة (مثل
- رخص البرامج المملوكة) وأنت تضمر مسبقا خرقها أمر غير أخلاقي ^1). لهذا فنحن لا نحل المشكلة
- بمشكلة أخرى بل إننا نقدم البديل.
-
- نحن لدينا رؤية مختلفة فنحن نقدم أعمالنا الفكرية من برامج حاسوبية وغيرها ابتغاء وجه الله، والتي
- هي الركيزة الأساسية لهذه الرخصة التي تميزها عن الرخص المملوكة وعلى أي غاية آخرى (مثل نشر العلم
- النافع أو جني الأرباح) أن تتحقق بوسيلة لا تخالف هذا الهدف الأسمى.
-
- في قناعتنا - التي لا نلزم أحدا بها والتي لا يضيرك أن لا تشترك معنا فيها - أن الاسلام يحرم
- وبشكل قطعي حكر العلم والمعرفة والانتاج الفكري على وجه العموم، وهذا التحريم يأتي من عدة أوجه :
-
- * الحديث النبوي الشريف : “من كتم علما ألجمه الله يوم القيامة بلجام من نار” ^2) وقد جاءت
- كلمة العلم نكرة عامة فهي تنطبق على كل علم ينتفع الناس به سواء علم ديني أو دنيوي.
- * أن الإسلام حدد ما يصح أن يكون مملوكا وذلك لا ينطبق على العمل الفكري لأنه ليس عينا محصورا
- وأغلب شروط البرمجيات المملوكة تقع في بيع الغرر (ذاك أن ما لا يجوز بيعه لا يجوز تملكه) دل
- عليه ما ورد من النهي عن بيع الغرر في صحيح مسلم (ويدخل فيه مسائل كثيرة غير منحصرة كبيع
- المعدوم والمجهول وما لا يقدر على تسليمه وما لم يتم ملك البائع عليه) وما ورد عن الأئمة
- الأربعة من فهمهم لهذا.
- * لسنا بحاجة لابتداع شيء لنشر العلم لأن الأعمال الفكرية ليست محدثة وأن قرون الخير الأولى
- نشرت العلم دون تملكها.
- * حبس المعرفة والعلم عمن يحتاجه هو إضرار بالناس لصالح قلة منهم، وهذا مما نهى عنه الشارع ولا
- يبرر هكذا فعل إلا من يؤمن بالرأسمالية الذاتية التي تطرفت في تعظيم مصلحة الفرد.
- * انتفاء مبرر المصلحة إذا وجدت طرق لنشر الأعمال الفكرية والتربح منها دون كتمها.
- * إن المتمعن في قوانين الملكية الفكرية المختلفة يجد في أنها تتلخص بإعطاء “المالك” المزعوم
- الحق في تحريم ما أحله الله ليكون ذلك مدخلا له في كسب مادي ولا علاقة لها بتقديم خدمة أو
- منتج معين.
- * الغموض الذي يحيط بماهية الشيء المزعوم ملكه تفتح الباب أمام جبات “الأتاوات” حيث تتكسب بعض
- الشركات من التهديد بخطر المقاضاة حتى على أشياء لا تملكها والقضاء الأمريكي يغص بمثل هذه
- القضايا.
-
- فإن كانت الغاية قد بررت الوسيلة لواضعي الدستور الأمريكي حين أقروا مفهوم ”الملكية الفكرية”
- كحق مكتسب (غير فطري باعترافهم) يخدم ما تؤمن به ثقافتهم من تعظيم المصلحة الذاتية وتقديمها على
- كل شيء؛ ذاك لا يعنينا في شيء ونحن نؤمن بعدم صلاحية ذلك لعموم البشر، لهذا جاءت رخصة “وقف”
- العامة (كما غيرها الكثير من رخص التوزيع المضادة لحكر التوزيع)، فهي وضعت لكي تؤكد لمستهلك العمل
- الفكري أن لا قيد يفرضه صاحب العمل على استخدم منتجه والإفادة منه أو إعادة انتاجه وتوزيعه. أي أن
- تجعل حقوق الطبع والتوزيع “ممنوحة” أو مرفوعة وليس “محفوظة” (وبالإنجليزية يشار لها باسم copyleft
- او copy-wrong تهكما على copyright)
-
- فمعادلة وتفاصيل تركيب الدواء هي عمل فكري، وبرمجية الحاسوب هي عمل فكري وقصيدة الشعر هي عمل
- فكري. وهي بشكل عام كل فكرة تنفع الناس تصلهم على شكل منتج. وعندما نتكلم عن العمل الفكري فإننا
- نحدد التعريف بالفكر الذي ينفع الناس نشره ولا نقصد به عموم الفكر فلكل منا خصوصيته وأسراره التي
- لايشارك بها الاخرين. فأسماء الزبائن وأرقام المناقصات وسياسة الدولة العسكرية أو السياسية ليست
- أعمالا فكرية.
-
- وهنا يجدر الوقوف عند مسألتين :
-
- * الأولى، أن الحق الأدبي للصاحب العمل يبقى للمبتكر الأصلي على كل الأحوال. فلا يجوز لأحد أن
- يأخذ هذا العمل وينتحله ويدعيه لنفسه.
- * والثانية، أن لصحاب العمل ولغيره (ممن عندهم الكفاية) الإفادة المادية من العمل كأن يطلب
- أتعابا أو يتقاضى أجرا عن تحسينه أوتطويره أو أجرا عن تدريسه وهكذا. ولكن ما وراء ذلك فلا يحق
- له إدعاء ملكيته للفكرة أو العمل في صورته المعنوية ولا يحق له منع الآخرين من اعادة نشرها
- والاستفادة منها. وهذا لا تناقض مع كون العمل موقوفا لأن الموقوف هو أصل العمل الفكري بصورته
- المعنوية وليس الوسيط أو الخدمة ^3)
-
- تعريفات
-
- تكون التعريفات هنا هي المقصودة عند استخدامها في الرخصة :
-
- * العمل الفكري (أو اختصارا العمل): هو أي عمل فكري نافع غير مادي ولا ملموس ويمكن لمن يتلقاه
- عمل نسخ منه ونقله إلى آخرين دون أي عبء على من قام بإيصال النسخة اليه.
- * صاحب العمل : هو الشخص المُبتكِر أو الجهة التي قامت بتطوير وتوفير العمل الفكري (والتي تملك
- حقوق النسخ عند الجهات الرسمية إن لزم الأمر).
- * المُنتًفع (المستخدِم): هو الشخص أو الجهة التي ترغب بالانتفاع من العمل الفكري.
- * رخصة الاستخدام (أو اختصارا الرخصة): هي هذا العقد الذي بين يديك وهو عقد بين صاحب العمل
- والمنتفع يحق للمنتفع بموجبه وضمن شروطه الاستفادة والانتفاع من العمل. ونظرا لتوفر العمل
- بشكل مفتوح للجميع فإن قيام المنتفع بالاستفادة من العمل الفكري يعني بالضرورة إقراره
- وموافقته على كافة شروط الرخصة. فإذا لم يكن المنتفع موافقا على الرخصة تنتفي عنه حقوق الحقوق
- الممنوحة بموجبها ويصبح أي انتفاع بالعمل غير مشروع ويعرض نفسه للمقاضاة.
-
- بنود الرخصة
-
- رخصة وقف العامة، يرمز لها اختصارا بـ “وقف”، هي رخصة لتوزيع العمل الفكري (من برمجيات أو مؤلفات
- مكتوبة أو إنتاج فني على سبيل المثال لا الحصر). تتشابه هذه الرخصه في اهدافها مع رخص البرمجيات
- الحرة والتوثيق الحر و رخصة الانتاج المشترك. ولكنها تزيد عليها ببعض الجوانب المتعلقة بالهدف من
- وراء الانتاج و حدود الاستخدام.
-
- رخصة وقف وكما يقترح الاسم هي إقرار من صاحب العمل بأن هذا العمل هو وقف لله تعالى ويتقصد به نوال
- رضاه من خلال انتفاع الناس به، أي أن هذا العمل هو صدقة جارية لوجه الله تعالى. وبذلك فإن رخصة
- وقف تقر بأن للمنتفع -أيا كان جنسه أو لونه أو عقيدته- الحق في الإفادة من العمل وإعادة توزيعه
- وحتى تطويره ضمن الشروط التالية:
-
- * أولا - أوجه الاستخدام :
-
- يحق للمنتفع استخدام العمل ضمن أي غرض فيه منفعة ولايجوز استخدامه فيما يسئ للأخرين أو يخالف
- مبادئ الإسلام السمحة. مع ملاحظة أن الأعمال التي يغلب الظن أنها الضارة لا يجوز أن توضع تحت هذه
- الرخصة أصلا.
-
- * ثانيا - حق التوزيع :
-
- يحق للمنتفع إعادة توزيع العمل بصورته الأصلية ودون تعديل وتحت شروط رخصة وقف، بالكم الذي يريد مع
- صون ذكر الحق الأدبي لصاحب العمل.
-
- * ثالثا - حق التعديل :
-
- يحق للمنتفع الحصول على النسخة المصدرية للعمل كما ويحق له التعديل عليها بما يناسب احتياجاته
- وضمن الحدود الموضحة في بند أولا.
-
- * رابعا - حق توزيع النسخة المُعدّلة :
-
- يحق للمنتفع إعادة توزيع العمل المعدّل فقط تحت رخصة وقف العامة وعلى أن يذكر أصل العمل المعدل
- وطبيعة التعديل وأن يكون واضحا بما لايدع مجالا للبس أن هذه النسخة معدلة وليست هي النسخة الأصلية
- التي انتجها صاحب العمل الأول.
-
- * خامسا - عدم المسؤلية :
-
- لا يتحمل صاحب العمل أية مسؤوليه لا قانونية ولا أخلاقية عن حسن أو إساءة استخدام العمل أو
- الأضرار المباشرة أو غير المباشرة الناتجة عنه إلى أقصى حد يسمح به القانون. وصاحب العمل بهذا لا
- يقدم أية ضمانة لا ضمنا ولا تصريحا بقدرة المنتج على تحقيق أي غرض.
-
- المسؤولية الكاملة تقع على عاتق المنتفع والضمانة الوحيدة المقدمة له هي مصدر العمل.
-
- الخلاصة
-
- استخدام رخصة وقف العامة يساعد في نشر الوعي على خطر مفاهيم الملكية الفكرية. كما ويقدم البديل
- القانوني وإن كنّا لانؤمن بقانونية تلك الملكيات.
-
- ^1) انظر http://www.islam-qa.com/ar/ref/454
- ^2) الحديث صحيح رواه أحمد وأبو داود والترمذي وابن ماجه انظر “رفع المنار بطرق حديث من
- كتم علماً ألجمه الله بلجام من نار”
- ^3) مثلا يجوز أخذ أجل على نقل ثمار أرض موقوفة أو عصرها
-
-
diff --git a/LICENSE-en b/LICENSE-en
deleted file mode 100644
index c389e77..0000000
--- a/LICENSE-en
+++ /dev/null
@@ -1,222 +0,0 @@
- بسم الله الرحمن الرحيم
-
- In the name of Allah, Most Gracious, Most Merciful
-
- "Waqf" General Public License
-
- This is the informal English translation of Waqf General Public License.
- Anything but the Arabic version of the license has no value except for
- convenience of our English speaking users. When we talk about the License
- we refer to the Arabic version, which is the only one we officially
- offer, we will try our best to make other translation as accurate as
- possible but because of the nature of human languages we use one single
- reference language.
-
- Preamble
-
- Publishing any intellectual work (including but not limited to books and
- Computer Software) is not done by selling even if it appeared to be so,
- but it's about getting an implied license that has no enforcement without
- the concept of intellectual property granted by man-made laws which was
- acquired as they claim to promote the publication of science and the
- useful arts. The concept of the so called “intellectual property” is
- centered around giving ownership to the first one who register the work
- (called owner or copyright holder and we will refer to him/her/it as
- holder) then the holder practice his rights on the work in it's intangible
- form on any media it's carried, and thus using the intellectual work
- without explicit prior permission is according to them ethically
- equivalent to hijacking ships or “piracy” as they call it. This permission
- is called License and it's aimed to give the holder (usually the
- publisher) an advantage in the market against other publishers through
- monopoly on providing that work by imposing restrictions on the users not
- the publishers. Such works are called “Proprietary”.
-
- We see that such agreements (proprietary licenses) are evil, but we also
- acknowledge that making an agreement with a prior intention to violate it
- is another immoral evil ^1). We don't solve problems with problems, we
- offer an alternative.
-
- We have a different vision, we offer our work (like computer software) to
- please Allah, and this is the pillar of this license which distinguish it
- from proprietary licenses. Any other intention (like spreading knowledge
- or getting profits) should be accomplished in ways that does not go
- against our primary moral aim.
-
- According to our believes (which we don't enforce on any one, and you may
- or may not share it with us) that Islam certainly forbids monopoly and
- concealing knowledge in general, and this comes from:
-
- * The sound Hadith (saying of prophet Muhammad PBUH): “Who conceal any
- type of knowledge will be bridle with a bridle of fire on the Day of
- Resurrection.” ^2)
- * Islamic Jurisprudence has specified what can be owned, and the rules
- does not apply to ideas as it's not a bounded tangible object and
- almost all the terms of proprietary agreements fall in ruling of
- forbidden Gharar^3) (a kind of fraud that use uncertainty to
- fool the customers) as we see in Imam Muslim Sahih description of that
- term {and below that term we classify so many actions like selling
- negligible objects or unknowns or what can't be delivered or things
- that are not full owned by the seller} and such view is also shared by
- the four major scholars.
- * The laws of Fiqh (Islamic Jurisprudence) have been perfected in the
- good centuries and no new rulings are allowed to be brought except for
- newly discovered things and this does not apply ideas as they did
- exist in the past centuries and Muslims massively spread them without
- owning them. FYI: Almost all the Islamic literature is freely
- available on the internet.
- * Concealing useful knowledge from those who need it impose damage to
- people for temporal gains to few individuals. This can not be
- justified by Shari'a (Islamic Law), it's only justified by
- individualist capitalism ^4)
- * Even bringing interests to software publishers is not a valid excuse
- because there are some companies produce and publish software without
- owning them (proprietary software) and yet they gain profits.
- * the so call “Intellectual Proprietary” is all about giving owners the
- right to forbid what Allah had made lawful to gain money, not by
- providing service nor goods.
- * The uncertainty and untangable nature of the claimed owned product is
- the source of “protection money collecting” where some company
- threaten some smaller company by copyright infringement lawsuit on
- some purposely ambiguous product which the later can't afford so they
- go safe and pay.
-
- If the good intentions made excuses to introduce “intellectual
- proprietary” by the Machiavellists who wrote the American constitution
- as an acquired right (at least they admit it's not a natural right), they
- serve their own way of life which is not international. We don't take
- that. We have our own high moral standards. We introduce Waqf General
- Public License to place it among the anti-copyright licenses. This license
- is designed to grant rights to users not to restrict them, it's uses the
- concept of copyleft/copy-wrong as opposed to the concept of copyright.
-
- Computer software, Medicine formula or even poems are considered to be
- intellectual works and thus can be covered by “Waqf”. An intellectual work
- is any useful idea that can be delivered or utilized by people. “Waqf” is
- not meant for ideas in general, but for intellectual work which is
- beneficial to people if published. As we support protecting privacy for
- example one shouldn't publish names of his customers, bid prices,
- government military or political secrets because those are not
- intellectual works.
-
- We should clarify two things:
-
- * First: The favor and ethical rights of the holder are preserved and
- acknowledged
- * Second: original author and any other party (if they have the
- intention and the ability) have the right to get fees on enhancing the
- work or developing it or to provide paid services or paid courses
- …etc. but not the work in its intangible form. There is not
- contradiction between getting fees on services because what is covered
- by “Waqf” is the source form of the work not the media nor the service
- (the same way one can take fees on shipping fruits for a charitable
- Waqf fruit field or make juice)
-
- Definitions
-
- The following terms would have the corresponding meanings in this license
-
- * The intellectual work (or The work for short): is any useful
- intangible intellectual product that can be passed on to others or
- replicated withno cost imposed on the original author or the one who
- pass it on before.
- * The holder: is the creative author or the party which carried the
- process of developing the work and make it available (and the one who
- holds the copyrights on the product before the governmental offices).
- * the licensee or the user: any one or party who uses the work (runs,
- copies, modifies or otherwise takes benefits provided by the work).
- * The license: this agreement (the Arabic version of “Waqf”) between the
- two parties (the holder and the user) covering the usage of the second
- party on the work provided by the first party. The usage of the
- product is with accordance to the terms of this license. And because
- of the intangible nature of the work, this agreement applies by using
- the product (in any way) without signing the agreement. One do not
- accept the terms looses the rights given by this license and his/her
- usage of the work is considered illegal and will be sued.
-
- The Terms
-
- Waqf General Public license (or “Waqf” for short) covers intellectual
- works (including but not limited to Software and paper publications,
- scientific theses, art works) and have many common features with FLOSS
- licenses. Waf is distinguished with its moral intentions behind the work
- and usage limits.
-
- As the name suggests, with “Waqf” ^5) the holder announce his/her work
- was produced and made available to the public for the sake of Allah aimed
- to please Allah by providing people with useful works. It's a form of
- “Sadaqa Jariah” ^6).
-
- “Waqf” gives users (regardless of their nationality, color, race,
- religion) the right to benefit from the work with running, copying,
- redistribution, or even develop and only with according to the following
-
- * First - Usage :
-
- The user may use the work for any good purpose and he may not use it to
- harm others or violate the permissive principles of Islam ^7). Notice
- that any work that is most likely harmful can't be put under Waqf in the
- first place.
-
- * Second - Redistribution :
-
- The user have the right to redistribute the unmodified work in any
- quantity to any third party under the terms of this license, acknowledging
- the favor and ethical rights of the holder to the third party.
-
- * Third - Modifications :
-
- The user has the right to get the source form of the work and make
- modifications enhancing or adopting to the user's needs to the limits
- given by this license.
-
- * Fourth - Redistribute modifications:
-
- The user can redistribute modifications only under this license provided
- that he points to previous original work (the unmodified work) and its
- author and the nature of modification clearly to the third party to whom
- the modified work is presented in a way that no one may reasonably get
- confused between it and the original unmodified work of the previous
- author.
-
- * Fourth - Disclaimer of responsibility :
-
- The holder does not take any legal nor moral direct or indirect
- responsibility nor liability on the good or bad usage or damages caused by
- the work to the farthest level allowed by the law. THIS SOFTWARE IS
- PROVIDED AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED BY THE HOLDER.
-
- The entire responsibility is on the user and the only warranty given to
- him is the source of the work.
-
- Conclusion
-
- Applying Waqf public license help to spread the awareness of dangers
- caused by intellectual property. And provide people with a legal
- alternative even though we don't believe on the validity of such man made
- laws.
-
- ^1) http://www.islam-qa.com/ar/ref/454
- ^2) This is an authentic sound/Sahih as it's narrated by Imam
- Ahmad, Abu Dawood, AlTirmithi and Ibn Majah. Refer to “Hoisting the
- Lighthouse : A synopsis of the Various narrations of the Hadith: Whoever
- conceals knowledge, Allah will bridle him with a bridle of fire” or in
- Arabic “رفع المنار بطرق حديث من كتم علماً ألجمه الله بلجام من نار”.
- ^3) Some information about Ghrar is available at
- http://www.investopedia.com/terms/g/gharar.asp
- ^4) Although Islamic economy is a kind of liberal capitalism economy
- but it's regulated with so many moral rulings that guarantee social
- justice
- ^5) The name Waqf means to prevent the source or assets from being
- consumed while giving away for charity the proceeds.
- ^6) Sadaqa Jariah means sustainable charity which is a kind of charity
- that continue to be beneficial to people even after the death of the one
- who give it like giving the fruits of a field but not the field, more over
- prophet Muhammad PBUH specifically named useful knowledge as a form of
- that kind of charity.
- ^7) Some scholars use the term Maqasid Alsharia and summarize them by
- saying that it aims to protect people rights in religion, lives, sanity,
- breed, and properties.
-
-
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..ce54358
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+بسم الله الرحمن الرحيم
+- copy multiple aya to clipboard
+- add 100% zoom button
+- add accels
diff --git a/exe-setup.py b/exe-setup.py
index 8016c5c..9d91139 100644
--- a/exe-setup.py
+++ b/exe-setup.py
@@ -11,38 +11,41 @@ import py2exe
# NOTE: before you use this tool you should run make to generate index and locale files
-locales=map(lambda i: ('locale/'+i,[''+i+'/othman.mo',]),glob('locale/*/LC_MESSAGES'))
-data_files=[('othman-data',glob('othman-data/*')), ('.', ['COPYING']+glob('LICENSE*')+glob('README*'))]
+locales=map(lambda i: ('locale/' + i, ['' + i + '/othman.mo',]), glob('locale/*/LC_MESSAGES'))
+data_files=[('othman-data',
+ glob('othman-data/*')),
+ ('.',
+ ['COPYING'] + glob('LICENSE*') + glob('README*'))]
data_files.extend(locales)
setup (name='Othman', version='0.2.0',
- description='Othman Quran Browser',
- author='Muayyad Saleh Alsadi',
- author_email='alsadi at ojuba.org',
- url='http://othman.ojuba.org/',
- license='Waqf',
- packages=['othman'],
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Intended Audience :: End Users/Desktop',
- 'Operating System :: POSIX',
- 'Programming Language :: Python',
- ],
+ description='Othman Quran Browser',
+ author='Muayyad Saleh Alsadi',
+ author_email='alsadi at ojuba.org',
+ url='http://othman.ojuba.org/',
+ license='Waqf',
+ packages=['othman'],
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: End Users/Desktop',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python',
+ ],
windows = [
- {
+ {
'script': 'othman-browser',
'icon_resources': [(1, "othman.ico")],
- }
- ],
+ }
+ ],
options = {
- 'py2exe': {
- 'packages':'encodings',
- 'includes': 'cairo, pango, pangocairo, atk, gobject, gio',
- }
- },
- data_files=data_files
-)
+ 'py2exe': {
+ 'packages':'encodings',
+ 'includes': 'cairo, pango, pangocairo, atk, gobject, gio',
+ }
+ },
+ data_files=data_files
+ )
diff --git a/gen-index.py b/gen-index.py
index a2e2c9d..761105a 100755
--- a/gen-index.py
+++ b/gen-index.py
@@ -4,12 +4,14 @@
import sys, os, os.path, time
from othman.core import othmanCore, searchIndexer
-q=othmanCore(False)
-ix=searchIndexer(True)
-wc=0
+q = othmanCore(False)
+ix = searchIndexer(True)
+wc = 0
for n,(o,i) in enumerate(q.getAyatIter(1, 6236)):
- for w in i.split(): ix.addWord(w,n+1); wc+=1
-d=os.path.dirname(sys.argv[0])
+ for w in i.split():
+ ix.addWord(w,n+1)
+ wc += 1
+d = os.path.dirname(sys.argv[0])
ix.save()
print "got %d words, %d terms (max term length=%d character, term vectors size=%d bytes)." % (wc, ix.terms_count, ix.maxWordLen, ix.term_vectors_size)
diff --git a/othman.spec b/othman.spec
index 889304e..8b3079e 100644
--- a/othman.spec
+++ b/othman.spec
@@ -1,19 +1,22 @@
+%global owner ojuba-org
+%global commit #Write commit number here
+
Name: othman
-Version: 0.2.7
+Version: 0.3
Release: 1%{?dist}
Summary: Othman Electronic Quran Browser
-# sitelib for noarch packages, sitearch for others (remove the unneeded one)
-%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
-
Group: Applications/Productivity
-License: Waqf
-URL: http://othman.ojuba.org
-Source: http://git.ojuba.org/cgit/%{name}/snapshot/%{name}-%{version}.tar.bz2
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+License: WAQFv2
+URL: http://ojuba.org
+Source: https://github.com/%{owner}/%{name}/archive/%{commit}/%{name}-%{commit}.tar.gz
BuildArch: noarch
BuildRequires: python
-Requires: pygtk2, islamic-menus, arabeyes-core-fonts
+BuildRequires: python2-devel
+Requires: islamic-menus
+Requires: arabeyes-core-fonts
+Requires: pygobject3 >= 3.0.2
Requires: python-othman
+
%description
Othman Electronic Quran Browser displays Quranic text in Othmani script style
as written under authority of Othman ibn Affan the companion of prophet Muhammad PBUH
@@ -21,13 +24,12 @@ as written under authority of Othman ibn Affan the companion of prophet Muhammad
Othman project features fast search, autoscrolling, copy Quranic text to clipboard.
%prep
-%setup -q
+%setup -q -n %{name}-%{commit}
%build
make %{?_smp_mflags}
%install
-rm -rf $RPM_BUILD_ROOT
%makeinstall DESTDIR=$RPM_BUILD_ROOT
%post
@@ -42,42 +44,48 @@ if [ -x %{_bindir}/gtk-update-icon-cache ] ; then
%{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
fi
-%clean
-rm -rf $RPM_BUILD_ROOT
-
%package -n python-othman
Group: System Environment/Base
Summary: python package providing access to Quranic text
-License: Waqf
+License: WAQFv2
BuildArch: noarch
Requires: python
+
%description -n python-othman
a python package that provides access to Quranic text with a fast search index
%files
%defattr(-,root,root,-)
-%doc LICENSE-en LICENSE-ar.txt README README-ar.txt COPYING
+%doc README README-ar.txt COPYING waqf2-ar.pdf
%{_bindir}/othman-browser
-%{python_sitelib}/othman/gtkUi.p*
+%{python2_sitelib}/othman/gtkUi.p*
%{_datadir}/applications/*.desktop
%{_datadir}/locale/*/*/*.mo
%{_datadir}/icons/hicolor/*/apps/*.png
%files -n python-othman
%defattr(-,root,root,-)
-%doc LICENSE-en LICENSE-ar.txt README README-ar.txt COPYING
-%dir %{python_sitelib}/othman
+%doc README README-ar.txt COPYING waqf2-ar.pdf
+%dir %{python2_sitelib}/othman
%dir %{_datadir}/othman
-%{python_sitelib}/*.egg-info
-%{python_sitelib}/othman/core.p*
-%{python_sitelib}/othman/varuints.p*
-%{python_sitelib}/othman/__init__.p*
+%{python2_sitelib}/*.egg-info
+%{python2_sitelib}/othman/core.p*
+%{python2_sitelib}/othman/*varuints.p*
+%{python2_sitelib}/othman/__init__.p*
%{_datadir}/othman/*
%changelog
+* Mon Jun 4 2012 Mosaab Alzoubi <moceap at hotmail.com> - 0.3-1
+- New Relese
+
+* Mon Jun 4 2012 Mosaab Alzoubi <moceap at hotmail.com> - 0.2.8-2
+- General Revision.
+
+* Mon Jun 4 2012 Muayyad Saleh AlSadi <alsadi at ojuba.org> - 0.2.8-1
+- port to gtk3
+
* Sat Jun 12 2010 Muayyad Saleh AlSadi <alsadi at ojuba.org> - 0.2.5-1
- update to new version
* Wed Apr 28 2010 Muayyad Saleh Alsadi <alsadi at ojuba.org> - 0.2.0-1
- initial release
-
diff --git a/othman/core.py b/othman/core.py
index 32ab60d..f5095d0 100644
--- a/othman/core.py
+++ b/othman/core.py
@@ -23,221 +23,250 @@ import sqlite3
import array
from itertools import imap
import threading
-import varuints
+import univaruints
-data_dir=None
+data_dir = None
def guessDataDir():
- global data_dir
- if data_dir: return data_dir
- if not hasattr(sys, "frozen"):
- # we are not in py2exe
- f=os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
- d=os.path.join(f, '..', 'othman-data')
- if os.path.exists(d): data_dir=os.path.abspath(os.path.realpath(d)); return data_dir
- d=os.path.join(f,'..', '..', '..', '..', 'share', 'othman')
- if os.path.exists(d): data_dir=os.path.abspath(os.path.realpath(d)); return data_dir
- # we are in py2exe or DATA can't be located relative to __FILE__
- f=os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
- d=os.path.join(f, 'othman-data')
- if os.path.exists(d): data_dir=os.path.abspath(os.path.realpath(d)); return data_dir
- d=os.path.join(f, '..', 'share', 'othman')
- data_dir=os.path.abspath(os.path.realpath(d))
- return data_dir
+ global data_dir
+ if data_dir: return data_dir
+ if not hasattr(sys, "frozen"):
+ # we are not in py2exe
+ f = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
+ d = os.path.join(f, '..', 'othman-data')
+ if os.path.exists(d):
+ data_dir=os.path.abspath(os.path.realpath(d))
+ return data_dir
+ d = os.path.join(f,'..', '..', '..', '..', 'share', 'othman')
+ if os.path.exists(d):
+ data_dir = os.path.abspath(os.path.realpath(d))
+ return data_dir
+ # we are in py2exe or DATA can't be located relative to __FILE__
+ f = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
+ d = os.path.join(f, 'othman-data')
+ if os.path.exists(d):
+ data_dir=os.path.abspath(os.path.realpath(d))
+ return data_dir
+ d = os.path.join(f, '..', 'share', 'othman')
+ data_dir = os.path.abspath(os.path.realpath(d))
+ return data_dir
def cmp_bisect_right(ccmp, a, x, lo=0, hi=None):
"""
- same as bisect.bisect but uses custom cmp function
+ same as bisect.bisect but uses custom cmp function
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
- mid = (lo+hi)>>1
- if ccmp(a[mid],x)>0: hi = mid # ie. if x < a[mid]
- else: lo = mid+1
+ mid = (lo + hi) >> 1
+ if ccmp(a[mid], x) > 0:
+ hi = mid # ie. if x < a[mid]
+ else:
+ lo = mid + 1
return lo
+
def cmp_bisect_left(ccmp, a, x, lo=0, hi=None):
"""
- same as bisect.bisect_left but uses custom cmp function
+ same as bisect.bisect_left but uses custom cmp function
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
- mid = (lo+hi)>>1
- if ccmp(x, a[mid])>0: lo = mid+1 # ie. if a[mid] < x
- else: hi = mid
+ mid = (lo + hi) >> 1
+ if ccmp(x, a[mid]) > 0:
+ lo = mid + 1 # ie. if a[mid] < x
+ else:
+ hi = mid
return lo
-class othmanCore:
- SQL_GET_AYAT='SELECT othmani, imlai FROM Quran WHERE id>=? ORDER BY id LIMIT ?'
- SQL_GET_SURA_INFO='SELECT rowid, sura_name, other_names, makki, starting_row, comment FROM SuraInfo ORDER BY rowid'
- def __init__(self, load_ix=True):
- self.data_dir = d = guessDataDir()
- self.db_fn = db_fn=os.path.join(d,'quran.db')
- self._cn = {}
- cn=self._getConnection()
- l=list(cn.execute(self.SQL_GET_SURA_INFO).fetchall())
- if len(l)!=114: raise IOError
- self.suraIdByName=dict(((i[1],i[0]) for i in l))
- self.suraInfoById=[list(i[1:])+[0] for i in l]
- for i in range(113):
- self.suraInfoById[i][5]=self.suraInfoById[i+1][3]-self.suraInfoById[i][3]
- self.suraInfoById[-1][5]=6
- self.basmala, self.basmala_imlai=list(self.getAyatIter(1))[0]
- self.basmala=self.basmala[:self.basmala.rfind(' ')]
- self.ix=None
- if load_ix: self.ix=searchIndexer()
-
- def _getConnection(self):
- n = threading.current_thread().name
- if self._cn.has_key(n):
- r = self._cn[n]
- else:
- r = sqlite3.connect(self.db_fn)
- self._cn[n] = r
- return r
-
- def showSunnahBasmala(self, sura):
- return sura!=1 and sura!=9
-
- def suraAyaFromAyaId(self, ayaId):
- sura=cmp_bisect_right(lambda i,j: cmp(i[3], j), self.suraInfoById, ayaId)
- aya=ayaId-self.suraInfoById[sura-1][3]+1
- return sura,aya
-
- def ayaIdFromSuraAya(self, suraId, aya=1):
- """
- suraId: sura number from 1 to 114
- aya: aya number from 1 to the end of the sura
- """
- return self.suraInfoById[suraId-1][3]+aya-1
-
- def getAyatIter(self, ayaId, number=1):
- """
- return a list of (othmani, imlai) tuples
- """
- return self._getConnection().execute(self.SQL_GET_AYAT, (ayaId,number))
-
- def getSuraIter(self, suraId, number=0, fromAya=1):
- """
- return a list of (othmani, imlai) tuples for the whole sura
- suraId: sura number from 1 to 114
- number of ayat (0 for the whole sura)
- """
- a=self.suraInfoById[suraId-1]
- m=a[5]
- if number<=0 or number>m: number=m
- return self.getAyatIter(a[3]+fromAya-1, number)
+class othmanCore(object):
+ SQL_GET_AYAT = 'SELECT othmani, imlai FROM Quran WHERE id>=? ORDER BY id LIMIT ?'
+ SQL_GET_SURA_INFO = 'SELECT rowid, sura_name, other_names, makki, starting_row, comment FROM SuraInfo ORDER BY rowid'
+ def __init__(self, load_ix=True):
+ self.data_dir = d = guessDataDir()
+ self.db_fn = db_fn = os.path.join(d, 'quran.db')
+ self._cn = {}
+ cn = self._getConnection()
+ l = list(cn.execute(self.SQL_GET_SURA_INFO).fetchall())
+ if len(l) != 114:
+ raise IOError
+ self.suraIdByName = dict(((i[1], i[0]) for i in l))
+ self.suraInfoById = [list(i[1:]) + [0] for i in l]
+ for i in range(113):
+ self.suraInfoById[i][5] = self.suraInfoById[i + 1][3] - self.suraInfoById[i][3]
+ self.suraInfoById[-1][5] = 6
+ self.basmala, self.basmala_imlai = list(self.getAyatIter(1))[0]
+ self.basmala = self.basmala[:self.basmala.rfind(' ')]
+ self.ix = None
+ if load_ix:
+ self.ix = searchIndexer()
+
+ def _getConnection(self):
+ n = threading.current_thread().name
+ if self._cn.has_key(n):
+ r = self._cn[n]
+ else:
+ r = sqlite3.connect(self.db_fn)
+ self._cn[n] = r
+ return r
+
+ def showSunnahBasmala(self, sura):
+ return sura != 1 and sura != 9
+
+ def suraAyaFromAyaId(self, ayaId):
+ sura = cmp_bisect_right(lambda i, j: cmp(i[3], j), self.suraInfoById, ayaId)
+ aya = ayaId - self.suraInfoById[sura-1][3] + 1
+ return sura, aya
+
+ def ayaIdFromSuraAya(self, suraId, aya = 1):
+ """
+ suraId: sura number from 1 to 114
+ aya: aya number from 1 to the end of the sura
+ """
+ return self.suraInfoById[suraId-1][3] + aya - 1
+
+ def getAyatIter(self, ayaId, number = 1):
+ """
+ return a list of (othmani, imlai) tuples
+ """
+ return self._getConnection().execute(self.SQL_GET_AYAT, (ayaId,number))
+
+ def getSuraIter(self, suraId, number=0, fromAya=1):
+ """
+ return a list of (othmani, imlai) tuples for the whole sura
+ suraId: sura number from 1 to 114
+ number of ayat (0 for the whole sura)
+ """
+ a = self.suraInfoById[suraId - 1]
+ m = a[5]
+ if number <= 0 or number > m:
+ number = m
+ return self.getAyatIter(a[3] + fromAya - 1, number)
normalize_tb={
-65: 97, 66: 98, 67: 99, 68: 100, 69: 101, 70: 102, 71: 103, 72: 104, 73: 105, 74: 106, 75: 107, 76: 108, 77: 109, 78: 110, 79: 111, 80: 112, 81: 113, 82: 114, 83: 115, 84: 116, 85: 117, 86: 118, 87: 119, 88: 120, 89: 121, 90: 122,
-1600: None, 1569: 1575, 1570: 1575, 1571: 1575, 1572: 1575, 1573: 1575, 1574: 1575, 1577: 1607, 1611: None, 1612: None, 1613: None, 1614: None, 1615: None, 1616: None, 1617: None, 1618: None, 1609: 1575}
-
-def normalize(s): return s.translate(normalize_tb)
+ 65: 97, 66: 98, 67: 99, 68: 100, 69: 101, 70: 102,
+ 71: 103, 72: 104, 73: 105, 74: 106, 75: 107, 76: 108,
+ 77: 109, 78: 110, 79: 111, 80: 112, 81: 113, 82: 114,
+ 83: 115, 84: 116, 85: 117, 86: 118, 87: 119, 88: 120,
+ 89: 121, 90: 122, 1600: None, 1569: 1575, 1570: 1575,
+ 1571: 1575, 1572: 1575, 1573: 1575, 1574: 1575, 1577: 1607,
+ 1611: None, 1612: None, 1613: None, 1614: None, 1615: None,
+ 1616: None, 1617: None, 1618: None, 1609: 1575}
+
+def normalize(s):
+ return s.translate(normalize_tb)
class searchIndexerItem(set):
- def __init__(self,*a):
- set.__init__(self, *a)
+ def __init__(self, *a):
+ set.__init__(self, *a)
+
+ def __or__(self, y):
+ return self.union(y)
- def __or__(self, y):
- return self.union(y)
+ def __and__(self, y):
+ return self.intersection(y)
- def __and__(self, y):
- return self.intersection(y)
-
- def toAyaIdList(self):
- l=list(self)
- l.sort()
- return l
+ def toAyaIdList(self):
+ l = list(self)
+ l.sort()
+ return l
class searchIndexer:
- def __init__(self, unlink=False, normalize=normalize):
- d=guessDataDir()
- self.db_fn = fn = os.path.join(d, "ix.db")
- if unlink and os.path.exists(fn): os.unlink(fn)
- self._cn = {}
- self.d={}
- self.normalize=normalize
- self.maxWordLen=0
- self.term_vectors_size=0
- self.terms_count=0
-
- def _getConnection(self):
- n = threading.current_thread().name
- if self._cn.has_key(n):
- r = self._cn[n]
- else:
- r = sqlite3.connect(self.db_fn)
- self._cn[n] = r
- return r
-
- def save(self):
- cn = self._getConnection()
- c=cn.cursor()
- c.execute('CREATE TABLE ix (w TEXT PRIMARY KEY NOT NULL, i BLOB)')
- for w in self.d:
- b=varuints.incremental_encode(self.d[w].toAyaIdList())
- self.term_vectors_size+=len(b)
- c.execute( 'INSERT INTO ix VALUES(?,?)', (w, sqlite3.Binary(b)) )
- self.terms_count=len(self.d.keys())
- cn.commit()
-
- def _itemFactory(self, r):
- a=varuints.incremental_decode(r[1])
- return r[0], searchIndexerItem(a)
-
- def _itemFactory2(self, r):
- a=varuints.incremental_decode(r[1])
- return searchIndexerItem(a)
-
- def get(self, w):
- cn = self._getConnection()
- r=cn.execute('SELECT w, i FROM ix WHERE w=?', (w,)).fetchone()
- if not r: return None, None
- return self._itemFactory(r)
-
- def getPartial(self, w, withWords=False):
- if "%" in w or "_" in w: return [] # special chars
- cn = self._getConnection()
- W="%"+w+"%"
- f=withWords and self._itemFactory or self._itemFactory2
- r=cn.execute('SELECT w, i FROM ix WHERE w LIKE ?', (W, ))
- if not r: return []
- return imap(lambda i: f(i), r)
-
- def find(self, words):
- if not words: return None
- w=self.normalize(words[0])
- W,x=self.get(w)
- if not x: return None
- for w in words[1:]:
- W,y=self.get(self.normalize(w))
- if not y: return None
- x&=y
- return x.toAyaIdList()
-
- def findPartial(self, words):
- if not words: return None
- w=self.normalize(words[0])
- x=reduce( lambda a,b: a|b, self.getPartial(w), searchIndexerItem() )
- for W in words[1:]:
- w=self.normalize(W)
- y=reduce( lambda a,b: a|b, self.getPartial(w), searchIndexerItem() )
- x&=y
- return x.toAyaIdList()
-
- def addWord(self, word, ayaId):
- w=self.normalize(word)
- #if not w: print word; return
- self.maxWordLen=max(self.maxWordLen,len(w))
- if self.d.has_key(w):
- self.d[w].add(ayaId)
- else:
- self.d[w]=searchIndexerItem((ayaId,))
+ def __init__(self, unlink = False, normalize = normalize):
+ d = guessDataDir()
+ self.db_fn = fn = os.path.join(d, "ix.db")
+ if unlink and os.path.exists(fn):
+ os.unlink(fn)
+ self._cn = {}
+ self.d = {}
+ self.normalize = normalize
+ self.maxWordLen = 0
+ self.term_vectors_size = 0
+ self.terms_count = 0
+
+ def _getConnection(self):
+ n = threading.current_thread().name
+ if self._cn.has_key(n):
+ r = self._cn[n]
+ else:
+ r = sqlite3.connect(self.db_fn)
+ self._cn[n] = r
+ return r
+
+ def save(self):
+ cn = self._getConnection()
+ c = cn.cursor()
+ c.execute('CREATE TABLE ix (w TEXT PRIMARY KEY NOT NULL, i BLOB)')
+ for w in self.d:
+ b = univaruints.incremental_encode(self.d[w].toAyaIdList())
+ self.term_vectors_size += len(b)
+ c.execute( 'INSERT INTO ix VALUES(?,?)', (w, sqlite3.Binary(b)) )
+ self.terms_count = len(self.d.keys())
+ cn.commit()
+
+ def _itemFactory(self, r):
+ a = univaruints.incremental_decode(r[1])
+ return r[0], searchIndexerItem(a)
+
+ def _itemFactory2(self, r):
+ a = univaruints.incremental_decode(r[1])
+ return searchIndexerItem(a)
+
+ def get(self, w):
+ cn = self._getConnection()
+ r = cn.execute('SELECT w, i FROM ix WHERE w=?', (w,)).fetchone()
+ if not r:
+ return None, None
+ return self._itemFactory(r)
+
+ def getPartial(self, w, withWords=False):
+ if "%" in w or "_" in w:
+ return [] # special chars
+ cn = self._getConnection()
+ W = "%" + w + "%"
+ f = withWords and self._itemFactory or self._itemFactory2
+ r = cn.execute('SELECT w, i FROM ix WHERE w LIKE ?', (W, ))
+ if not r:
+ return []
+ return imap(lambda i: f(i), r)
+
+ def find(self, words):
+ if not words:
+ return None
+ w = self.normalize(words[0])
+ W, x = self.get(w)
+ if not x:
+ return None
+ for w in words[1:]:
+ W, y = self.get(self.normalize(w))
+ if not y:
+ return None
+ x &= y
+ return x.toAyaIdList()
+
+ def findPartial(self, words):
+ if not words:
+ return None
+ w = self.normalize(words[0])
+ x = reduce( lambda a,b: a|b, self.getPartial(w), searchIndexerItem() )
+ for W in words[1:]:
+ w = self.normalize(W)
+ y = reduce( lambda a,b: a|b, self.getPartial(w), searchIndexerItem() )
+ x &= y
+ return x.toAyaIdList()
+
+ def addWord(self, word, ayaId):
+ w = self.normalize(word)
+ #if not w: print word; return
+ self.maxWordLen = max(self.maxWordLen,len(w))
+ if self.d.has_key(w):
+ self.d[w].add(ayaId)
+ else:
+ self.d[w] = searchIndexerItem((ayaId,))
diff --git a/othman/gtkUi.py b/othman/gtkUi.py
index 7f1ceea..aa82061 100644
--- a/othman/gtkUi.py
+++ b/othman/gtkUi.py
@@ -5,391 +5,420 @@ gtkUi - gtk user interface for Othman API
Copyright © 2008-2010, Muayyad Alsadi <alsadi at ojuba.org>
- Released under terms of Waqf Public License.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the latest version Waqf Public License as
- published by Ojuba.org.
+ Released under terms of Waqf Public License.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the latest version Waqf Public License as
+ published by Ojuba.org.
- 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.
+ 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.
- The Latest version of the license can be found on
- "http://waqf.ojuba.org/license"
+ The Latest version of the license can be found on
+ "http://waqf.ojuba.org/license"
"""
import sys, os, os.path, time
import gettext
-import pango
-import glib
-import gtk
-
+from gi.repository import Gtk, Gdk, GLib, Pango, GdkPixbuf
from core import othmanCore, searchIndexer
-class searchWindow(gtk.Window):
- def __init__(self, w):
- gtk.Window.__init__(self)
- self.w=w
- self.connect('delete-event', lambda w,*a: w.hide() or True)
- self.last_txt = None
- self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
- self.set_modal(True)
- self.set_deletable(True)
- self.set_title(_('Search results'))
- self.set_transient_for(w)
- vb=gtk.VBox(False,0); self.add(vb)
- self.search=gtk.Entry()
- self.search.set_width_chars(15)
- vb.pack_start(self.search, False,False, 0)
- self.scroll=gtk.ScrolledWindow()
- self.scroll.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
- vb.pack_start(self.scroll,True, True, 6)
-
- self.ls = gtk.ListStore(int,str,int,int)
- self.cells=[]; self.cols=[]
- self.cells.append(gtk.CellRendererText())
- self.cols.append(gtk.TreeViewColumn(_('Sura'), self.cells[0], text=1))
- self.cols[0].set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
- self.cols[0].set_resizable(True)
-
- self.cells.append(gtk.CellRendererText()); # self.cols[-1].set_expand(False)
- self.ls_w=gtk.TreeView(self.ls)
- self.ls_w.connect("cursor-changed", self.move)
- self.ls_w.set_headers_visible(False)
- for i in self.cols: self.ls_w.insert_column(i, -1)
- self.scroll.add(self.ls_w)
- self.search.connect("activate", self.search_cb)
- self.show_all()
-
- def move(self, t):
- a=self.ls_w.get_selection().get_selected()
- if not a or len(a)<2 or not a[1]: return
- sa=self.ls[self.ls.get_path(a[1])[0]]
- self.w.sura_c.set_active(sa[2]-1)
- self.w.viewAya(sa[3], sa[2])
-
- def search_cb(self, b, *a):
- t=b.get_text()
- self.w.search.set_text(t)
- self.find(t)
-
- def find(self, txt, backward=False):
- txt=txt.strip()
- if not txt: self.hide(); return
- if type(txt)==str: txt=txt.decode('utf-8')
- if txt==self.last_txt:
- # TODO: just move cursor to next/prev result before showing it
- pass
- else:
- self.search.set_text(txt)
- self.last_txt=txt
- self.ls.clear()
- for i in self.w.ix.findPartial(txt.split()):
- sura,aya=self.w.suraAyaFromAyaId(i)
- name=self.w.suraInfoById[sura-1][0]
- self.ls.append([i, "%03d %s - %03d" % (sura, name, aya), sura, aya,])
- self.ls_w.set_cursor((0,))
- self.show_all()
-
-class othmanUi(gtk.Window, othmanCore):
- def __init__(self):
- gtk.window_set_default_icon_name('Othman')
- gtk.Window.__init__(self)
- othmanCore.__init__(self)
- self.sw = None
- self.lastSearchText=None
- self.lastSearchResult=[]
- self.ix=searchIndexer()
- self.set_title(_('Othman Quran Browser'))
- self.connect("delete_event", self.quit)
- self.set_default_size(600, 480)
-
- self.clip1=gtk.Clipboard(selection="PRIMARY")
- self.clip2=gtk.Clipboard(selection="CLIPBOARD")
- self.accel=gtk.AccelGroup()
-
- vb=gtk.VBox(False,0); self.add(vb)
- hb=gtk.HBox(False,2)
- vb.pack_start(hb,False, False, 0)
-
- self.scroll=gtk.ScrolledWindow()
- self.scroll.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
- self.scroll.connect_after("size-allocate", self.resize_cb)
- vb.pack_start(self.scroll,True, True, 6)
-
- img=gtk.Image()
- img.set_from_stock(gtk.STOCK_COPY,gtk.ICON_SIZE_BUTTON)
- b=gtk.Button()
- b.add(img)
- b.connect("clicked", self.show_cp_dlg)
- hb.pack_start(b,False, False, 0)
- hb.pack_start(gtk.VSeparator(),False, False, 6)
- hb.pack_start(gtk.Label(_("Sura")),False, False, 0)
-
- self.sura_ls=tuple("%d. %s" % (i+1,j[0]) for (i,j) in enumerate(self.suraInfoById))
- self.sura_c = gtk.combo_box_new_text()
- self.sura_c.set_wrap_width(5)
- for i in self.sura_ls: self.sura_c.append_text(i)
- self.sura_c.set_tooltip_text(_("choose a Sura"))
- self.sura_c.connect("changed", self.sura_changed_cb)
- hb.pack_start(self.sura_c,False, False, 0)
-
- hb.pack_start(gtk.VSeparator(),False, False, 6)
- img=gtk.Image()
- img.set_from_stock(gtk.STOCK_ZOOM_IN, gtk.ICON_SIZE_BUTTON)
- b=gtk.Button()
- b.add(img)
- hb.pack_start(b, False, False, 0)
- b.connect("clicked", self.zoomIn)
-
- img=gtk.Image()
- img.set_from_stock(gtk.STOCK_ZOOM_OUT, gtk.ICON_SIZE_BUTTON)
- b=gtk.Button()
- b.add(img)
- hb.pack_start(b, False, False, 0)
- b.connect("clicked", self.zoomOut)
-
- img=gtk.Image()
- img.set_from_stock(gtk.STOCK_MEDIA_FORWARD, gtk.ICON_SIZE_BUTTON)
- b=gtk.ToggleButton()
- b.add(img)
- hb.pack_start(b, False, False, 0)
- self.autoScrolling=False
- b.connect("clicked", self.autoScrollCb)
- glib.timeout_add(100, self.autoScroll, b)
-
- hb.pack_start(gtk.VSeparator(), False, False, 6)
- hb.pack_start(gtk.image_new_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON), False, False, 0)
- search=gtk.Entry(); search.set_width_chars(15)
- hb.pack_start(search, False,False, 0)
- search.connect("activate", self.search_cb)
- self.search=search
-
- hb.pack_start(gtk.VSeparator(),False, False, 6)
- img=gtk.Image()
- img.set_from_stock(gtk.STOCK_ABOUT, gtk.ICON_SIZE_BUTTON)
- b=gtk.Button()
- b.add(img)
- hb.pack_start(b, False, False, 0)
- b.connect("clicked", self.about)
-
-
- self.scale=1
- self.txt = gtk.ListStore(str,int,str)
- self.cells=[]; self.cols=[]
- self.cells.append(gtk.CellRendererText())
- #self.cols.append(gtk.TreeViewColumn('Quranic Text', self.cells[0], markup=0))
- self.cols.append(gtk.TreeViewColumn('Quranic Text', self.cells[0], text=0, foreground=2))
- self.cells[0].set_property("background","#fffff8")
- #self.cells[0].set_property("foreground","#204000")
- #self.cells[0].set_property("alignment",pango.ALIGN_CENTER)
- self.cells[0].set_property("wrap-mode",pango.WRAP_WORD_CHAR)
- self.cells[0].set_property("wrap-width",500)
- self.cells[0].set_property("font","Simplified Naskh 32")
- #self.cells[0].set_property("font","KFGQPC Uthmanic Script HAFS 32")
- self.cells[0].set_property("scale", self.scale)
- self.cols[0].set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
-
- self.cells.append(gtk.CellRendererText()); # self.cols[-1].set_expand(False)
- self.txt_list=gtk.TreeView(self.txt)
- self.txt_list.set_headers_visible(False)
- for i in self.cols: self.txt_list.insert_column(i, -1)
-
- self.scroll.add(self.txt_list)
- self.sura_c.set_active(0)
- self.build_cp_dlg()
- self.build_about_dlg()
- self.show_all()
-
- def build_about_dlg(self):
- self.about_w=gtk.AboutDialog()
- self.about_w.set_default_response(gtk.RESPONSE_CLOSE)
- self.about_w.connect('delete-event', lambda w,*a: w.hide() or True)
- self.about_w.connect('response', lambda w,*a: w.hide() or True)
- try: self.about_w.set_program_name("Othman")
- except: pass
- self.about_w.set_name(_('Othman Quran Browser'))
- #self.about_w.set_version(version)
- self.about_w.set_copyright("Copyright © 2008-2010 Muayyad Saleh Alsadi <alsadi at ojuba.org>")
- self.about_w.set_comments(_("Electronic Mus-haf"))
- self.about_w.set_license("""
- Released under terms of Waqf Public License.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the latest version Waqf Public License as
- published by Ojuba.org.
-
- 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.
-
- The Latest version of the license can be found on
- "http://waqf.ojuba.org/"
-
-""")
- self.about_w.set_website("http://othman.ojuba.org/")
- self.about_w.set_website_label("http://othman.ojuba.org")
- self.about_w.set_authors(["Muayyad Saleh Alsadi <alsadi at ojuba.org>"])
- self.about_w.set_translator_credits(_("translator-credits"))
- fn=os.path.join(self.data_dir, "quran-kareem.svg")
- try: logo=gtk.gdk.pixbuf_new_from_file_at_size(fn, 128, 128)
- except:
- fn=os.path.join(self.data_dir, "quran-kareem.png")
- logo=gtk.gdk.pixbuf_new_from_file(fn)
- self.about_w.set_logo(logo)
-
-
- def about(self, b):
- self.about_w.show_all()
-
- def search_cb(self, b, *a):
- if not self.sw: self.sw = searchWindow(self)
- self.sw.find(b.get_text())
-
- def autoScroll(self, b):
- if not self.autoScrolling: return True
- v=self.scroll.get_vadjustment()
- m=v.upper-v.page_size
- n=min(m, v.get_value() + 2 )
- if n==m: b.set_active(False)
- v.set_value(n)
- return True
-
- def autoScrollCb(self, b, *a):
- self.autoScrolling=b.get_active()
-
- def zoomIn(self, *a):
- sura,aya=self.getCurrentSuraAya()
- self.scale+=0.1
- self.cells[0].set_property("scale", self.scale)
- self.resize_cb()
- self.queue_draw()
- self.viewSura(sura)
- self.viewAya(aya)
-
- def zoomOut(self, *a):
- sura,aya=self.getCurrentSuraAya()
- self.scale-=0.1
- self.scale=max(0.2, self.scale)
- self.cells[0].set_property("scale", self.scale)
- self.resize_cb()
- self.queue_draw()
- self.viewSura(sura)
- self.viewAya(aya)
-
- def viewAya(self, aya, sura=None):
- if sura==None: sura=self.sura_c.get_active()+1
- aya=max(1,abs(aya))
- i=aya+int(self.showSunnahBasmala(sura))
- self.txt_list.scroll_to_cell((i-1,))
- self.txt_list.get_selection().select_path((i-1,))
-
- def viewSura(self, i):
- #self.play_pause.set_active(False)
- self.txt.clear()
- if self.showSunnahBasmala(i):
- #self.txt.append(['<span foreground="#440000">%s</span>' % self.basmala,0,])
- self.txt.append([self.basmala,0,"#802000",])
- for j,k in enumerate(self.getSuraIter(i)):
- self.txt.append([k[0],j+1,"#204000",])
- self.resize_cb()
- self.scroll.get_vadjustment().set_value(0)
- self.txt_list.get_selection().select_path((0,))
-
- def sura_changed_cb(self, c, *a):
- self.viewSura(self.sura_c.get_active()+1)
-
- def resize_cb(self,*args):
- if self.cols[0].get_width()>10: self.cells[0].set_property("wrap-width",self.cols[0].get_width()-10)
-
- def cp_cb(self, *a):
- sura=self.cp_sura.get_active()+1
- aya1=self.cp_from.get_value()
- aya2=self.cp_to.get_value()
- n=aya2-aya1+1
- i=self.cp_is_imlai.get_active()
- a=[' ','\n',' * ', ' *\n']
- s=a[int(i)*2+int(self.cp_aya_perline.get_active())]
- s=s.join([l[i] for l in self.getSuraIter(sura, n, aya1)])+'\n'
- self.clip1.set_text(s)
- self.clip2.set_text(s)
- self.cp_w.hide()
-
- def cp_sura_cb(self, *a):
- sura=self.cp_sura.get_active()+1
- m=self.suraInfoById[sura-1][5]
- self.cp_from.set_range(1, m)
- self.cp_to.set_range(1, m)
- self.cp_from.set_value(1)
- self.cp_to.set_value(m)
-
- def show_cp_dlg(self, *a):
- sura,aya=self.getCurrentSuraAya()
- aya=max(1, abs(aya))
- self.cp_sura.set_active(sura-1)
- self.cp_sura_cb()
- self.cp_from.set_value(aya)
- self.cp_w.show_all()
-
- def build_cp_dlg(self):
- self.cp_w = gtk.Window()
- self.cp_w.set_title(_('Copy to clipboard'))
- self.cp_w.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
- self.cp_w.connect('delete-event', lambda w,*a: w.hide() or True)
- self.cp_sura = gtk.combo_box_new_text(); self.cp_sura.set_wrap_width(5)
-
- for i in self.sura_ls: self.cp_sura.append_text(i)
- self.cp_sura.set_tooltip_text(_("choose a Sura"))
-
- self.cp_from=gtk.SpinButton(gtk.Adjustment(0, 0, 286, 1, 10, 0))
- self.cp_to=gtk.SpinButton(gtk.Adjustment(0, 0, 286, 1, 10, 0))
- self.cp_is_imlai=gtk.CheckButton(_("Imla'i style"))
- self.cp_aya_perline=gtk.CheckButton(_("an Aya per line"))
- self.cp_ok=gtk.Button(stock=gtk.STOCK_OK)
- self.cp_cancel=gtk.Button(stock=gtk.STOCK_CANCEL)
- vb=gtk.VBox(False,0)
- self.cp_w.add(vb)
- hb=gtk.HBox(False,3)
- vb.pack_start(hb,True,True,3)
- hb.pack_start(gtk.Label("سورة"),False,False,3)
- hb.pack_start(self.cp_sura,False,False,6)
- hb=gtk.HBox(False,6)
- vb.pack_start(hb,True,True,6)
- hb.pack_start(gtk.Label("الآيات من"),False,False,3)
- hb.pack_start(self.cp_from,False,False,3)
- hb.pack_start(gtk.Label("إلى"),False,False,3)
- hb.pack_start(self.cp_to,False,False,3)
- hb=gtk.HBox(False,6)
- vb.pack_start(hb,True,True,6)
- hb.pack_start(self.cp_is_imlai,False,False,3)
- hb.pack_start(self.cp_aya_perline,False,False,3)
- hb=gtk.HBox(False,6)
- vb.pack_start(hb,True,True,6)
- hb.pack_start(self.cp_ok,False,False,6)
- hb.pack_start(self.cp_cancel,False,False,6)
- self.cp_sura.connect("changed", self.cp_sura_cb)
- self.cp_cancel.connect('clicked', lambda *args: self.cp_w.hide())
- self.cp_ok.connect('clicked', self.cp_cb)
- self.cp_is_imlai.set_active(True)
-
- def getCurrentSuraAya(self):
- a=self.txt_list.get_selection().get_selected()
- aya=1
- if a: aya=self.txt[self.txt.get_path(a[1])[0]][1]
- aya=max(aya,1)
- return self.sura_c.get_active()+1, aya
-
- def quit(self,*args):
- gtk.main_quit()
- return False
+class searchWindow(Gtk.Window):
+ def __init__(self, w):
+ Gtk.Window.__init__(self)
+ self.w = w
+ self.connect('delete-event', lambda w,*a: w.hide() or True)
+ self.last_txt = None
+ self.set_type_hint(Gdk.WindowTypeHint.UTILITY)
+ self.set_modal(True)
+ self.set_deletable(True)
+ self.set_title(_('Search results'))
+ self.set_transient_for(w)
+ vb = Gtk.VBox(False,0)
+ self.add(vb)
+
+ self.search = Gtk.Entry()
+ self.search.set_width_chars(15)
+ vb.pack_start(self.search, False,False, 0)
+
+ self.scroll = Gtk.ScrolledWindow()
+ self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC,Gtk.PolicyType.AUTOMATIC)
+ self.scroll.set_size_request(100, 250)
+ vb.pack_start(self.scroll,True, True, 6)
+
+ self.ls = Gtk.ListStore(int,str,int,int)
+ self.cells = []
+ self.cols = []
+ self.cells.append(Gtk.CellRendererText())
+ self.cols.append(Gtk.TreeViewColumn(_('Sura'), self.cells[0], text=1))
+ self.cols[0].set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+ self.cols[0].set_resizable(True)
+
+ self.cells.append(Gtk.CellRendererText()); # self.cols[-1].set_expand(False)
+ self.ls_w = Gtk.TreeView(self.ls)
+ self.ls_w.connect("cursor-changed", self.move)
+ self.ls_w.set_direction(Gtk.TextDirection.RTL)
+ self.ls_w.set_headers_visible(False)
+ for i in self.cols:
+ self.ls_w.insert_column(i, -1)
+ self.scroll.add(self.ls_w)
+ self.search.connect("activate", self.search_cb)
+ self.show_all()
+
+ def move(self, t):
+ a = self.ls_w.get_selection().get_selected()
+ if not a or len(a) < 2 or not a[1]:
+ return
+ sa = self.ls[self.ls.get_path(a[1])]
+ self.w.sura_c.set_active(sa[2]-1)
+ self.w.viewAya(sa[3], sa[2])
+
+ def search_cb(self, b, *a):
+ t = b.get_text()
+ self.w.search.set_text(t)
+ self.find(t)
+
+ def find(self, txt, backward = False):
+ txt = txt.strip()
+ if not txt:
+ self.hide()
+ return
+ if type(txt) == str:
+ txt = txt.decode('utf-8')
+ if txt == self.last_txt:
+ # TODO: just move cursor to next/prev result before showing it
+ pass
+ else:
+ self.search.set_text(txt)
+ self.last_txt = txt
+ self.ls.clear()
+ for i in self.w.ix.findPartial(txt.split()):
+ sura, aya = self.w.suraAyaFromAyaId(i)
+ name = self.w.suraInfoById[sura-1][0]
+ self.ls.append([i, "%03d %s - %03d" % (sura, name, aya), sura, aya,])
+ self.ls_w.set_cursor(Gtk.TreePath(path=0), None, False)
+ self.show_all()
+
+class othmanUi(Gtk.Window, othmanCore):
+ def __init__(self):
+ Gtk.Window.set_default_icon_name('Othman')
+ Gtk.Window.__init__(self)
+ othmanCore.__init__(self)
+ self.sw = None
+ self.lastSearchText = None
+ self.lastSearchResult = []
+ self.ix = searchIndexer()
+ self.set_title(_('Othman Quran Browser'))
+ self.connect("delete_event", self.quit)
+ self.connect('destroy', self.quit)
+ self.set_default_size(600, 480)
+ self.maximize()
+ self.clip1 = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
+ self.clip2 = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
+ self.accel = Gtk.AccelGroup()
+
+ vb = Gtk.VBox(False,0)
+ self.add(vb)
+ hb = Gtk.HBox(False,2)
+ vb.pack_start(hb, False, False, 0)
+
+ self.scroll = Gtk.ScrolledWindow()
+ self.scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
+ self.scroll.connect_after("size-allocate", self.resize_cb)
+ vb.pack_start(self.scroll, True, True, 6)
+
+ img = Gtk.Image()
+ img.set_from_stock(Gtk.STOCK_COPY, Gtk.IconSize.BUTTON)
+ b = Gtk.Button()
+ b.add(img)
+ b.connect("clicked", self.show_cp_dlg)
+ hb.pack_start(b,False, False, 0)
+ hb.pack_start(Gtk.VSeparator(), False, False, 6)
+ hb.pack_start(Gtk.Label(_("Sura")), False, False, 0)
+
+ self.sura_ls = tuple("%d. %s" % (i+1,j[0]) for (i,j) in enumerate(self.suraInfoById))
+ self.sura_c = Gtk.ComboBoxText.new()
+ self.sura_c.set_wrap_width(5)
+ for i in self.sura_ls:
+ self.sura_c.append_text(i)
+ self.sura_c.set_tooltip_text(_("choose a Sura"))
+ self.sura_c.connect("changed", self.sura_changed_cb)
+ hb.pack_start(self.sura_c, False, False, 0)
+
+ hb.pack_start(Gtk.VSeparator(),False, False, 6)
+ img = Gtk.Image()
+ img.set_from_stock(Gtk.STOCK_ZOOM_IN, Gtk.IconSize.BUTTON)
+ b = Gtk.Button()
+ b.add(img)
+ hb.pack_start(b, False, False, 0)
+ b.connect("clicked", self.zoomIn)
+
+ img = Gtk.Image()
+ img.set_from_stock(Gtk.STOCK_ZOOM_OUT, Gtk.IconSize.BUTTON)
+ b = Gtk.Button()
+ b.add(img)
+ hb.pack_start(b, False, False, 0)
+ b.connect("clicked", self.zoomOut)
+
+ img = Gtk.Image()
+ img.set_from_stock(Gtk.STOCK_MEDIA_FORWARD, Gtk.IconSize.BUTTON)
+ b = Gtk.ToggleButton()
+ b.add(img)
+ hb.pack_start(b, False, False, 0)
+ self.autoScrolling = False
+ b.connect("clicked", self.autoScrollCb)
+ GLib.timeout_add(100, self.autoScroll, b)
+
+ hb.pack_start(Gtk.VSeparator(), False, False, 6)
+ hb.pack_start(Gtk.Image.new_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.BUTTON), False, False, 0)
+ search = Gtk.Entry(); search.set_width_chars(15)
+ hb.pack_start(search, False,False, 0)
+ search.connect("activate", self.search_cb)
+ self.search = search
+
+ hb.pack_start(Gtk.VSeparator(),False, False, 6)
+ img = Gtk.Image()
+ img.set_from_stock(Gtk.STOCK_ABOUT, Gtk.IconSize.BUTTON)
+ b = Gtk.Button()
+ b.add(img)
+ hb.pack_start(b, False, False, 0)
+ b.connect("clicked", lambda *a: self.show_about_dlg(self))
+
+
+ self.scale = 1
+ self.txt = Gtk.ListStore(str,int,str)
+ self.cells = []
+ self.cols = []
+ self.cells.append(Gtk.CellRendererText())
+ #self.cols.append(Gtk.TreeViewColumn('Quranic Text', self.cells[0], markup=0))
+ self.cols.append(Gtk.TreeViewColumn('Quranic Text', self.cells[0], text = 0, foreground = 2))
+ self.cells[0].set_property("background","#fffff8")
+ #self.cells[0].set_property("foreground","#204000")
+ #self.cells[0].set_property("alignment",Pango.ALIGN_CENTER)
+ self.cells[0].set_property("wrap-mode", Pango.WrapMode.WORD)
+ self.cells[0].set_property("wrap-width", 500)
+ self.cells[0].set_property("font", "Simplified Naskh 32")
+ #self.cells[0].set_property("font","KFGQPC Uthmanic Script HAFS 32")
+ self.cells[0].set_property("scale", self.scale)
+ self.cols[0].set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+
+ self.cells.append(Gtk.CellRendererText()); # self.cols[-1].set_expand(False)
+ self.txt_list = Gtk.TreeView(self.txt)
+ self.txt_list.set_headers_visible(False)
+ self.txt_list.set_direction(Gtk.TextDirection.RTL)
+ for i in self.cols:
+ self.txt_list.insert_column(i, -1)
+
+ self.scroll.add(self.txt_list)
+ self.sura_c.set_active(0)
+ self.build_cp_dlg()
+ self.show_all()
+
+ def show_about_dlg(self, parent):
+ dlg = Gtk.AboutDialog()
+ dlg.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+ dlg.set_modal(True)
+ dlg.set_transient_for(parent)
+ dlg.set_default_response(Gtk.ResponseType.CLOSE)
+ dlg.connect('delete-event', lambda w,*a: w.hide() or True)
+ dlg.connect('response', lambda w,*a: w.hide() or True)
+ try:
+ dlg.set_program_name("Othman")
+ except:
+ pass
+ dlg.set_name(_('Othman Quran Browser'))
+ #dlg.set_version(version)
+ dlg.set_copyright("Copyright © 2008-2010 Muayyad Saleh Alsadi <alsadi at ojuba.org>")
+ dlg.set_comments(_("Electronic Mus-haf"))
+ dlg.set_license("""
+ Released under terms of Waqf Public License.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the latest version Waqf Public License as
+ published by Ojuba.org.
+
+ 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.
+
+ The Latest version of the license can be found on
+ "http://waqf.ojuba.org/"
+
+ """)
+ dlg.set_website("http://othman.ojuba.org/")
+ dlg.set_website_label("http://othman.ojuba.org")
+ dlg.set_authors(["Muayyad Saleh Alsadi <alsadi at ojuba.org>"])
+ dlg.set_translator_credits(_("translator-credits"))
+ fn = os.path.join(self.data_dir, "quran-kareem.svg")
+ try:
+ logo = GdkPixbuf.Pixbuf.new_from_file_at_size(fn, 128, 128)
+ except:
+ fn = os.path.join(self.data_dir, "quran-kareem.png")
+ logo = GdkPixbuf.pixbuf_new_from_file(fn)
+ dlg.set_logo(logo)
+ #dlg.set_logo_icon_name('Othman')
+ dlg.run()
+ dlg.destroy()
+
+ def search_cb(self, b, *a):
+ if not self.sw:
+ self.sw = searchWindow(self)
+ self.sw.find(b.get_text())
+
+ def autoScroll(self, b):
+ if not self.autoScrolling:
+ return True
+ v = self.scroll.get_vadjustment()
+ m = v.get_upper() - v.get_page_size()
+ n = min(m, v.get_value() + 2 )
+ if n == m:
+ b.set_active(False)
+ v.set_value(n)
+ return True
+
+ def autoScrollCb(self, b, *a):
+ self.autoScrolling = b.get_active()
+
+ def zoomIn(self, *a):
+ sura, aya = self.getCurrentSuraAya()
+ self.scale += 0.1
+ self.cells[0].set_property("scale", self.scale)
+ self.resize_cb()
+ self.queue_draw()
+ self.viewSura(sura)
+ self.viewAya(aya)
+
+ def zoomOut(self, *a):
+ sura, aya = self.getCurrentSuraAya()
+ self.scale -= 0.1
+ self.scale = max(0.2, self.scale)
+ self.cells[0].set_property("scale", self.scale)
+ self.resize_cb()
+ self.queue_draw()
+ self.viewSura(sura)
+ self.viewAya(aya)
+
+ def viewAya(self, aya, sura = None):
+ if sura == None:
+ sura = self.sura_c.get_active() + 1
+ aya = max(1,abs(aya))
+ i = aya + int(self.showSunnahBasmala(sura))
+ self.txt_list.scroll_to_cell((i - 1,))
+ self.txt_list.get_selection().select_path((i - 1,))
+
+ def viewSura(self, i):
+ #self.play_pause.set_active(False)
+ self.txt.clear()
+ if self.showSunnahBasmala(i):
+ #self.txt.append(['<span foreground="#440000">%s</span>' % self.basmala,0,])
+ self.txt.append([self.basmala, 0, "#802000",])
+ for j, k in enumerate(self.getSuraIter(i)):
+ self.txt.append([k[0], j + 1, "#204000",])
+ self.resize_cb()
+ self.scroll.get_vadjustment().set_value(0)
+ self.txt_list.get_selection().select_path((0,))
+
+ def sura_changed_cb(self, c, *a):
+ self.viewSura(self.sura_c.get_active() + 1)
+
+ def resize_cb(self,*args):
+ if self.cols[0].get_width() > 10:
+ self.cells[0].set_property("wrap-width", self.cols[0].get_width() - 10)
+
+ def cp_cb(self, *a):
+ sura = self.cp_sura.get_active() + 1
+ aya1 = self.cp_from.get_value()
+ aya2 = self.cp_to.get_value()
+ n = aya2 - aya1 + 1
+ i = self.cp_is_imlai.get_active()
+ a = [' ', '\n', ' * ', ' *\n']
+ s = a[int(i) * 2 + int(self.cp_aya_perline.get_active())]
+ s = s.join([l[i] for l in self.getSuraIter(sura, n, aya1)]) + '\n'
+ self.clip1.set_text(s, -1)
+ self.clip2.set_text(s, -1)
+ self.cp_w.hide()
+
+ def cp_sura_cb(self, *a):
+ sura = self.cp_sura.get_active() + 1
+ m = self.suraInfoById[sura - 1][5]
+ self.cp_from.set_range(1, m)
+ self.cp_to.set_range(1, m)
+ self.cp_from.set_value(1)
+ self.cp_to.set_value(m)
+
+ def show_cp_dlg(self, *a):
+ sura, aya = self.getCurrentSuraAya()
+ aya = max(1, abs(aya))
+ self.cp_sura.set_active(sura - 1)
+ self.cp_sura_cb()
+ self.cp_from.set_value(aya)
+ self.cp_w.show_all()
+
+ def build_cp_dlg(self):
+ self.cp_w = Gtk.Window()
+ self.cp_w.set_title(_('Copy to clipboard'))
+ self.cp_w.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+ self.cp_w.connect('delete-event', lambda w,*a: w.hide() or True)
+ self.cp_sura = Gtk.ComboBoxText.new()
+ self.cp_sura.set_wrap_width(5)
+
+ for i in self.sura_ls:
+ self.cp_sura.append_text(i)
+ self.cp_sura.set_tooltip_text(_("choose a Sura"))
+ adj = Gtk.Adjustment(0, 0, 286, 1, 10, 0)
+ self.cp_from = s = Gtk.SpinButton()
+ s.set_adjustment(adj)
+ self.cp_to = s = Gtk.SpinButton()
+ s.set_adjustment(adj)
+ self.cp_is_imlai = Gtk.CheckButton(_("Imla'i style"))
+ self.cp_aya_perline = Gtk.CheckButton(_("an Aya per line"))
+ self.cp_ok = Gtk.Button(stock=Gtk.STOCK_OK)
+ self.cp_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
+ vb = Gtk.VBox(False,0)
+ self.cp_w.add(vb)
+ hb = Gtk.HBox(False,3)
+ vb.pack_start(hb,True,True,3)
+ hb.pack_start(Gtk.Label(_("Sorat:")),False,False,3)
+ hb.pack_start(self.cp_sura,False,False,6)
+ hb = Gtk.HBox(False,6)
+ vb.pack_start(hb,True,True,6)
+ hb.pack_start(Gtk.Label(_("Ayat from:")),False,False,3)
+ hb.pack_start(self.cp_from,False,False,3)
+ hb.pack_start(Gtk.Label(_("To")),False,False,3)
+ hb.pack_start(self.cp_to,False,False,3)
+ hb = Gtk.HBox(False,6)
+ vb.pack_start(hb,True,True,6)
+ hb.pack_start(self.cp_is_imlai,False,False,3)
+ hb.pack_start(self.cp_aya_perline,False,False,3)
+ hb = Gtk.HBox(False,6)
+ vb.pack_start(hb,True,True,6)
+ hb.pack_start(self.cp_ok,False,False,6)
+ hb.pack_start(self.cp_cancel,False,False,6)
+ self.cp_sura.connect("changed", self.cp_sura_cb)
+ self.cp_cancel.connect('clicked', lambda *args: self.cp_w.hide())
+ self.cp_ok.connect('clicked', self.cp_cb)
+ self.cp_is_imlai.set_active(True)
+
+ def getCurrentSuraAya(self):
+ a = self.txt_list.get_selection().get_selected()
+ aya = 1
+ if a:
+ aya = self.txt[self.txt.get_path(a[1])][1]
+ aya = max(aya, 1)
+ return self.sura_c.get_active() + 1, aya
+
+ def quit(self,*args):
+ Gtk.main_quit()
+ return False
def main():
- exedir=os.path.dirname(sys.argv[0])
- ld=os.path.join(exedir,'..','share','locale')
- if not os.path.exists(ld): ld=os.path.join(exedir, 'locale')
- gettext.install('othman', ld, unicode=0)
- w=othmanUi()
- gtk.main()
+ exedir = os.path.dirname(sys.argv[0])
+ ld = os.path.join(exedir,'..', 'share', 'locale')
+ if not os.path.exists(ld):
+ ld = os.path.join(exedir, 'locale')
+ gettext.install('othman', ld, unicode = 0)
+ w = othmanUi()
+ Gtk.main()
if __name__ == "__main__":
- main()
+ main()
diff --git a/othman/univaruints.py b/othman/univaruints.py
new file mode 100644
index 0000000..1babaf5
--- /dev/null
+++ b/othman/univaruints.py
@@ -0,0 +1,302 @@
+# -*- coding: UTF-8 -*-
+"""
+univaruints is a serialization of integer list
+Copyright © 2009-2013, Muayyad Alsadi <alsadi at ojuba.org>
+
+
+
+this implementation is unit-tested (by running this module)
+"""
+
+import struct, bisect
+int64=struct.Struct('>Q')
+shifts=[0, 128, 16512, 2113664, 270549120, 34630287488, 4432676798592, 567382630219904, 72624976668147840]
+shifts2=shifts[2:]
+n_by_chr='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x [...]
+
+def bisect_right7(a, x):
+ if x<a[3]:
+ if x<a[1]:
+ if (x<a[0]):
+ return 0
+ else:
+ return 1
+ else:
+ if x<a[2]:
+ return 2
+ else:
+ return 3
+ else:
+ if x<a[5]:
+ if x<a[4]:
+ return 4
+ else:
+ return 5
+ else:
+ if x<a[6]:
+ return 6
+ else:
+ return 7
+
+
+
+def write(f, s, max_items=0, incremental=0, unique=0, last_item=0):
+ """
+ encode the sequence s and write the string into the file-like object f
+
+ only max_items are encoded (0 mean infinity)
+ if incremental is set to True then the sequence is assumed to be incremental
+ which would result in more compact output
+ if you know that the sequence is strictly increasing then set unique=1
+ """
+ # NOTE: below some lookups before loop for optimizations
+ fwrite=f.write
+ rbisect=bisect_right7 #bisect.bisect_right
+ int64pack=int64.pack
+ char=chr
+
+ count=0
+ last_item-=unique
+ for item in s:
+ if max_items and count>=max_items: break
+ count+=1
+ v=item
+ if incremental:
+ if item<last_item+unique: raise ValueError
+ v-=last_item+unique
+ if v<128:
+ fwrite(char(v))
+ else:
+ n=rbisect(shifts2, v)+1
+ offset=shifts[n]
+ v-=offset
+ fwrite(char(((0b1111111100000000>>n) & 255) | ( (127>>n) & (v>>(n<<3)) ))+int64pack(v)[8-n:])
+ last_item=item
+ return count
+
+def read(f, max_items=0, incremental=0, unique=0, last_item=0):
+ """
+ read and decode sequence at most max_items from the file-like object
+
+ parameters are just like write
+ """
+ # NOTE: below some lookups before loop for optimizations
+ fread=f.read
+ int64unpack=int64.unpack
+ chr2int=ord
+
+ count=0
+ last_item-=unique
+
+ while True:
+ if max_items and count>=max_items: break
+ ch=fread(1)
+ if not ch: break
+ count+=1
+ o=chr2int(ch)
+ if o<128:
+ v=o
+ else:
+ n_ch=n_by_chr[o]
+ n=chr2int(n_ch)
+ mask=127>>n
+ payload=fread(n)
+ if len(payload)<n: raise IOError
+ v=shifts[n] + (((o & mask)<< (n<<3)) | ( (int64unpack(('\0'*(8-n))+payload))[0] ))
+ if incremental: v+=last_item+unique
+ yield v
+ last_item=v
+
+
+def decode_single(s):
+ """
+ return number of bytes consumed and the decoded value
+ """
+ o=ord(s[0])
+ if o<128: return 1, o
+ n_ch=n_by_chr[o]
+ n=ord(n_ch)
+ mask=127>>n
+ return n+1, shifts[n] + (((o & mask)<< (n<<3)) | ( (int64.unpack(('\0'*(8-n))+s[1:n+1]))[0] ))
+
+def decode(s):
+ "return a generator that yields all decoded integers"
+ offset=0
+ while offset<len(s):
+ o=ord(s[offset])
+ offset+=1
+ if o<128: yield o # just an optimization
+ else:
+ n_ch=n_by_chr[o]
+ n=ord(n_ch)
+ mask=127>>n
+ yield shifts[n] + (((o & mask)<< (n<<3)) | ( (int64.unpack(('\0'*(8-n))+s[offset:offset+n]))[0] ))
+ offset+=n
+
+def encode_single(v):
+ if v<128: return chr(v)
+ n=bisect_right7(shifts2, v)+1 #bisect.bisect_right(shifts2, v)+1
+ offset=shifts[n]
+ v-=offset
+ return chr(((0b1111111100000000>>n) & 255) | ( (127>>n) & (v>>(n<<3)) )) + int64.pack(v)[8-n:]
+
+def encode_single_alt(v):
+ if v<128: return chr(v) # just an optimization
+ offset=128
+ m=0
+ # enumerate was slower
+ #for i,m in enumerate(shifts2):
+ for i in shifts2: # although we can use bisect, but we only got 8 elements
+ n=m+1
+ if v<i:
+ v-=offset
+ msb=((0b1111111100000000>>n) & 255) | ( (127>>n) & (v>>(n<<3)) )
+ p=int64.pack(v)
+ return chr(msb) + p[8-n:]
+ offset=i
+ m+=1
+ #m+=1 # if enumerate is used uncomment this line
+ v-=offset
+ n=m+1
+ msb=((0b1111111100000000>>n) & 255) | ( (127>>n) & (v>>(n<<3)) )
+ p=int64.pack(v)
+ return chr(msb) + p[8-n:]
+
+def encode(a):
+ return "".join(map(encode_single, a))
+
+def incremental_encode_list(a, unique=1, last=0):
+ if unique!=1 and unique!=0: raise ValueError
+ last-=unique
+ for i in a:
+ if i<last+unique: raise ValueError
+ yield i-last-unique
+ last=i
+
+def incremental_decode_list(a, unique=1, last=0):
+ if unique!=1 and unique!=0: raise ValueError
+ last-=unique
+ for i in a:
+ j=i+last+unique
+ yield j
+ last=j
+
+def incremental_encode(a, unique=1, last=0):
+ return encode(incremental_encode_list(a, unique, last))
+
+def incremental_decode(s, unique=1, last=0):
+ return incremental_decode_list(decode(s), unique, last)
+
+#import unittest
+#class TestSequenceFunctions(unittest.TestCase):
+# def setUp(self):
+# self.seq = range(10)
+# def test_t1(self):
+# self.assertEqual(x, y)
+# self.assertRaises(TypeError, random.shuffle, (1,2,3))
+# self.assertTrue(element in self.seq)
+# in main run unittest.main()
+
+if __name__ == "__main__":
+ import time, itertools, random
+ from cStringIO import StringIO
+ boundary=[(i-1,i,i+1) for i in shifts[1:]]
+ boundary=list(itertools.chain(*boundary))
+ boundary.insert(0,0)
+ print "simple unit tests..."
+ l1=[0,1,100,200,300,500,1000,10000]
+ for i in l1:
+ print 'before dec:', i, ', hex:', hex(i), ', bin:', bin(i)
+ e=encode_single(i)
+ print 'after len:',len(e), ', str:', repr(e)
+ assert i == decode_single(encode_single(i))[1]
+ f=StringIO()
+ write(f, l1)
+ f.seek(0)
+ assert l1==list(read(f))
+ print "boundary unit tests..."
+ for i in boundary:
+ print 'before dec:', i, ', hex:', hex(i), ', bin:', bin(i)
+ e=encode_single(i)
+ print 'after len:',len(e), ', str:', repr(e)
+ assert i == decode_single(encode_single(i))[1]
+ assert boundary == list(decode(encode(boundary)))
+ assert boundary == list(incremental_decode(incremental_encode(boundary, unique=0), unique=0))
+ assert boundary == list(incremental_decode(incremental_encode(boundary, unique=1), unique=1))
+
+ f=StringIO()
+ write(f, boundary, 0, 0, 0)
+ f.seek(0)
+ assert boundary == list(read(f, 0, 0, 0))
+ f=StringIO()
+ write(f, boundary, 0, 1, 0)
+ f.seek(0)
+ assert boundary == list(read(f, 0, 1, 0))
+ f=StringIO()
+ write(f, boundary, 0, 1, 1)
+ f.seek(0)
+ assert boundary == list(read(f, 0, 1, 1))
+
+
+ print "random unit tests..."
+ l=[random.randint(0, 5000000) for i in range(1000)]
+ s=encode(l)
+ l2=list(decode(s))
+ assert l2==l
+ ll=0
+ l=[0]
+ for i in range(1000):
+ ll+=random.randint(0, 5000000)
+ l.append(ll)
+ l2=list(incremental_decode(incremental_encode(l, unique=0), unique=0))
+ assert l2==l
+
+ ll=0
+ l=[0]
+ for i in range(1000):
+ ll+=random.randint(1, 5000000)
+ l.append(ll)
+ l2=list(incremental_decode(incremental_encode(l, unique=1), unique=1))
+ assert l2==l
+
+ print "pass"
+ print "performance tests"
+ q=struct.Struct('>Q')
+ pack=lambda l: ''.join(itertools.imap(lambda i: q.pack(i), l))
+ def unpack(s):
+ for i in range(0,len(s),8):
+ yield q.unpack(s[i:i+8])[0]
+ t1=time.time()
+ for i in range(1000): list(unpack(pack(boundary)))
+ t2=time.time()
+ delta_pack=t2-t1
+ print 'struct-based done in ', delta_pack
+
+ f=StringIO()
+ t1=time.time()
+ for i in range(1000):
+ f.seek(0)
+ write(f, boundary)
+ f.seek(0)
+ list(read(f))
+ t2=time.time()
+ print 'file-like done in ', t2-t1
+
+
+ t1=time.time()
+ for i in range(1000): list(decode(encode(boundary)))
+ t2=time.time()
+ delta_our=t2-t1
+ print 'we are done in ', delta_our
+ t1=time.time()
+ for i in range(1000): encode(boundary)
+ t2=time.time()
+ delta_our=t2-t1
+ print 'we are done in encoding in ', delta_our
+ e=encode(boundary)
+ t1=time.time()
+ for i in range(1000): list(decode(e))
+ t2=time.time()
+ delta_our=t2-t1
+ print 'we are done in decoding in ', delta_our
+
diff --git a/othman/varuints.py b/othman/varuints.py
deleted file mode 100644
index 4d60e1a..0000000
--- a/othman/varuints.py
+++ /dev/null
@@ -1,170 +0,0 @@
-# -*- coding: UTF-8 -*-
-"""
-Othman - Quran browser
-varuints is a serialization of integer list
-
-Copyright © 2009-2011, Muayyad Alsadi <alsadi at ojuba.org>
-
- Released under terms of Waqf Public License.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the latest version Waqf Public License as
- published by Ojuba.org.
-
- 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.
-
- The Latest version of the license can be found on
- "http://waqf.ojuba.org/license"
-
-varuints is a serialization of integer list
-based on idea from http://code.google.com/apis/protocolbuffers/docs/encoding.html
-
-but unlike protocolbuffers it preserve order by saving most significant first
-
-in this implementation a single varuint can be something like
-
-1xxx-xxxx 1xxx-xxxx 0xxx-xxxx
-
-MSB in each byte is "has_more", and rest bits are base-128 int
-the above is xxx-xxx xxx-xxxx xxx-xxxx
-
-
-0xxx-xxxx is 0-127
-
-1xxx-xxxx 0xxx-xxxx is 128-16511
-
-and so on
-
-use it like this
-
-s=varuints.decode([150,5,7])
-a=from_varints(s)
-
-"""
-
-def decode(s):
- v=0
- for c in s:
- c=ord(c)
- v+=(c&127)
- if (c&128)==0: yield v; v=0
- else: v+=1
- v<<=7
- if v: raise ValueError
-
-def decode_single(s):
- v=0
- for c in s:
- c=ord(c)
- v+=(c&127)
- if (c&128)==0: return v; v=0
- else: v+=1
- v<<=7
- if v: raise ValueError
-
-
-def encode_single(i):
- r=""
- k=0
- j,m=0,0
- while(1):
- r=chr(m|(i&127))+r
- m=128
- j+=1
- i>>=7
- if i==0: return r
- i-=1
- raise ValueError # never reached
-
-
-def encode(a):
- r=[]
- k=0
- for i in a:
- j,m=0,0
- while(1):
- r.insert(k,chr(m|(i&127)))
- m=128
- j+=1
- i>>=7
- if i==0: break
- i-=1
- k+=j
- return "".join(r)
-
-def write(f, a):
- """encode members of a and write them to file f and return the number of bytes written"""
- s=encode(a)
- f.write(s)
- return len(s)
-
-def read(f, limit=1):
- """read and decode up to limit integers from file f and return them"""
- v,n=0,0
- while(n<limit):
- c=f.read(1)
- if c=='': raise ValueError
- c=ord(c)
- v+=(c&127)
- if (c&128)==0: yield v; v=0; n+=1
- else: v+=1
- v<<=7
- if v: raise ValueError
-
-def read_single(f):
- """read and decode a single varuint from file f and return it"""
- return read(f,1).next()
-
-
-def incremental_encode_list(a, unique=1, last=0):
- if unique!=1 and unique!=0: raise ValueError
- for i in a:
- if i<last+unique: raise ValueError
- yield i-last-unique
- last=i
-
-def incremental_decode_list(a, unique=1, last=0):
- if unique!=1 and unique!=0: raise ValueError
- for i in a:
- j=i+last+unique
- yield j
- last=j
-
-def incremental_encode(a, unique=1, last=0):
- return encode(incremental_encode_list(a, unique, last))
-
-def incremental_decode(s, unique=1, last=0):
- return incremental_decode_list(decode(s), unique, last)
-
-def incremental_write(f, a, unique=1, last=0):
- return write(f, incremental_encode_list(a, unique, last))
-
-def incremental_read(f, unique=1, limit=1, last=0):
- return incremental_decode_list(read(f, limit), unique, last)
-
-
-def decode_safe(s):
- v=0
- for c in s:
- c=safe_ord(c)
- v<<=6
- v+=c&63
- if (c&64)==0: yield v; v=0
- if v: raise
-
-def encode_safe(a):
- r=[]
- k=0
- for i in a:
- j,m=0,0
- while(i>0):
- r.insert(k,safe_chr(m|(i&63)))
- i>>=6
- m=64
- j+=1
- if j==0: r.insert(k,safe_chr(0)); k+=1
- else: k+=j
- return "".join(r)
-
-
diff --git a/po/ar.po b/po/ar.po
index 723f3c3..b849d58 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -6,50 +6,62 @@ msgid ""
msgstr ""
"Project-Id-Version: othman 0.2.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-04 17:44+0300\n"
+"POT-Creation-Date: 2012-06-04 05:56+0200\n"
"PO-Revision-Date: 2010-04-28 18:31+0300\n"
"Last-Translator: alsadi <alsadi at ojuba.org>\n"
"Language-Team: thawab at ojuba.org\n"
+"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Generator: Virtaal 0.5.0\n"
-#: ../Othman.desktop.in.h:1 ../othman/gtkUi.py:205
+#: ../Othman.desktop.in.h:1 ../othman/gtkUi.py:232
msgid "Electronic Mus-haf"
msgstr "المصحف الإلكتروني"
-#: ../Othman.desktop.in.h:2 ../othman/gtkUi.py:96 ../othman/gtkUi.py:202
+#: ../Othman.desktop.in.h:2 ../othman/gtkUi.py:111 ../othman/gtkUi.py:229
msgid "Othman Quran Browser"
msgstr "مصحف عثمان الإلكتروني"
-#: ../othman/gtkUi.py:37
+#: ../othman/gtkUi.py:35
msgid "Search results"
msgstr "نتائج البحث"
-#: ../othman/gtkUi.py:50 ../othman/gtkUi.py:120
+#: ../othman/gtkUi.py:53 ../othman/gtkUi.py:137
msgid "Sura"
msgstr "سورة"
-#: ../othman/gtkUi.py:126 ../othman/gtkUi.py:327
+#: ../othman/gtkUi.py:144 ../othman/gtkUi.py:366
msgid "choose a Sura"
msgstr "اختر سورة"
-#: ../othman/gtkUi.py:223
+#: ../othman/gtkUi.py:250
msgid "translator-credits"
msgstr "مؤيد صالح السعدي <alsadi at ojuba.org>"
-#: ../othman/gtkUi.py:321
+#: ../othman/gtkUi.py:358
msgid "Copy to clipboard"
msgstr "نسخ إلى الحافظة"
-#: ../othman/gtkUi.py:331
+#: ../othman/gtkUi.py:372
msgid "Imla'i style"
msgstr "بالرسم الإملائي"
-#: ../othman/gtkUi.py:332
+#: ../othman/gtkUi.py:373
msgid "an Aya per line"
msgstr "كل آية على كل سطر مستقل"
+
+#: ../othman/gtkUi.py:380
+msgid "Sorat:"
+msgstr "سورة :"
+
+#: ../othman/gtkUi.py:384
+msgid "Ayat from:"
+msgstr "الآيات من:"
+
+#: ../othman/gtkUi.py:386
+msgid "To"
+msgstr "إلى :"
diff --git a/po/de.po b/po/de.po
index 8d21a59..4cebb5a 100644
--- a/po/de.po
+++ b/po/de.po
@@ -7,52 +7,59 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-04 17:44+0300\n"
+"POT-Creation-Date: 2012-06-04 05:56+0200\n"
"PO-Revision-Date: 2010-08-27 23:29+0100\n"
"Last-Translator: cegerxwin <cegerxwin at web.de>\n"
"Language-Team: LANGUAGE <LL at li.org>\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../Othman.desktop.in.h:1
-#: ../othman/gtkUi.py:205
+#: ../Othman.desktop.in.h:1 ../othman/gtkUi.py:232
msgid "Electronic Mus-haf"
msgstr "Elektronischer Mus-Haf"
-#: ../Othman.desktop.in.h:2
-#: ../othman/gtkUi.py:96
-#: ../othman/gtkUi.py:202
+#: ../Othman.desktop.in.h:2 ../othman/gtkUi.py:111 ../othman/gtkUi.py:229
msgid "Othman Quran Browser"
msgstr "Othman Koran Browser"
-#: ../othman/gtkUi.py:37
+#: ../othman/gtkUi.py:35
msgid "Search results"
msgstr "Suchergebnisse"
-#: ../othman/gtkUi.py:50
-#: ../othman/gtkUi.py:120
+#: ../othman/gtkUi.py:53 ../othman/gtkUi.py:137
msgid "Sura"
msgstr "Sure"
-#: ../othman/gtkUi.py:126
-#: ../othman/gtkUi.py:327
+#: ../othman/gtkUi.py:144 ../othman/gtkUi.py:366
msgid "choose a Sura"
msgstr "Wähle eine Sure aus"
-#: ../othman/gtkUi.py:223
+#: ../othman/gtkUi.py:250
msgid "translator-credits"
msgstr "Übersetzer"
-#: ../othman/gtkUi.py:321
+#: ../othman/gtkUi.py:358
msgid "Copy to clipboard"
msgstr "In Zwischenablage kopieren"
-#: ../othman/gtkUi.py:331
+#: ../othman/gtkUi.py:372
msgid "Imla'i style"
msgstr "Imla'i Stil"
-#: ../othman/gtkUi.py:332
+#: ../othman/gtkUi.py:373
msgid "an Aya per line"
msgstr "eine Aya pro Zeile"
+#: ../othman/gtkUi.py:380
+msgid "Sorat:"
+msgstr ""
+
+#: ../othman/gtkUi.py:384
+msgid "Ayat from:"
+msgstr ""
+
+#: ../othman/gtkUi.py:386
+msgid "To"
+msgstr ""
diff --git a/po/othman.pot b/po/othman.pot
index cd85fe3..43d7cfb 100644
--- a/po/othman.pot
+++ b/po/othman.pot
@@ -8,46 +8,59 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-04 17:44+0300\n"
+"POT-Creation-Date: 2012-06-04 05:56+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at li.org>\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../Othman.desktop.in.h:1 ../othman/gtkUi.py:205
+#: ../Othman.desktop.in.h:1 ../othman/gtkUi.py:232
msgid "Electronic Mus-haf"
msgstr ""
-#: ../Othman.desktop.in.h:2 ../othman/gtkUi.py:96 ../othman/gtkUi.py:202
+#: ../Othman.desktop.in.h:2 ../othman/gtkUi.py:111 ../othman/gtkUi.py:229
msgid "Othman Quran Browser"
msgstr ""
-#: ../othman/gtkUi.py:37
+#: ../othman/gtkUi.py:35
msgid "Search results"
msgstr ""
-#: ../othman/gtkUi.py:50 ../othman/gtkUi.py:120
+#: ../othman/gtkUi.py:53 ../othman/gtkUi.py:137
msgid "Sura"
msgstr ""
-#: ../othman/gtkUi.py:126 ../othman/gtkUi.py:327
+#: ../othman/gtkUi.py:144 ../othman/gtkUi.py:366
msgid "choose a Sura"
msgstr ""
-#: ../othman/gtkUi.py:223
+#: ../othman/gtkUi.py:250
msgid "translator-credits"
msgstr ""
-#: ../othman/gtkUi.py:321
+#: ../othman/gtkUi.py:358
msgid "Copy to clipboard"
msgstr ""
-#: ../othman/gtkUi.py:331
+#: ../othman/gtkUi.py:372
msgid "Imla'i style"
msgstr ""
-#: ../othman/gtkUi.py:332
+#: ../othman/gtkUi.py:373
msgid "an Aya per line"
msgstr ""
+
+#: ../othman/gtkUi.py:380
+msgid "Sorat:"
+msgstr ""
+
+#: ../othman/gtkUi.py:384
+msgid "Ayat from:"
+msgstr ""
+
+#: ../othman/gtkUi.py:386
+msgid "To"
+msgstr ""
diff --git a/waqf2-ar.pdf b/waqf2-ar.pdf
new file mode 100644
index 0000000..b4a0f47
Binary files /dev/null and b/waqf2-ar.pdf differ
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-islamic/packages/othman.git
More information about the Debian-islamic-commits
mailing list