From 281db1fb8186b33a152a795989b7a169997c9c27 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 08:47:32 -0700 Subject: [PATCH 1/5] Arabian Business by DM. Fixes #8199 (New recipe for Arabian Business newsportal in English) --- resources/images/news/arabian_business.png | Bin 0 -> 4213 bytes resources/recipes/arabian_business.recipe | 86 +++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 resources/images/news/arabian_business.png create mode 100644 resources/recipes/arabian_business.recipe diff --git a/resources/images/news/arabian_business.png b/resources/images/news/arabian_business.png new file mode 100644 index 0000000000000000000000000000000000000000..e9498309885eeda4078f3b6ad312232fbf5d4bb6 GIT binary patch literal 4213 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 QbW_SSZy~ii|HG*VsM(Jy@ zo~x^J?ec2H%pOh^$ldX^#cN#ez|pi2VDYMb^D4gF#Ijz;ovV+##M1!rpmW~;{J2{O zuQ)hWrDpt{r(MyM=an)ARb2Vn6y?l65O@>vdYyyH7{u*#%!YLSkhG`i$Al+&vnB48>D|G8&iVyB5gro@ji#S ziOU;u-%7oi4XT+Yq~vgX9z$^K~WK;z!<953L2U! zs+y__AO&?z1qD*bn9Tn(`1&I}ABX*4gQ9|_;vG$8wf{9VY~MU)7yu>)7W#Ny=a~Ni D1%5d$ literal 0 HcmV?d00001 diff --git a/resources/recipes/arabian_business.recipe b/resources/recipes/arabian_business.recipe new file mode 100644 index 0000000000..8b41c99e68 --- /dev/null +++ b/resources/recipes/arabian_business.recipe @@ -0,0 +1,86 @@ +__license__ = 'GPL v3' +__copyright__ = '2011, Darko Miletic ' +''' +www.arabianbusiness.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Arabian_Business(BasicNewsRecipe): + title = 'Arabian Business' + __author__ = 'Darko Miletic' + description = 'Comprehensive Guide to Middle East Business & Gulf Industry News including,Banking & Finance,Construction,Energy,Media & Marketing,Real Estate,Transportation,Travel,Technology,Politics,Healthcare,Lifestyle,Jobs & UAE guide.Top Gulf & Dubai Business News.' + publisher = 'Arabian Business Publishing Ltd.' + category = 'ArabianBusiness.com,Arab Business News,Middle East Business News,Middle East Business,Arab Media News,Industry Events,Middle East Industry News,Arab Business Industry,Dubai Business News,Financial News,UAE Business News,Middle East Press Releases,Gulf News,Arab News,GCC Business News,Banking Finance,Media Marketing,Construction,Oil Gas,Retail,Transportation,Travel Hospitality,Photos,Videos,Life Style,Fashion,United Arab Emirates,UAE,Dubai,Sharjah,Abu Dhabi,Qatar,KSA,Saudi Arabia,Bahrain,Kuwait,Oman,Europe,South Asia,America,Asia,news' + oldest_article = 2 + max_articles_per_feed = 200 + no_stylesheets = True + encoding = 'utf8' + use_embedded_content = False + language = 'en' + remove_empty_feeds = True + publication_type = 'newsportal' + masthead_url = 'http://www.arabianbusiness.com/skins/ab.main/gfx/arabianbusiness_logo_sm.gif' + extra_css = """ + body{font-family: Georgia,serif } + img{margin-bottom: 0.4em; margin-top: 0.4em; display:block} + .byline,.dateline{font-size: small; display: inline; font-weight: bold} + ul{list-style: none outside none;} + """ + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + remove_tags_before=dict(attrs={'id':'article-title'}) + remove_tags = [ + dict(name=['meta','link','base','iframe','embed','object']) + ,dict(attrs={'class':'printfooter'}) + ] + remove_attributes=['lang'] + + + feeds = [ + (u'Africa' , u'http://www.arabianbusiness.com/world/Africa/?service=rss' ) + ,(u'Americas' , u'http://www.arabianbusiness.com/world/americas/?service=rss' ) + ,(u'Asia Pacific' , u'http://www.arabianbusiness.com/world/asia-pacific/?service=rss' ) + ,(u'Europe' , u'http://www.arabianbusiness.com/world/europe/?service=rss' ) + ,(u'Middle East' , u'http://www.arabianbusiness.com/world/middle-east/?service=rss' ) + ,(u'South Asia' , u'http://www.arabianbusiness.com/world/south-asia/?service=rss' ) + ,(u'Banking & Finance', u'http://www.arabianbusiness.com/industries/banking-finance/?service=rss' ) + ,(u'Construction' , u'http://www.arabianbusiness.com/industries/construction/?service=rss' ) + ,(u'Education' , u'http://www.arabianbusiness.com/industries/education/?service=rss' ) + ,(u'Energy' , u'http://www.arabianbusiness.com/industries/energy/?service=rss' ) + ,(u'Healthcare' , u'http://www.arabianbusiness.com/industries/healthcare/?service=rss' ) + ,(u'Media' , u'http://www.arabianbusiness.com/industries/media/?service=rss' ) + ,(u'Real Estate' , u'http://www.arabianbusiness.com/industries/real-estate/?service=rss' ) + ,(u'Retail' , u'http://www.arabianbusiness.com/industries/retail/?service=rss' ) + ,(u'Technology' , u'http://www.arabianbusiness.com/industries/technology/?service=rss' ) + ,(u'Transport' , u'http://www.arabianbusiness.com/industries/transport/?service=rss' ) + ,(u'Travel' , u'http://www.arabianbusiness.com/industries/travel-hospitality/?service=rss') + ,(u'Equities' , u'http://www.arabianbusiness.com/markets/equities/?service=rss' ) + ,(u'Commodities' , u'http://www.arabianbusiness.com/markets/commodities/?service=rss' ) + ,(u'Currencies' , u'http://www.arabianbusiness.com/markets/currencies/?service=rss' ) + ,(u'Market Data' , u'http://www.arabianbusiness.com/markets/market-data/?service=rss' ) + ,(u'Comment' , u'http://www.arabianbusiness.com/opinion/comment/?service=rss' ) + ,(u'Think Tank' , u'http://www.arabianbusiness.com/opinion/think-tank/?service=rss' ) + ,(u'Arts' , u'http://www.arabianbusiness.com/lifestyle/arts/?service=rss' ) + ,(u'Cars' , u'http://www.arabianbusiness.com/lifestyle/cars/?service=rss' ) + ,(u'Food' , u'http://www.arabianbusiness.com/lifestyle/food/?service=rss' ) + ,(u'Sport' , u'http://www.arabianbusiness.com/lifestyle/sport/?service=rss' ) + ] + + def print_version(self, url): + return url + '?service=printer&page=' + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + for alink in soup.findAll('a'): + if alink.string is not None: + tstr = alink.string + alink.replaceWith(tstr) + return soup From af5d7eef227952e70eb0284f312fe121ff979595 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 08:49:03 -0700 Subject: [PATCH 2/5] Deia by Gerardo Diez. Fixes #405 (New news feed) --- resources/recipes/deia.recipe | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 resources/recipes/deia.recipe diff --git a/resources/recipes/deia.recipe b/resources/recipes/deia.recipe new file mode 100644 index 0000000000..980d59d3d1 --- /dev/null +++ b/resources/recipes/deia.recipe @@ -0,0 +1,70 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'Gerardo Diez' +__copyright__ = 'Gerardo Diez' +description = 'Main daily newspaper from Spain - v1.00 (05, Enero 2011)' +__docformat__ = 'restructuredtext en' + +''' +deia.com +''' +from calibre.web.feeds.recipes import BasicNewsRecipe + +class Deia(BasicNewsRecipe): + title ='Deia' + __author__ ='Gerardo Diez' + publisher ='Editorial Iparraguirre, S.A' + category ='news, politics, finances, world, spain, euskadi' + publication_type ='newspaper' + oldest_article =1 + max_articles_per_feed =100 + simultaneous_downloads =10 + cover_url ='http://2.bp.blogspot.com/_RjrWzC6tI14/TM6jrPLaBZI/AAAAAAAAFaI/ayffwxidFEY/s1600/2009-10-13-logo-deia.jpg' + timefmt ='[%a, %d %b, %Y]' + encoding ='utf8' + language ='es_ES' + remove_javascript =True + remove_tags_after =dict(id='Texto') + remove_tags_before =dict(id='Texto') + remove_tags =[dict(name='div', attrs={'class':['Herramientas ', 'Multimedia']})] + no_stylesheets =True + extra_css ='h1 {margin-bottom: .15em;font-size: 2.7em; font-family: Georgia, "Times New Roman", Times, serif;} .Antetitulo {margin: 1em 0;text-transform: uppercase;color: #999;} .PieFoto {margin: .1em 0;padding: .5em .5em .5em .5em;background: #F0F0F0;} .PieFoto p {margin-bottom: 0;font-family: Georgia,"Times New Roman",Times,serif;font-weight: bold; font-style: italic; color: #666;}' + keep_only_tags =[dict(name='div', attrs={'class':['Texto ', 'NoticiaFicha ']})] + feeds = [ + (u'Bizkaia' ,u'http://www.deia.com/index.php/services/rss?seccion=bizkaia'), + (u'Bilbao' ,u'http://www.deia.com/index.php/services/rss?seccion=bilbao'), + (u'Hemendik eta Handik' ,u'http://www.deia.com/index.php/services/rss?seccion=hemendik-eta-handik'), + (u'Margen Derecha' ,u'http://www.deia.com/index.php/services/rss?seccion=margen-derecha'), + (u'Encartaciones y Margen Izquierda' ,u'http://www.deia.com/index.php/services/rss?seccion=margen-izquierda-encartaciones'), + (u'Costa' ,u'http://www.deia.com/index.php/services/rss?seccion=costa'), + (u'Duranguesado' ,u'http://www.deia.com/index.php/services/rss?seccion=duranguesado'), + (u'Llodio-Nervión' ,u'http://www.deia.com/index.php/services/rss?seccion=llodio-nervion'), + (u'Arratia-Nervión' ,u'http://www.deia.com/index.php/services/rss?seccion=arratia-nervion'), + (u'Uribe-Txorierri' ,u'http://www.deia.com/index.php/services/rss?seccion=uribe-txorierri'), + (u'Ecos de sociedad' ,u'http://www.deia.com/index.php/services/rss?seccion=ecos-de-sociedad'), + (u'Sucesos' ,u'http://www.deia.com/index.php/services/rss?seccion=sucesos'), + (u'Política' ,u'http://www.deia.com/index.php/services/rss?seccion=politica'), + (u'Euskadi' ,u'http://www.deia.com/index.php/services/rss?seccion=politica/euskadi'), + (u'España' ,u'http://www.deia.com/index.php/services/rss?seccion=politica/espana'), + (u'Sociedad',u'http://www.deia.com/index.php/services/rss?seccion=sociedad'), + (u'Euskadi' ,u'http://www.deia.com/index.php/services/rss?seccion=socidad/euskadi'), + (u'Sociedad.España' ,u'http://www.deia.com/index.php/services/rss?seccion=sociedad/espana'), + (u'Ocio y Cultura' ,u'http://www.deia.com/index.php/services/rss?seccion=ocio-y-cultura'), + #(u'Cultura' ,u'http://www.deia.com/index.php/services/rss?seccion=cultura'), + #(u'Ocio' ,u'http://www.deia.com/index.php/services/rss?seccion=ocio'), + (u'On' ,u'http://www.deia.com/index.php/services/rss?seccion=on'), + (u'Agenda' ,u'http://www.deia.com/index.php/services/rss?seccion=agenda'), + (u'Comunicación' ,u'http://www.deia.com/index.php/services/rss?seccion=comunicacion'), + (u'Viajes' ,u'http://www.deia.com/index.php/services/rss?seccion=viajes'), + (u'¡Mundo!' ,u'http://www.deia.com/index.php/services/rss?seccion=que-mundo'), + (u'Humor' ,u'http://www.deia.com/index.php/services/rss?seccion=humor'), + (u'Opinión' ,u'http://www.deia.com/index.php/services/rss?seccion=opinion'), + (u'Editorial' ,u'http://www.deia.com/index.php/services/rss?seccion=editorial'), + (u'Tribuna abierta' ,u'http://www.deia.com/index.php/services/rss?seccion=tribuna-abierta'), + (u'Colaboración' ,u'http://www.deia.com/index.php/services/rss?seccion=colaboracion'), + (u'Columnistas' ,u'http://www.deia.com/index.php/services/rss?seccion=columnistas'), + (u'Deportes' ,u'http://www.deia.com/index.php/services/rss?seccion=deportes'), + (u'Athletic' ,u'http://www.deia.com/index.php/services/rss?seccion=athletic'), + (u'Economía' ,'http://www.deia.com/index.php/services/rss?seccion=economia'), + (u'Mundo' ,u'http://www.deia.com/index.php/services/rss?seccion=mundo')] + From 164ef37cc8955d7bb8562201f416eee43470bcae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 09:41:44 -0700 Subject: [PATCH 3/5] Beginning of a libwmf wrapper --- src/calibre/utils/wmf/__init__.py | 9 ++ src/calibre/utils/wmf/wmf.c | 200 ++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 src/calibre/utils/wmf/__init__.py create mode 100644 src/calibre/utils/wmf/wmf.c diff --git a/src/calibre/utils/wmf/__init__.py b/src/calibre/utils/wmf/__init__.py new file mode 100644 index 0000000000..68dfb8d2b5 --- /dev/null +++ b/src/calibre/utils/wmf/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + + diff --git a/src/calibre/utils/wmf/wmf.c b/src/calibre/utils/wmf/wmf.c new file mode 100644 index 0000000000..1f8e8a27f3 --- /dev/null +++ b/src/calibre/utils/wmf/wmf.c @@ -0,0 +1,200 @@ +#define UNICODE +#define PY_SSIZE_T_CLEAN +#include + +#include +#include + +typedef struct { + char *data; + size_t len; + size_t pos; +} buf; + +//This code is taken mostly from the Abiword wmf plugin + + +// returns unsigned char cast to int, or EOF +static int wmf_WMF_read(void * context) { + char c; + buf *info = (buf*)context; + + if (info->pos == info->len) + return EOF; + + c = info->data[pos]; + + info->pos++; + + return (int)c; +} + +// returns (-1) on error, else 0 +static int wmf_WMF_seek(void * context, long pos) { + buf* info = (buf*) context; + + if (pos < 0 || (size_t)pos > info->len) return -1; + info->pos = (size_t)pos; + return 0; +} + +// returns (-1) on error, else pos +static long wmf_WMF_tell(void * context) { + buf* info = (buf*) context; + + return (long) info->pos; +} + + +#define CLEANUP if(API) { if (stream) wmf_free(API, stream); wmf_api_destroy(API); }; + +static PyObject * +wmf_render(PyObject *self, PyObject *args) { + char *data; + Py_ssize_t sz; + PyObject *ans; + + unsigned int disp_width = 0; + unsigned int disp_height = 0; + + float wmf_width; + float wmf_height; + float ratio_wmf; + float ratio_bounds; + + unsigned long flags; + + unsigned int max_width = 1600; + unsigned int max_height = 1200; + unsigned long max_flags = 0; + + static const char* Default_Description = "wmf2svg"; + + wmf_error_t err; + + wmf_svg_t* ddata = 0; + + wmfAPI* API = 0; + wmfD_Rect bbox; + + wmfAPI_Options api_options; + + buf read_info; + + char *stream = NULL; + unsigned long stream_len = 0; + + if (!PyArg_ParseTuple(args, "s#", &data, &sz)) + return NULL; + + flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION; + api_options.function = wmf_svg_function; + + err = wmf_api_create(&API, flags, &api_options); + + if (err != wmf_E_None) { + CLEANUP; + return PyErr_NoMemory(); + } + + read_info.data = data; + read_info.len = sz; + read_info.pos = 0; + + err = wmf_bbuf_input(API, wmf_WMF_read, wmf_WMF_seek, wmf_WMF_tell, (void *) &read_info); + if (err != wmf_E_None) { + CLEANUP; + PyErr_SetString(PyExc_Exception, "Failed to initialize WMF input"); + return NULL; + } + + err = wmf_scan(API, 0, &(bbox)); + if (err != wmf_E_None) + { + CLEANUP; + PyErr_SetString(PyExc_ValueError, "Failed to scan the WMF"); + return NULL; + } + +/* Okay, got this far, everything seems cool. + */ + ddata = WMF_SVG_GetData (API); + + ddata->out = wmf_stream_create(API, NULL); + + ddata->Description = (char *)Default_Description; + + ddata->bbox = bbox; + + wmf_display_size(API, &disp_width, &disp_height, 96, 96); + + wmf_width = (float) disp_width; + wmf_height = (float) disp_height; + + if ((wmf_width <= 0) || (wmf_height <= 0)) { + CLEANUP; + PyErr_SetString(PyExc_ValueError, "Bad WMF image size"); + return NULL; + } + + if ((wmf_width > (float) max_width ) + || (wmf_height > (float) max_height)) { + ratio_wmf = wmf_height / wmf_width; + ratio_bounds = (float) max_height / (float) max_width; + + if (ratio_wmf > ratio_bounds) { + ddata->height = max_height; + ddata->width = (unsigned int) ((float) ddata->height / ratio_wmf); + } + else { + ddata->width = max_width; + ddata->height = (unsigned int) ((float) ddata->width * ratio_wmf); + } + } + else { + ddata->width = (unsigned int) ceil ((double) wmf_width ); + ddata->height = (unsigned int) ceil ((double) wmf_height); + } + + ddata->flags |= WMF_SVG_INLINE_IMAGES; + + ddata->flags |= WMF_GD_OUTPUT_MEMORY | WMF_GD_OWN_BUFFER; + + err = wmf_play(API, 0, &(bbox)); + + if (err != wmf_E_None) { + CLEANUP; + PyErr_SetString(PyExc_ValueError, "Playing of the WMF file failed"); + return NULL; + } + + wmf_stream_destroy(API, ddata->out, &stream, &stream_len); + + ans = Py_BuildValue("s#", stream, stream_len); + + wmf_free(API, stream); + wmf_api_destroy (API); + + return ans; +} + + +static PyMethodDef wmf_methods[] = { + {"render", wmf_render, METH_VARARGS, + "render(path) -> Render wmf as svg." + }, + + {NULL} /* Sentinel */ +}; + + +PyMODINIT_FUNC +initwmf(void) +{ + PyObject* m; + m = Py_InitModule3("wmf", wmf_methods, + "Wrapper for the libwmf library"); + + +} + From 7e309d5d28c159c2b2f425af392937f25ff256a1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 11:00:24 -0700 Subject: [PATCH 4/5] Search replace: Add ability to manipulate number and boolean columns. Add type ahead completion to the advanced search dialog. Fixes #8035 (Advanced Search, Titel/Author?series/Tag - Type Ahead Word Lists) --- src/calibre/gui2/dialogs/metadata_bulk.py | 5 ++- src/calibre/gui2/dialogs/search.py | 44 +++++++++++++++++++---- src/calibre/gui2/dialogs/search.ui | 27 +++++++++++--- src/calibre/library/custom_columns.py | 22 ++++++++++-- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index ef14c95b1d..e1ee4327f3 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -321,7 +321,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): if (f in ['author_sort'] or (fm[f]['datatype'] in ['text', 'series', 'enumeration'] and fm[f].get('search_terms', None) - and f not in ['formats', 'ondevice', 'sort'])): + and f not in ['formats', 'ondevice', 'sort']) or + fm[f]['datatype'] in ['int', 'float', 'bool'] ): self.all_fields.append(f) self.writable_fields.append(f) if f in ['sort'] or fm[f]['datatype'] == 'composite': @@ -431,6 +432,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): val = mi.get('title_sort', None) else: val = mi.get(field, None) + if isinstance(val, (int, float, bool)): + val = str(val) if val is None: val = [] if fm['is_multiple'] else [''] elif not fm['is_multiple']: diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 8e8fd09652..62a0f8a9f1 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal ' import re, copy -from PyQt4.QtGui import QDialog, QDialogButtonBox +from PyQt4.Qt import QDialog, QDialogButtonBox, QCompleter, Qt from calibre.gui2.dialogs.search_ui import Ui_Dialog from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH @@ -22,6 +22,28 @@ class SearchDialog(QDialog, Ui_Dialog): key=lambda x: sort_key(x if x[0] != '#' else x[1:])) self.general_combo.addItems(searchables) + all_authors = db.all_authors() + all_authors.sort(key=lambda x : sort_key(x[1])) + for i in all_authors: + id, name = i + name = name.strip().replace('|', ',') + self.authors_box.addItem(name) + self.authors_box.setEditText('') + self.authors_box.completer().setCompletionMode(QCompleter.PopupCompletion) + self.authors_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive) + + all_series = db.all_series() + all_series.sort(key=lambda x : sort_key(x[1])) + for i in all_series: + id, name = i + self.series_box.addItem(name) + self.series_box.setEditText('') + self.series_box.completer().setCompletionMode(QCompleter.PopupCompletion) + self.series_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive) + + all_tags = db.all_tags() + self.tags_box.update_tags_cache(all_tags) + self.box_last_values = copy.deepcopy(box_values) if self.box_last_values: for k,v in self.box_last_values.items(): @@ -121,26 +143,34 @@ class SearchDialog(QDialog, Ui_Dialog): return tok def box_search_string(self): + mk = self.matchkind.currentIndex() + if mk == CONTAINS_MATCH: + self.mc = '' + elif mk == EQUALS_MATCH: + self.mc = '=' + else: + self.mc = '~' + ans = [] self.box_last_values = {} title = unicode(self.title_box.text()).strip() self.box_last_values['title_box'] = title if title: - ans.append('title:"' + title + '"') + ans.append('title:"' + self.mc + title + '"') author = unicode(self.authors_box.text()).strip() self.box_last_values['authors_box'] = author if author: - ans.append('author:"' + author + '"') + ans.append('author:"' + self.mc + author + '"') series = unicode(self.series_box.text()).strip() self.box_last_values['series_box'] = series if series: - ans.append('series:"' + series + '"') - self.mc = '=' + ans.append('series:"' + self.mc + series + '"') + tags = unicode(self.tags_box.text()) self.box_last_values['tags_box'] = tags - tags = self.tokens(tags) + tags = [t.strip() for t in tags.split(',') if t.strip()] if tags: - tags = ['tags:' + t for t in tags] + tags = ['tags:"=' + t + '"' for t in tags] ans.append('(' + ' or '.join(tags) + ')') general = unicode(self.general_box.text()) self.box_last_values['general_box'] = general diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 7bb4c15363..6848a45506 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -21,7 +21,7 @@ - What kind of match to use: + &What kind of match to use: matchkind @@ -228,7 +228,7 @@ - + Enter the title. @@ -265,21 +265,21 @@ - + Enter an author's name. Only one author can be used. - + Enter a series name, without an index. Only one series name can be used. - + Enter tags separated by spaces @@ -348,6 +348,23 @@ + + + EnLineEdit + QLineEdit +
widgets.h
+
+ + EnComboBox + QComboBox +
widgets.h
+
+ + TagsLineEdit + QLineEdit +
widgets.h
+
+
all phrase diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index 07ea407460..ba218c3ecc 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -133,7 +133,15 @@ class CustomColumns(object): def adapt_bool(x, d): if isinstance(x, (str, unicode, bytes)): - x = bool(int(x)) + x = x.lower() + if x == 'true': + x = True + elif x == 'false': + x = False + elif x == 'none': + x = None + else: + x = bool(int(x)) return x def adapt_enum(x, d): @@ -142,9 +150,17 @@ class CustomColumns(object): v = None return v + def adapt_number(x, d): + if isinstance(x, (str, unicode, bytes)): + if x.lower() == 'none': + return None + if d['datatype'] == 'int': + return int(x) + return float(x) + self.custom_data_adapters = { - 'float': lambda x,d : x if x is None else float(x), - 'int': lambda x,d : x if x is None else int(x), + 'float': adapt_number, + 'int': adapt_number, 'rating':lambda x,d : x if x is None else min(10., max(0., float(x))), 'bool': adapt_bool, 'comments': lambda x,d: adapt_text(x, {'is_multiple':False}), From e1bbefba8390859b0a7a50d24633eb4c0245ecb0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 12:15:37 -0700 Subject: [PATCH 5/5] Fix #4442 (Make input character encoding selection more user-friendly) --- src/calibre/gui2/convert/look_and_feel.ui | 17 ++++++++++++--- src/calibre/gui2/widgets.py | 25 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/convert/look_and_feel.ui b/src/calibre/gui2/convert/look_and_feel.ui index 367233e2c0..0edc324dc5 100644 --- a/src/calibre/gui2/convert/look_and_feel.ui +++ b/src/calibre/gui2/convert/look_and_feel.ui @@ -127,9 +127,6 @@ - - - @@ -244,8 +241,22 @@ + + + + true + + + + + + EncodingComboBox + QComboBox +
widgets.h
+
+
diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index bc3c23876f..d87bb45f7a 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -616,6 +616,31 @@ class ComboBoxWithHelp(QComboBox): QComboBox.hidePopup(self) self.set_state() + +class EncodingComboBox(QComboBox): + ''' + A combobox that holds text encodings support + by Python. This is only populated with the most + common and standard encodings. There is no good + way to programatically list all supported encodings + using encodings.aliases.aliases.keys(). It + will not work. + ''' + + ENCODINGS = ['', 'cp1252', 'latin1', 'utf-8', '', 'ascii', 'big5', 'cp1250', 'cp1251', 'cp1253', + 'cp1254', 'cp1255', 'cp1256', 'euc_jp', 'euc_kr', 'gb2312', 'gb18030', + 'hz', 'iso2022_jp', 'iso2022_kr', 'iso8859_5', 'shift_jis', + ] + + def __init__(self, parent=None): + QComboBox.__init__(self, parent) + self.setEditable(True) + self.setLineEdit(EnLineEdit(self)) + + for item in self.ENCODINGS: + self.addItem(item) + + class PythonHighlighter(QSyntaxHighlighter): Rules = []