o _@sdZddlZddlZddlZddlZddlZz ddlmmZWne y3ddl mm ZYnwddl m Z mZddlmZddlmZddlmZddlZddlZddZd d Zd Zd Zd ZdZdZdZGdddZ GdddZ!GdddZ"GdddZ#GdddZ$GdddZ%GdddZ&d\Z'Z(d d!Z)d"d#Z*d$d%Z+d&d'Z,Gd(d)d)e-Z.Gd*d+d+Z/d/d-d.Z0dS)0a Implementation of the XDG Menu Specification http://standards.freedesktop.org/menu-spec/ Example code: from xdg.Menu import parse, Menu, MenuEntry def print_menu(menu, tab=0): for submenu in menu.Entries: if isinstance(submenu, Menu): print (" " * tab) + unicode(submenu) print_menu(submenu, tab+1) elif isinstance(submenu, MenuEntry): print (" " * tab) + unicode(submenu.DesktopEntry) print_menu(parse()) N) xdg_data_dirsxdg_config_dirs) DesktopEntry) ParsingError)PY3cCsDtjdkrt|}tjdkrt|St|Stj|tdS)N))ridctx)sys version_infoast literal_evalConstant NameConstantNameLoad)namer*/usr/lib/python3/dist-packages/xdg/Menu.py _ast_const's     rcCs"ts t|tr |d}t|S)zgWrapper around locale.strxfrm that accepts unicode strings on Python 2. See Python bug #2481. zutf-8)r isinstanceunicodeencodelocalestrxfrm)srrr_strxfrm2s  rDeleted NoDisplayHiddenEmpty NotShowInNoExecc@seZdZdZddZddZddZdd Zd d Zd d Z ddZ d(ddZ d(ddZ ddZ d)ddZddZddZddZdd Zd!d"Z d#d$Zd%d&Zd'S)*Menuz^Menu containing sub menus under menu.Entries Contains both Menu and MenuEntry items. cCsd|_d|_g|_d|_d|_d|_d|_d|_d|_d|_ g|_ d|_ d|_ g|_ g|_d|_g|_g|_d|_g|_g|_dS)NrFT)r DirectoryEntriesDocFilenameDepthParentNotInXmlShowVisibleAppDirs DefaultLayoutr Directories DirectoryDirsLayout MenuEntriesMovesOnlyUnallocatedRulesSubmenusselfrrr__init__Is* z Menu.__init__cC|jSNrr;rrr__str__ez Menu.__str__cCs|jD]}|j|q|jD]}|j|q|jD]}|j|q|jdur-|j|_|jdur6|j|_|jr=|j|_|jrD|j|_|jD]}|j|qG|j D]}|j |qS|j D]}| |q_|Sr?) r1appendr4r3r r8r5r2r9r7r: addSubmenu)r<otherdir directoryrulemovesubmenurrr__add__hs*         z Menu.__add__cCst||Sr?)rstrcollgetNamer<rErrr__cmp__sz Menu.__cmp__cCs t|Sz&Key function for locale-aware sorting.)rrMr;rrr_keys z Menu._keycC,z|}Wn tyYnw||kSr?rQAttributeErrorrNrrr__lt__    z Menu.__lt__cCs2z|jt|kWSty|jt|kYSwr?)rr NameErrorstrrNrrr__eq__s  z Menu.__eq__Fccs.|jD]}|r |Vq|jdur|VqdS)z4Interator for a list of Entries visible to the user.TN)r)r/)r< show_hiddenentryrrr getEntriess  zMenu.getEntriescCsB|jD] }|j|kr|Sq|r|jD] }|||qdSdS)z4Searches for a MenuEntry with a given DesktopFileID.N)r6 DesktopFileIDr: getMenuEntry)r< desktopfileiddeep menuentryrJrrrr^s   zMenu.getMenuEntrycCsP|dd}|jD]}|j|dkr%t|dkr!||dS|Sq dS)z&Searches for a Menu with a given path./rN)splitr:rlengetMenu)r<patharrayrJrrrrfs   z Menu.getMenucCsv|}g} |r||jn|||jdkr|j}nnq|d}|s-|d|D] }tj ||}q/|S)z/Returns this menu's path in the menu structure.rcrr') rCrrMr,r-reversepoposrgjoin)r<orgtoplevelparentnamesrgrrrrgetPaths"   z Menu.getPathcCs(z|jjWSty|jYSw)z"Returns the menu's localised name.)r(rrMrTrr;rrrrMs   z Menu.getNamecC$z|jjWStyYdSw)z Returns the menu's generic name.r')r(rgetGenericNamerTr;rrrrs  zMenu.getGenericNamecCrr)z Returns the menu's comment text.r')r(r getCommentrTr;rrrrurtzMenu.getCommentcCrr)z0Returns the menu's icon, filename or simple namer')r(rgetIconrTr;rrrrvrtz Menu.getIconcCs2g|_d|_|jD]}|q t}t}|jjD]}|ddkr*||dq|ddkr7||dq|jjD]}|ddkrat|}t |jdkrZt |jdtrZd|_ |j |q<|ddkrw| |d}|rv|j |q<|ddkr||d}|r|jjr||q<|j |q<|ddkr|dd ks|dd kr|j|jD] }|j|vr|j |qq<|dd ks|dd kr|j|jD]}|j|vr|jjr||q|j |qq<|jD]}d |_ |jd7_t |tr>|jd urt|_ |jd8_qt |jtr=|jjr,t|_ |jd8_q|jjr=t|_ |jd8_qt |tr|jrUt|_ |jd8_q|jrft|_ |jd8_q|jr}|js}t |_ |jd8_qt!j"j#r|j$gkrt!j"j#|j$vst!j"j#|j%vrt&|_ |jd8_qt |tr|jd8_qt |jdkrt |jdtrd|jd_ t |jdkrt |jdtrd|jd_ |jddD]*}t |tr|jj's|jdkrt(|_ |jd8_|j)d ur|j*|qdS) Nrr+rcMenuname SeparatorFMergefilesallmenusT)+r)r0r:sortsetr5orderaddrxrerr/rCr^rfinline merge_inliner6r]rr&r DELETEDr( MenuEntryr getNoDisplay NO_DISPLAY getHiddenHIDDEN getTryExec findTryExecNO_EXECxdgConfig windowmanager getOnlyShowIn getNotShowIn NOT_SHOW_IN show_emptyEMPTYr.remove)r<rJ _submenus_entriesr separatorrar[rrrr~s                                "  z Menu.sortcCsD|jD] }||kr||7}dSq|j|||_|jd|_dS)Nrc)r:rCr-r,)r<newmenurJrrrrDTs  zMenu.addSubmenucCst|jdkr9|jjr9|jd}|jjd|dd|jjd|dd|jjd|dd|j |dSt|j|jj ksH|jj dkrl|jj r^t |||}|j ||jD]}|j |qadS|j |dS) z}Appends a submenu's entries to this menu See the section of the spec about the "inline" attribute rcrrT)r GenericNameCommentN) rer)r5 inline_aliasrrrMrsrurC inline_limit inline_headerHeader)r<rJraheaderr[rrrr_s   zMenu.merge_inlineNF)FF)__name__ __module__ __qualname____doc__r=rArKrOrQrUrYr\r^rfrqrMrsrurvr~rDrrrrrr&Ds,"   d r&c@s"eZdZdZdddZddZdS) MovezA move operationr'cCs||_||_dSr?)OldNew)r<oldnewrrrr=u z Move.__init__cCst|j|jSr?)cmprrNrrrrOysz Move.__cmp__Nr'r')rrrrr=rOrrrrrss  rc@s8eZdZdZ  d ddZeddZejd dZd S) r5zMenu Layout classFrTcCs:||_||_||_||_||_g|_ddgddgg|_dS)Nrzr}r{)rrrrr_order_default_order)r<rrrrrrrrr=s zLayout.__init__cCs|jr|jS|jSr?)rrr;rrrrz Layout.ordercC ||_dSr?)r)r<rrrrrs N)FFrTF)rrrrr=propertyrsetterrrrrr5}s  r5c@s<eZdZdZd\ZZeddZddZddZ d d Z d S) RulezInclude / Exclude Rules Classrrcc Cs`tjtjt|tgtjtjdtddtdgdddd}t|t ||}|S) Nrar r]valueattrr leftops comparatorsrcr)bodylineno col_offset) r ExpressionCompareStrEq Attributerrfix_missing_locationsr)clstypefilenametreerHrrr fromFilenames  zRule.fromFilenamecCs ||_||_t|jdd|_dS)Nzeval)Type expressioncompilecode)r<rrrrrr=sz Rule.__init__cCs t|jSr?)rdumprr;rrrrAs z Rule.__str__cCsV|D]&}|dkr|jdus|jdurqt|jr(|jtjur%d|_d|_qd|_q|S)NTF)MatchedInclude Allocatedrrrr TYPE_INCLUDEAdd)r< menuentriesrunrarrrapplys   z Rule.applyN) rrrrr TYPE_EXCLUDE classmethodrr=rArrrrrrs  rc@seZdZdZdZdZdZdddZdd Zd d Z d d Z dddZ ddZ ddZ ddZddZddZddZddZdS) rz(Wrapper for 'Menu Style' Desktop EntriesUserSystemBothr'cCsVttj|||_||||d|_d|_g|_d|_d|_ d|_ |j |_ dSNTF) rrkrgrl setAttributesr/OriginalParentsrrr getCategories Categoriesr<rrFprefixrrrr=szMenuEntry.__init__cCs|jjr |jdSdS)z&Save any changes to the desktop entry.N)rtaintedwriter;rrrsaveszMenuEntry.savecCs|jj|jdS)z7Return the directory containing the desktop entry file.r')rrreplacer+r;rrrgetDirrzMenuEntry.getDircCs6tjjs|jr |jStd|jjvr|jS|j S|jS)z.Return the type of MenuEntry, System/User/Bothr) rr root_moder TYPE_BOTHrrr TYPE_USER TYPE_SYSTEMr;rrrgetTypeszMenuEntry.getTypecCsD||_||_tj||dd|_tj|jj s | dSdS)Nrb-) r+Prefixrkrgrlrr]isabsrr_MenuEntry__setFilenamerrrrrs  zMenuEntry.setAttributescCs4||jkrt|j||j|_|dSdSr?)rrrr+rrrrr;rrrupdateAttributess zMenuEntry.updateAttributescCs^tjjs td}ntd}|jdkrtj|d}ntj|d}tj||j |j_ dS)Nrrc Application applicationsdesktop-directories) rrrrrrrkrgrlr+r)r<rgdir_rrr __setFilenames zMenuEntry.__setFilenamecCst|j|jSr?)rrLrrMrNrrrrOszMenuEntry.__cmp__cCst|jSrP)rrrMr;rrrrQszMenuEntry._keycCrRr?rSrNrrrrUrVzMenuEntry.__lt__cCs|jt|kr dSdSr)r]rXrNrrrrYszMenuEntry.__eq__cCr>r?)r]r;rrr__repr__"rBzMenuEntry.__repr__Nr)rrrrrrrr=rrrrrrrOrQrUrYrrrrrrs"    rc@seZdZdZddZdS)rxz!Just a dummy class for SeparatorscCs||_d|_dS)NT)r-r/)r<rorrrr=(rzSeparator.__init__N)rrrrr=rrrrrx&s rxc@s eZdZdZddZddZdS)rzClass for Inline HeaderscCs||_||_||_dSr?)rrr)r<r generic_namecommentrrrr=/s zHeader.__init__cCr>r?r@r;rrrrA4rBzHeader.__str__N)rrrrr=rArrrrr-s rrcCsvtj|}tj|stj||}tj|}tj|s!dS|tkr-tj|r-|S|t kr9tj |r9|SdSNF) rkrgdirnamerrlabspathexistsTYPE_DIRisdir TYPE_FILEisfile)rrrrgrrr_check_file_path;s    rcCsPtt}tjjdur|d|D]}tj|d|}tj |r%|SqdS)NTrr}) listrrrrrjrkrgrlr)rdirsdmenunamerrr_get_menu_file_pathIs   r cCst|tr|S|dkS)Ntrue)rboollower)rrrr_to_boolSs  rcs*i|fdd|D}||S)Ncs g|] }|vr||qSr) setdefault).0e_setrr ]s z_dedupe..)ri)_listrrr_dedupeZs rc@seZdZd3ddZd4ddZddZd4d d Zd d Zd dZddZ ddZ ddZ ddZ ddZ ddZddZddZdd Zd!d"Zd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2ZdS)5XMLMenuBuilderFcCrr?)debug)r<rrrrr=ds zXMLMenuBuilder.__init__NcCs|r tj|s t|}|s*tjddd}t|}|s*tjddd}t|}|s3tdd||ds=td|zt |}Wntd |t |_ t |_ t |_|||}||_||_||||||||||S) zLoad an applications.menu file. filename : str, optional The default is ``$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu``. XDG_MENU_PREFIXr'applications.menuzdebian-menu.menuFile not foundz/etc/xdg/menus/%s.menuzNot a .menu fileNot a valid .menu file)rkrgrr environgetrendswithetreeparser _merged_files_directory_dirsMenuEntryCachecache parse_menugetrootrr handle_moves post_parsegenerate_not_only_allocatedgenerate_only_allocatedr~)r<r candidatermenurrrr"gs<        zXMLMenuBuilder.parsecCst}|||||Sr?)r& parse_node)r<noderr.rrrr'szXMLMenuBuilder.parse_menuc Csbt|}|D]'}|j|j}}|r|nd}|dkr(|||}||q|dkr6|r6||||q|dkrA|||q|dkrO|rO||||q|dkrZ| ||q|dkrd|rd||_ q|dkrq|rq|j |q|dkryd |_ q|d krd |_ q|d krd |_q|d krd |_q|dks|dkr|j ||q|dkr|jdddkr|d|||q|r|||||q|dkr|r|||||q|dkr||||q|dkr|j ||q|dkr|dkr|||_q|dkr |dkr |||_q|dkr#|r#|||jdd||q|dkr.|||qdS)Nr&AppDirDefaultAppDirs DirectoryDirDefaultDirectoryDirsrr(r8TNotOnlyUnallocatedFr NotDeletedIncludeExclude MergeFilerrorMergeDirDefaultMergeDirsrr5rcr2 LegacyDirrr' KDELegacyDirs)retagtextstripr'rD parse_app_dirparse_default_app_dirparse_directory_dirparse_default_directory_dirrr3rCr8r r9 parse_ruleattribrparse_merge_fileparse_merge_dirparse_default_merge_dirsr7 parse_move parse_layoutr5r2parse_legacy_dirparse_kde_legacy_dirs) r<r0rro num_childrenchildr>r?r.rrrr/sn              zXMLMenuBuilder.parse_nodecCsLtt|jddt|jddt|jddt|jddt|jddd }g}|D]p}|j|j}}|r?|nd}|d krw|rw|d |t|jddt|jddt|jddt|jddt|jddgq0|d kr|d gq0|d kr|r|d |gq0|d kr|d |jddgq0||_ |S)NrFrrrrTr)rrrrrrwrxr+rzrr|) r5rrFrintr>r?r@rCr)r<r0layoutrrOr>r?rrrrKsB    zXMLMenuBuilder.parse_layoutcCs\d\}}|D]"}|j|j}}|r|nd}|dkr |r |}q|dkr(|r(|}qt||S)Nrrr)r>r?r@r)r<r0rrrOr>r?rrrrJs   zXMLMenuBuilder.parse_movecCs^|jdkrtjntj}tjddd}||t}|r ||_nt d|_t |t||S)Nr7rcr)rrFalse) r>rrrrr parse_bool_opOrrrr)r<r0rrexprrrrrEs   zXMLMenuBuilder.parse_rulecCsVg}|D]}||}|r||qt|}|dkr!t||S|dkr)|dSdS)Nrcr)parse_rule_noderCrerBoolOp)r<r0operatorvaluesrOrH num_valuesrrrrSs   zXMLMenuBuilder.parse_bool_opcCs|j}|dkr||tS|dkr||tS|dkr3||t}|r1tt|SdS|dkr;tdS|dkr`|j}tj t |t gtj tj dtdd td gd S|d kr|j}tj t |tgtj tj dtdd td gd SdS)NrTAndNotAllTrueCategoryrar rrrr+r])r>rSrrTr[UnaryOpr\rr?rrInrrrr)r<r0r>rUcategoryrrrrrVsB zXMLMenuBuilder.parse_rule_nodecC$t||t}|r|j|dSdSr?)rrr1rCr<rrrorrrrAA zXMLMenuBuilder.parse_app_dircC*ttD]}|tj|d||qdS)Nr)reversedrrArkrgrlr<rrorrrrrBF z$XMLMenuBuilder.parse_default_app_dircCrcr?)rrr4rCrdrrrrCJrez"XMLMenuBuilder.parse_directory_dircCrf)Nr)rgrrCrkrgrlrhrrrrDOriz*XMLMenuBuilder.parse_default_directory_dircCs|jdddkr@tD]2}||dd}||kr=tD] }||kr#qtjtj||r<| tj||||nqq dSt ||t }|rQ| |||dSdS)Nrror'rb) rFrrrr@rkrgrrl merge_filerr)r<rrOrrorrel_fileprrrrGUs" zXMLMenuBuilder.parse_merge_filec Cs`t||t}|r,t|D] }z|dr!|tj||||Wq ty+Yq wdSdS)Nr) rrrklistdirr rjrgrlUnicodeDecodeError)r<rrOrroitemrrrrHes   zXMLMenuBuilder.parse_merge_dirc CsJtjtj|d}ttD]}|tj|d|d|||qdS)Nrr}z-merged)rkrgsplitextbasenamergrrHrl)r<rOrrorqrrrrrIos "z'XMLMenuBuilder.parse_default_merge_dirscCs||jvr|jr td|dS|j|zt|}Wnty.|jr+td|YdS|jr9td|YdS|}||||dS)Nz Infinite MergeFile loop detectedrr) r#rrrr!r"IOErrorr(r/)r<rrOrorrootrrrrjts"      zXMLMenuBuilder.merge_filecCs$|||||}|r||7}dSdSr?)merge_legacy_dir)r<rrrromrrrrLs zXMLMenuBuilder.parse_legacy_dirc CsJt||t}|r||jvr|j|t}|j||j|tj ||_ d|_ t |D]3}z(|dkr?|j|ntj tj ||rZ||tj |||||Wq1tydYq1w|j|g|d|j|gd}|D]&}|j} t| dkrttj|j} |j| ||jvr| d| |_qx|SdSdS)NTz .directoryFrLegacy)rrr$rr&r1rCr4rkrgrqrr.rmr3rrlrDrtrnr&add_menu_entriesget_menu_entriesrrerrrr]r9) r<rrrrorurorra categoriesrrrrrtsH          zXMLMenuBuilder.merge_legacy_dircCsztjgdtjdd}|d}Wn tyYdSwz|ddD] }||d||q(WdSty?YdSw)N)z kde-configz--pathappsT)stdoutuniversal_newlinesr:kde) subprocessPopenPIPE communicate splitlinesOSErrorrdrL IndexError)r<rroprocoutputrrrrrMs"  z$XMLMenuBuilder.parse_kde_legacy_dirscCs|jdurd|_|jdurd|_|jr|jsI|jr|j|_n+|jr1|jdkr,|jj|_nt|_n|jdkrA|jj|_|jj|_nt|_t|_|jdkr^|jj|j|_|jj|j|_t|j |_ t|j|_t|j|_|j D]}| |qs|j |j |j |j D]8}|jD],}t jt j||rt||}|js||_q|tjkr|jtjkr||j_q|jrdSqdS)NFr)r r8r5r2r,r-r1r4rr3r:r*rirkrgrrlrr(rrrr)r<r.rJrGrFrarrrr*sR                      zXMLMenuBuilder.post_parsecCs|jD]}||q|jdurF|j|jg}|jD] }||j|jd}q|D]}|j durE|j |d|_ d|_ |j |q,dSdS)NFrcT)r:r+r8r&rwr1r9rrxrrrCrr6r<r.rJrrHrarrrr+s       z*XMLMenuBuilder.generate_not_only_allocatedcCs|jD]}||q|jdur@|j|jg}|jD] }||j|jd}q|D]}|j dur?|j ||j |q,dSdS)NTr) r:r,r8r&rwr1r9rrxrrrCr6rrrrr,s       z&XMLMenuBuilder.generate_only_allocatedc Cs|jD]}||q|jD]Y}||j}|rg||j}|jd}d}t|dkr\|s0|}||d}|sOt}|d|_ t|dkrJd|_ | ||}| dt|dks,||7}|j j|qdS)NrbrrcT)r:r)r7rfrrrdrer&rr.rDrjr-r) r<r.rJrImove_from_menu move_to_menur} oldparentrrrrr)$s2             zXMLMenuBuilder.handle_movesrr?)rrrr=r"r'r/rKrJrErSrVrArBrCrDrGrHrIrjrLrtrMr*r+r,r)rrrrrbs2  6 4"   $ &9 rc@s4eZdZdZddZdddZdd Zdd d Zd S)r%zClass to cache Desktop EntriescCsi|_g|jd<i|_dS)Nlegacy) cacheEntriesr&r;rrrr=Cs  zMenuEntryCache.__init__r'FcCs2|D]}||jvrg|j|<||d||qdS)Nr')r_MenuEntryCache__addFiles)r<rrrrrrrrwHs   zMenuEntryCache.add_menu_entriesc Csttj||D]L}|dr;z ttj||||}Wn ty'Yq w|j|||r:|jd|q tj tj|||rV|sV| |tj||||q dS)Nz.desktopr) rkrmrgrlr rrrrCrr)r<rsubdirrrrorarrr __addFilesNs  zMenuEntryCache.__addFilesTc Csg}t}|dd}|r|dd|}z|j|WSty&Ynw|D]@}|j|D]8}z-|j|vrD||j||n|t j kr^| |} || } | t j kr^|| _ Wq0tyhYq0wq)||j|<|S)Nrr')rrCrlr&KeyErrorrr]rrrrindexrrrn) r<rrentriesidsappdirskeyrraidxr[rrrrx\s:           zMenuEntryCache.get_menu_entriesN)r'F)T)rrrrr=rwrrxrrrrr%As  r%FcCst||S)zYHelper function. Equivalent to calling xdg.Menu.XMLMenuBuilder().parse(filename) )rr")rrrrrr"{sr"r)1rrkrrrr xml.etree.cElementTreer! cElementTree ImportErrorxml.etree.ElementTree ElementTreexdg.BaseDirectoryrrxdg.DesktopEntryrxdg.Exceptionsrxdg.utilr xdg.Localer xdg.Configrrrrrrrrr&rr5rrrxrrrrr rrobjectrr%r"rrrrsZ      1 /b  b: