ukui-menu/0000755000175000017500000000000013267550265011430 5ustar fengfengukui-menu/COPYING0000644000175000017500000004325413110762006012454 0ustar fengfeng GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ukui-menu/ukui-menu-editor0000775000175000017500000000315413237775114014565 0ustar fengfeng#!/usr/bin/python3 # ## Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the ## Free Software Foundation, Inc., ## 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # import gi import sys import ukui_menu.menueditor as menueditor from gi.repository import Gio gi.require_version("Gtk", "3.0") from gi.repository import Gtk def on_delete(widget, event): Gtk.main_quit() def on_close_button_clicked(button): Gtk.main_quit() settings = Gio.Settings.new("org.ukui.ukui-menu") showCategoryMenu = settings.get_boolean("show-category-menu") if showCategoryMenu == False: msg = _("\nCurrent menu mode is not category mode, please switch and try again!") md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() exit() app = menueditor.MenuEditMainWindow() app.tree.get_object('mainwindow').connect("delete-event", on_delete) app.tree.get_object('close_button').connect("clicked", on_close_button_clicked) app.run() Gtk.main() ukui-menu/README.md0000664000175000017500000000077613237775114012722 0ustar fengfengThis is UKUI Menu, a fork of Mate Menu. * UKUI Menu is a new style startup menu. * An advanced menu for UKUI. Supports filtering, favorites, easy-uninstallation, autosession, and many other features. . * This menu originated in the Linux Mint distribution and has been ported to other distros that ship the UKUI Desktop Environment. ukui-menu/ukui_menu/0000755000175000017500000000000013265322640013421 5ustar fengfengukui-menu/ukui_menu/pointerMonitor.py0000664000175000017500000000737413237775114017047 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import gi import threading gi.require_version("Gtk", "3.0") from Xlib.display import Display from Xlib import X, error from gi.repository import Gtk, Gdk, GObject, GLib class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): try: GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] except Exception as cause: print (("init pointerMonitor error :\n", str(cause))) self.display = None return None # Receives GDK windows def addWindowToMonitor(self, window): if self.display == None: return self.windows.append(window) def grabPointer(self): if self.display == None: return self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): if self.display == None: return self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): if self.display == None: return self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: if Gtk.check_version (3, 20, 0) is None: pdevice = Gdk.Display.get_default().get_default_seat().get_pointer() else: pdevice = Gdk.Display.get_default().get_device_manager().get_client_pointer() p = self.get_window().get_device_position(pdevice) g = self.get_size() if p.x >= 0 and p.y >= 0 and p.x <= g.width and p.y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print (("Unexpected error: " + str(e))) def stop(self): if self.display == None: return self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close() ukui-menu/ukui_menu/easybuttons.py0000664000175000017500000005414413237775114016374 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os import re import shutil import xdg.DesktopEntry import xdg.Menu from .execute import * from .filemonitor import monitor as filemonitor from gi.repository import Gtk, Gdk, GLib, GdkPixbuf from gi.repository.GdkPixbuf import Pixbuf from gi.repository import Pango from gi.repository import GObject ICON_PATH = "/usr/share/ukui-menu/icons/" class IconManager(GObject.GObject): __gsignals__ = { "changed" : (GObject.SignalFlags.RUN_LAST, None, () ) } def __init__( self ): GObject.GObject.__init__( self ) self.icons = { } self.count = 0 # Some apps don't put a default icon in the default theme folder, so we will search all themes def createTheme( d ): theme = Gtk.IconTheme() theme.set_custom_theme( d ) return theme # This takes to much time and there are only a very few applications that use icons from different themes #self.themes = map( createTheme, [ d for d in os.listdir( "/usr/share/icons" ) if os.path.isdir( os.path.join( "/usr/share/icons", d ) ) ] ) self.defaultTheme = Gtk.IconTheme.get_default() # Setup and clean up the temp icon dir configDir = GLib.get_user_config_dir() self.iconDir = os.path.join(configDir, "ukui-menu") if not os.path.exists(self.iconDir): os.makedirs(self.iconDir) # Skip over files and dirs belonging to the applications plugin contents = frozenset(os.listdir(self.iconDir)) - frozenset(('applications', 'applications.list')) for fn in contents: if os.path.isfile(os.path.join(self.iconDir, fn)): print (("Removing file : " + os.path.join(self.iconDir, fn))) os.remove(os.path.join(self.iconDir, fn)) else: print ((os.path.join(self.iconDir, fn) + " is not a file, skipping delete.")) self.defaultTheme.append_search_path(self.iconDir) # Themes with the same content as the default them aren't needed #self.themes = [ theme for theme in self.themes if theme.list_icons() != defaultTheme.list_icons() ] self.themes = [ self.defaultTheme ] # Listen for changes in the themes for theme in self.themes: theme.connect("changed", self.themeChanged ) def getIcon( self, iconName, iconSize ): if not iconName: return None try: iconFileName = "" realIconName = "" needTempFile = False #[ iconWidth, iconHeight ] = self.getIconSize( iconSize ) if iconSize <= 0: return None elif os.path.isabs( iconName ): iconFileName = iconName needTempFile = True else: if iconName[-4:] in [".png", ".xpm", ".svg", ".gif"]: realIconName = iconName[:-4] else: realIconName = iconName if iconFileName and needTempFile and os.path.exists( iconFileName ): tmpIconName = iconFileName.replace("/", "-") realIconName = tmpIconName[:-4] if not os.path.exists(os.path.join(self.iconDir, tmpIconName)): shutil.copyfile(iconFileName, os.path.join(self.iconDir, tmpIconName)) self.defaultTheme.append_search_path(self.iconDir) image = Gtk.Image() icon_found = False for theme in self.themes: if theme.lookup_icon( realIconName, 0, Gtk.IconLookupFlags.FORCE_REGULAR ): icon_found = True break if icon_found: image.set_from_icon_name(realIconName, Gtk.IconSize.DND) image.set_pixel_size(iconSize) else: #image = None image.set_from_icon_name("access", Gtk.IconSize.DND) image.set_pixel_size(iconSize) return image except Exception as e: print (("Exception " + e.__class__.__name__ + ": " + e.message)) return None def themeChanged( self, theme ): self.emit( "changed" ) GObject.type_register(IconManager) class easyButton( Gtk.Button ): def __init__( self, iconName, iconSize, labels = None, buttonWidth = 200, buttonHeight = 38 ): GObject.GObject.__init__( self ) self.connections = [ ] self.iconName = iconName self.iconSize = iconSize self.showIcon = True self.set_relief( Gtk.ReliefStyle.NONE ) self.set_size_request( buttonWidth, buttonHeight ) self.viewport = Gtk.Viewport() self.Align1 = Gtk.Alignment.new( 0, 0.5, 1.0, 0 ) self.HBox1 = Gtk.HBox() self.labelBox = Gtk.VBox( False, 2 ) self.buttonImage = Gtk.Image() icon = self.getIcon( self.iconSize ) if icon: self.buttonImage = icon else: #[ iW, iH ] = iconManager.getIconSize( self.iconSize ) self.buttonImage.set_size_request( self.iconSize, self.iconSize ) self.image_box = Gtk.HBox() self.image_box.pack_start(self.buttonImage, False, False, 5) self.image_box.show_all() self.HBox1.pack_start( self.image_box, False, False, 0 ) if labels: for label in labels: if isinstance( label, str ): self.addLabel( label ) elif isinstance( label, list ): self.addLabel( label[0], label[1] ) self.labelBox.show() self.HBox1.pack_start( self.labelBox , True, True, 0) #add right icon for category self.buttonIconRight = Gtk.Image() iconfile = ICON_PATH + "arrow-right-line.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(6, 9, 2) #2 := BILINEAR self.buttonIconRight.set_from_pixbuf(pixbuf) #self.buttonIconRight.set_from_icon_name(ICON_PATH + "arrow-right-line.png", self.iconSize) self.rightbox = Gtk.HBox() self.rightbox.pack_start(self.buttonIconRight, False, False, 5) self.HBox1.pack_start( self.rightbox, False, False, 0 ) self.HBox1.show() self.Align1.add( self.HBox1 ) self.Align1.show() self.add( self.Align1 ) self.connectSelf( "destroy", self.onDestroy ) self.connect( "released", self.onRelease ) # Reload icons when the theme changed self.themeChangedHandlerId = iconManager.connect("changed", self.themeChanged ) def connectSelf( self, event, callback ): self.connections.append( self.connect( event, callback ) ) def onRelease( self, widget ): widget.set_state(Gtk.StateType.NORMAL) def onDestroy( self, widget ): self.buttonImage.clear() iconManager.disconnect( self.themeChangedHandlerId ) for connection in self.connections: self.disconnect( connection ) del self.connections def addLabel( self, text, styles = None ): label = Gtk.Label() label.set_name("startup-label") if "" in text or " 0 and color[0] == "#": #appName = "%s" % (color, appName); #appComment = "%s" % (color, appComment); #appName = "%s" % (appName); #appComment = "%s" % (appComment); #else: #appName = "%s" % (appName); #appComment = "%s" % (appComment); appName = "%s" % (appName); appComment = "%s" % (appComment); except Exception as detail: print (detail) pass if self.showComment and self.appComment != "": if self.iconSize <= 2: # self.addLabel( '%s' % appName) self.addLabel( '%s' % appComment) else: self.addLabel( appName ) # self.addLabel( '%s' % appComment) else: self.addLabel( appName ) def execute( self, *args ): self.highlight = False for child in self.labelBox: child.destroy() self.setupLabels() return super(MenuApplicationLauncher, self).execute(*args) def setShowComment( self, showComment ): self.showComment = showComment for child in self.labelBox: child.destroy() self.setupLabels() class FavApplicationLauncher( ApplicationLauncher ): def __init__( self, desktopFile, iconSize, swapGeneric = False ): self.swapGeneric = swapGeneric ApplicationLauncher.__init__( self, desktopFile, iconSize ) def setupLabels( self ): if self.appGenericName: if self.swapGeneric: self.addLabel( self.appName ) # self.addLabel( self.appGenericName ) else: # self.addLabel( '%s' % self.appGenericName ) self.addLabel( self.appName ) else: self.addLabel( self.appName ) # if self.appComment != "": # self.addLabel( self.appComment ) # else: # self.addLabel ( "" ) def setSwapGeneric( self, swapGeneric ): self.swapGeneric = swapGeneric for child in self.labelBox: child.destroy() self.setupLabels() class CategoryButton( easyButton ): def __init__( self, iconName, iconSize, labels , f ): easyButton.__init__( self, iconName, iconSize, labels, 200, 28 ) self.filter = f iconManager = IconManager() ukui-menu/ukui_menu/plugins/0000755000175000017500000000000013260366513015104 5ustar fengfengukui-menu/ukui_menu/plugins/__init__.py0000644000175000017500000000000013110762006017172 0ustar fengfengukui-menu/ukui_menu/plugins/menu.py0000664000175000017500000033766013260366513016443 0ustar fengfeng#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import gi gi.require_version("Gtk", "3.0") gi.require_version("Bamf", "3") from gi.repository import Gtk, Pango, Gdk, Gio, GLib, GObject, GdkPixbuf#, Wnck from gi.repository.GdkPixbuf import Pixbuf from gi.repository import Bamf import os import sys import time import shutil import string import gettext import threading import subprocess import filecmp import ctypes from ctypes import * from ukui_menu.easybuttons import * from ukui_menu.execute import Execute from ukui_menu.easygsettings import EasyGSettings from ukui_menu.easyfiles import * from ukui_menu.filemonitor import monitor as filemonitor from xml.etree import ElementTree as ET import copy from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE_SELF, IN_CLOSE_WRITE, IN_MODIFY import dbus from configobj import ConfigObj import _thread import ukuimenu import operator import platform import configparser class UkuiConfigParser(configparser.ConfigParser): def optionxform(self, optionstr): return optionstr # i18n gettext.install("ukui-menu", "/usr/share/locale") ICON_PATH = "/usr/share/ukui-menu/icons/" ICON_SIZE = 16 ukuimenu_settings = Gio.Settings.new("org.ukui.ukui-menu") FAVNUM = 5 #常用软件显示个数 server = False try: (status, output) = subprocess.getstatusoutput("uname -a | grep server") if status == 0: server = True else: server = False except Exception as e: print (e) serverx86 = False try: (statusx86, outputx86) = subprocess.getstatusoutput("dpkg -l | grep mate-server-guide") if statusx86 == 0: serverx86 = True else: serverx86 = False except Exception as e: print (e) def get_user_icon(): current_user = GLib.get_user_name() try: bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) result = bus.call_sync('org.freedesktop.Accounts', '/org/freedesktop/Accounts', 'org.freedesktop.Accounts', 'FindUserByName', GLib.Variant ('(s)', (current_user,)), GLib.VariantType.new ('(o)'), Gio.DBusCallFlags.NONE, -1, None) (path,) = result.unpack() result = bus.call_sync('org.freedesktop.Accounts', path, 'org.freedesktop.DBus.Properties', 'GetAll', GLib.Variant ('(s)', ('org.freedesktop.Accounts.User',)), GLib.VariantType.new ('(a{sv})'), Gio.DBusCallFlags.NONE, -1, None) (props,) = result.unpack() usericon = props['IconFile'] return usericon except Exception as e: print (e) return GLib.get_home_dir() + "/.face" usericonPath = get_user_icon() RecManagerInstance = Gtk.RecentManager.get_default() APPLIST = [_("WPS Writer"), _("WPS Presentation"), _("WPS Spreadsheets"), _("Qt Creator"), _("SMPlayer"), _("Kylin Video"), _("Eye of MATE Image Viewer"), _("Atril Document Viewer"), _("Pluma")] WPS_W = ["application/wps-office.doc", "application/wps-office.docx", "application/wps-office.wps", "application/wps-office.wpt", "application/wps-office.dot"] WPS_P = ["application/wps-office.pptx", "application/wps-office.ppt", "application/wps-office.pot", "application/wps-office.pps", "application/wps-office.dps", "application/wps-office.dpt"] WPS_S = ["application/wps-office.xlsx", "application/wps-office.xls", "application/wps-office.et", "application/wps-office.ett", "application/wps-office.xlt"] class EventHandle(ProcessEvent): def process_IN_DELETE_SELF(self, event): time.sleep(0.2) ifchangegsettings = ukuimenu_settings.get_boolean("recent-file-changed") ukuimenu_settings.set_boolean("recent-file-changed", ~ifchangegsettings) def process_IN_CLOSE_WRITE(self, event): time.sleep(0.2) ifchangegsettings = ukuimenu_settings.get_boolean("recent-file-changed") ukuimenu_settings.set_boolean("recent-file-changed", ~ifchangegsettings) def process_IN_MODIFY(self, event): time.sleep(0.2) ifchangegsettings = ukuimenu_settings.get_boolean("recent-file-changed") ukuimenu_settings.set_boolean("recent-file-changed", ~ifchangegsettings) def FileMonitor(self): wm = WatchManager() mask = IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MODIFY notifier = Notifier(wm, EventHandle()) while True: try: notifier.process_events() if os.path.exists(os.path.join(GLib.get_user_data_dir(), 'recently-used.xbel')): ret = wm.add_watch(os.path.join(GLib.get_user_data_dir(), 'recently-used.xbel'), mask, rec = True) if ret[os.path.join(GLib.get_user_data_dir(), 'recently-used.xbel')] == -1: os.system("/usr/bin/python3 /usr/lib/ukui-menu/ukui-menu.py") ifchangegsettings = ukuimenu_settings.get_boolean("permission-changed") ukuimenu_settings.set_boolean("permission-changed", ~ifchangegsettings) if os.path.exists(os.path.join(GLib.get_user_config_dir(), 'kylin-video/kylin-video.ini')): wm.add_watch(os.path.join(GLib.get_user_config_dir(), 'kylin-video/kylin-video.ini'), mask, rec = True) if os.path.exists(os.path.join(GLib.get_user_config_dir(), 'QtProject/QtCreator.ini')): wm.add_watch(os.path.join(GLib.get_user_config_dir(), 'QtProject/QtCreator.ini'), mask, rec = True) if notifier.check_events(): notifier.read_events() except KeyboardInterrupt: notifier.stop() break def get_user_item_path(): item_dir = None if 'XDG_DATA_HOME' in os.environ: item_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'applications') else: item_dir = os.path.join(os.environ['HOME'], '.local', 'share', 'applications') if not os.path.isdir(item_dir): os.makedirs(item_dir) return item_dir def get_system_item_paths(): item_dirs = None if 'XDG_DATA_DIRS' in os.environ: item_dirs = os.environ['XDG_DATA_DIRS'].split(":") else: item_dirs = [os.path.join('usr', 'share')] return item_dirs def get_desktop_file_path(desktop_filename): if not desktop_filename: return '' #path = os.path.join(get_user_item_path(), desktop_filename) #if os.path.exists(path): # return path for p in get_system_item_paths(): path = os.path.join(p, 'applications', desktop_filename) if os.path.exists(path): return path return '' def get_category_file_path(): dir_path = os.path.dirname(os.path.abspath(sys.argv[0])) system_category_file_path = os.path.join(dir_path, 'category.xml') os.system("mkdir -p %s/.config/ukui-menu/category/" % GLib.get_home_dir()) user_category_file_path = '%s/.config/ukui-menu/category/category.xml' % GLib.get_home_dir() if not os.path.exists(user_category_file_path): os.system("cp %s %s" % (system_category_file_path, user_category_file_path)) return user_category_file_path class Category(GObject.GObject): def __init__(self, all_application_list): GObject.GObject.__init__ (self) self.hasOffice = False self.hasGame = False self.hasAndroid = False self.category_list_m = [] self.application_list_m = [] self.current_category = None self.all_application_list = all_application_list for app in self.all_application_list: if app['category'] == 'Games' or app['category'] == _("Games") or app['category'] == '游戏': self.hasGame = True self.application_list_m.append(app) for app in self.all_application_list: if app['category'] == 'Android' or app['category'] == _("Android") or app['category'] == '安卓兼容': self.hasAndroid = True self.application_list_m.append(app) for app in self.all_application_list: if app['category'] == 'Office' or app['category'] == _('Office') or app['category'] == '办公': self.hasOffice = True self.application_list_m.append(app) if not server or not serverx86: self.application_list_m += self.autostart_list() self.category_file = open(get_category_file_path(), 'r+b') self.tree = ET.ElementTree(file = self.category_file.name) self.load_tree_data() def load_tree_data(self): for directory in self.tree.getroot(): name = directory.attrib['name'] icon = directory.attrib['icon'] for entry in directory: desktop_file_path = get_desktop_file_path(entry.attrib['source']) if not desktop_file_path: continue found = False for app in self.all_application_list: if app['desktop_file_path'] == desktop_file_path: found = True break if not found: continue self.application_list_m.append( { 'desktop_file_path': desktop_file_path, 'category': name } ) if name == "办公": if not self.hasOffice: continue if name == "游戏": if not self.hasGame: continue if name == "安卓兼容": if not self.hasAndroid: continue self.category_list_m.append( { "name": name, "icon": icon, "tooltip": name, "filter": name } ) def category_list(self): return self.category_list_m def application_list(self): return self.application_list_m def autostart_list(self): autostart_dirs = [] try: autostart_dirs = [os.path.join(p, 'autostart') for p in os.environ['XDG_CONFIG_DIRS'].split(':')] # home's autostart has the highest priority autostart_dirs.insert(0, '%s/.config/autostart' % GLib.get_home_dir()) autostart_dirs = [ d for d in autostart_dirs if os.path.exists(d) ] except KeyError as e: print ('No environment variable called XDG_CONFIG_DIRS') autostart_apps = [] autostart_desktop_filenames = [] for di in autostart_dirs: for filename in os.listdir(di): file_path = os.path.join(di, filename) if filename in autostart_desktop_filenames: continue autostart_desktop_filenames.append(filename) try: app = Gio.DesktopAppInfo.new_from_filename(file_path) if app.get_show_in('MATE'): autostart_apps.append(app) except TypeError as e: print ("error") for app in autostart_apps: # we have to use get_string here since false as a boolean may indicate the # key does not exist in which condition the remove op will not be performed if app.get_string('X-MATE-Autostart-enabled') == 'false': autostart_apps.remove(app) if app.get_string('Name') == "MATE Settings Daemon": autostart_apps.remove(app) # TODO: use translation return [{'desktop_file_path': app.get_filename(), 'category': '启动'} for app in autostart_apps] class Menu: def __init__(self, MenuToLookup): self.tree = ukuimenu.lookup_tree(MenuToLookup) self.directory = self.tree.get_root_directory() def getMenus(self, parent = None): if parent == None: yield self.tree.root else: for menu in parent.get_contents(): if menu.get_type() == ukuimenu.TYPE_DIRECTORY and self.__isVisible(menu): yield menu def getItems(self, menu): for item in menu.get_contents(): if item.get_type() == ukuimenu.TYPE_ENTRY and item.get_desktop_file_id()[-19:] != '-usercustom.desktop' and self.__isVisible(item): yield item def __isVisible(self, item): if item.get_type() == ukuimenu.TYPE_ENTRY: return not (item.get_is_excluded() or item.get_is_nodisplay()) if item.get_type() == ukuimenu.TPYE_DIRECTORY and len( item.get_contents()): return True class pluginclass( object ): TARGET_TYPE_TEXT = 80 toButton = (Gtk.TargetEntry.new("text/uri-list", 0, TARGET_TYPE_TEXT), Gtk.TargetEntry.new("text/uri-list", 0, TARGET_TYPE_TEXT)) TARGET_TYPE_FAV = 81 toFav = (Gtk.TargetEntry.new("FAVORITES", Gtk.TargetFlags.SAME_APP, 81), Gtk.TargetEntry.new("text/plain", 0, 100 ), Gtk.TargetEntry.new("text/uri-list", 0, 101)) fromFav = (Gtk.TargetEntry.new("FAVORITES", Gtk.TargetFlags.SAME_APP, 81), Gtk.TargetEntry.new("FAVORITES", Gtk.TargetFlags.SAME_APP, 81)) def __init__(self, ukuiMenuWin, showCategoryMenu): self.ukuiMenuWin = ukuiMenuWin self.showCategoryMenu = showCategoryMenu self.de = "ukui" self.showRecentFile = ukuimenu_settings.get_boolean("show-recent-file") self.builder = Gtk.Builder() #The Glade file for the plugin self.builder.add_from_file (os.path.join('/', 'usr', 'share', 'ukui-menu', 'plugins', 'ukuimenu.glade')) ukuimenu_settings.connect("changed::user-icon-changed", self.changeimage) ukuimenu_settings.connect("changed::recent-file-changed", self.RecentFileChanged) ukuimenu_settings.connect("changed::show-recent-file", self.RecentFileChanged) ukuimenu_settings.connect("changed::recent-app-num", self.updateHistoryBox) self.FileMonitorthread() self.windowHeight = 505 self.addedHeight = 0 self.ButtonHoverWidgetHistory = None self.window = self.builder.get_object("window") self.expanderApp = self.builder.get_object("expanderApp") self.labelExpanderApp = self.builder.get_object("labelExpanderApp") self.expanderSystem = self.builder.get_object("expanderSystem") self.labelExpanderSystem = self.builder.get_object("labelExpanderSystem") self.categoriesApp = self.builder.get_object("categoriesApp") self.categoriesSystem = self.builder.get_object("categoriesSystem") self.expanderRecent = self.builder.get_object("expanderRecent") self.recentBox = self.builder.get_object("recentBox") self.expanderRecent.hide() self.labelExpander = self.builder.get_object("labelExpander") self.labelExpander.set_text(_("Recent")) self.content_holder = self.builder.get_object("eventbox") self.content_holder.connect( "key-press-event", self.keyPress ) self.content_holder.set_size_request(575, self.windowHeight) self.layout_left = self.builder.get_object("layout_left") self.viewport1 = self.builder.get_object("viewport1") self.viewport2 = self.builder.get_object("viewport2") self.vp_right_container = self.builder.get_object("vp_right_container") self.vp_right_container.set_name("Viewport1") self.viewport_left = self.builder.get_object("viewport_left") self.viewport_left.set_name("Viewport1") self.viewport_right = self.builder.get_object("viewport_right") self.viewport_right.set_name("Viewport1") self.vp_left_container = self.builder.get_object("vp_left_container") self.vp_left_container.set_name("Viewport1") self.scrolledwindow_fav = self.builder.get_object("scrolledwindow_fav") self.scrolledwindow_fav.set_name("Viewport1") self.scrolledwindow_rec = self.builder.get_object("scrolledwindow_recent") self.scrolledwindow_rec.set_name("Viewport1") self.applicationsScrolledWindow = self.builder.get_object("applicationsScrolledWindow") self.applicationsScrolledWindow.set_name("Viewport1") self.scrolledwindow1 = self.builder.get_object("scrolledwindow1") self.scrolledwindow1.set_name("Viewport1") self.scrolledwindow2 = self.builder.get_object("scrolledwindow2") self.scrolledwindow2.set_name("Viewport1") self.viewport_fav = self.builder.get_object("viewport_fav") self.viewport_fav.set_name("Viewport1") self.viewport_allapp = self.builder.get_object("viewport_allapp") self.viewport_allapp.set_name("Viewport1") self.viewport8 = self.builder.get_object("viewport8") self.viewport8.set_name("Viewport1") self.viewport9 = self.builder.get_object("viewport9") self.viewport9.set_name("Viewport1") self.viewport12 = self.builder.get_object("viewport12") self.viewport12.set_name("Viewport1") self.viewport16 = self.builder.get_object("viewport16") self.viewport16.set_name("Viewport1") self.button_user = self.builder.get_object("button_user") self.button_user.connect("button-press-event", self.on_button_user_clicked) self.button_showall = self.builder.get_object("button_showall") self.button_showall.set_label(_("All App")) self.button_showall.set_name("ButtonApp") self.button_showall.connect("clicked", self.on_button_showall_clicked) self.button_back = self.builder.get_object("button_back") self.button_back.set_label(_("Favorite")) self.button_back.set_name("ButtonApp") self.button_back.connect("clicked", self.on_button_back_clicked) self.image_user = self.builder.get_object("image_user") if not os.path.exists(usericonPath): iconpath = "/usr/share/ukui-menu/icons/stock_person.png" os.system("cp %s %s" % (iconpath, usericonPath)) else: iconpath = usericonPath try: pixbuf = Pixbuf.new_from_file(iconpath) except Exception as e: iconpath = "/usr/share/ukui-menu/icons/stock_person.png" os.system("cp %s %s" % (iconpath, usericonPath)) pixbuf = Pixbuf.new_from_file(iconpath) print (e) pixbuf = pixbuf.scale_simple(63, 63, 2) #2 := BILINEAR self.image_user.set_from_pixbuf(pixbuf) self.image1 = self.builder.get_object("image1") iconfile = ICON_PATH + "sep.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(210, 2, 2) #2 := BILINEAR self.image1.set_from_pixbuf(pixbuf) self.image2 = self.builder.get_object("image2") iconfile = ICON_PATH + "sep.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(210, 2, 2) #2 := BILINEAR self.image2.set_from_pixbuf(pixbuf) self.image3 = self.builder.get_object("image3") iconfile = ICON_PATH + "sep.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(80, 2, 2) #2 := BILINEAR self.image3.set_from_pixbuf(pixbuf) self.image5 = self.builder.get_object("image5") iconfile = ICON_PATH + "sep.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(210, 2, 2) #2 := BILINEAR self.image5.set_from_pixbuf(pixbuf) self.button_shutdown = self.builder.get_object("button_shutdown") self.button_shutdown.set_name("ButtonPower") self.viewport18 = self.builder.get_object("viewport18") self.viewport18.set_name("ViewportPower") self.button_shutdown.set_label(_("Power")) self.change_icon(self.button_shutdown, ICON_PATH + "shutdown.svg") self.button_shutdown.connect("button-press-event", self.shutdown) self.button_shutdown.connect("enter", self.button_shutdown_enter) self.button_shutdown.connect("leave", self.button_shutdown_leave) self.button_shutdown1 = self.builder.get_object("button_shutdown1") self.button_shutdown1.set_name("ButtonPower") self.viewport23 = self.builder.get_object("viewport23") self.viewport23.set_name("ViewportPower") self.button_shutdown1.connect("button-press-event", self.menu_right_click) self.button_shutdown1.connect("enter", self.button_shutdown1_enter) self.button_shutdown1.connect("leave", self.button_shutdown1_leave) self.image_showall = self.builder.get_object("image_showall") iconfile = ICON_PATH + "arrow-right.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(5, 7, 2) #2 := BILINEAR self.image_showall.set_from_pixbuf(pixbuf) self.image_shutdown1 = self.builder.get_object("image_shutdown1") iconfile = ICON_PATH + "arrow-right.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(5, 7, 2) #2 := BILINEAR self.image_shutdown1.set_from_pixbuf(pixbuf) self.image_back = self.builder.get_object("image_back") iconfile = ICON_PATH + "arrow-left.png" pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(5, 7, 2) #2 := BILINEAR self.image_back.set_from_pixbuf(pixbuf) #recentbox self.layout_applications_left = self.builder.get_object("layout_applications_left") self.layout_applications_right = self.builder.get_object("layout_applications_right") self.FileList=[] self.favAppList = [] #dirty ugly hack, to get favorites drag origin position self.drag_origin = None self.favoritesPath = os.path.join(GLib.get_home_dir(), ".config","ukui-menu","applications.list") self.favoritesBox = self.builder.get_object("favoritesBox") self.favoritesBox.set_row_spacings(3) self.favoritesBox.connect( "drag-data-received", self.ReceiveCallback ) self.favoritesBox.drag_dest_set ( Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.toButton, Gdk.DragAction.COPY ) self.AddFavBtn() self.hovermenupopup = False self.appHistoryList= self.buildAppHistoryList() self.categoriesBox = self.builder.get_object("categoriesbox") self.categoriesBox.set_spacing(2) self.applicationsBox = self.builder.get_object("applicationsBox") self.applicationsBox.set_spacing(2) self.applicationsScrolledWindow = self.builder.get_object( "applicationsScrolledWindow") self.favappbutton = self.builder.get_object("button_favapp") self.favappbutton.set_label(_("Favorite")) self.favappbutton.set_name("Button") self.change_icon(self.favappbutton, ICON_PATH + "favapp.png") self.favappbutton.connect("enter", self.favappbutton_enter) self.favappbutton.connect("leave", self.favappbutton_leave) self.favappbutton.connect("clicked", lambda c1: self.changeTab(0)) self.viewportfav = self.builder.get_object("viewportfav") self.viewportallapp = self.builder.get_object("viewportallapp") self.allappbutton = self.builder.get_object("button_allapp") self.allappbutton.set_label(_("All App")) self.allappbutton.set_name("Button") self.change_icon(self.allappbutton, ICON_PATH + "allapp.png") self.allappbutton.connect("enter", self.allappbutton_enter) self.allappbutton.connect("leave", self.allappbutton_leave) self.allappbutton.connect("clicked", lambda c2: self.changeTab(1)) self.viewport_entry = self.builder.get_object("viewport_entry") self.viewport_entry.set_name("Viewport1") self.searchEntry =self.builder.get_object( "search_entry" ) self.searchEntry.set_name("Entry") self.searchEntry.set_text(_("Search local program")) self.searchEntry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse('#999')) iconpath = ICON_PATH + "so.png" pixbuf = Pixbuf.new_from_file(iconpath) pixbuf = pixbuf.scale_simple(16, 16, 2) #2 := BILINEAR self.searchEntry.set_icon_from_pixbuf(1, pixbuf) self.searchEntry.set_icon_activatable(1, True) self.searchEntry.connect("changed", self.searchChanged) self.searchEntry.connect("button-press-event", self.entryPopup) self.searchEntry.connect("focus-out-event", self.focusOut) self.searchEntry.connect("focus-in-event", self.focusIn) self.searchEntry.connect("icon-press", self.searchIconPressed) self.searchResultBox = self.builder.get_object( "searchResultBox" ) self.searchResultBox.set_name("AppBox") self.searchResultBox.set_spacing(3) self.recentAppBox = self.builder.get_object( "RecentAppBox" ) self.recentAppBox.set_spacing(3) self.bamfok = False GLib.timeout_add( 100, self.getBamfStatus ) self.updateHistoryBox() self.label_user = self.builder.get_object("label_user") user_name = GLib.get_user_name() self.label_user.set_text(user_name) self.label_user.set_name("myGtkLabel") #left-menu self.button_computer = self.builder.get_object( "button_computer" ) self.button_computer.set_label(_("Home")) self.button_computer.set_name("Button") self.change_icon(self.button_computer, ICON_PATH + "computer.png") self.button_computer.connect("button-press-event",self.on_computer_clicked) self.button_computer.connect("enter", self.button_computer_enter) self.button_computer.connect("leave", self.button_computer_leave) self.button_controlcenter = self.builder.get_object( "button_controlcenter" ) self.button_controlcenter.set_label(_("Settings")) self.button_controlcenter.set_name("Button") self.change_icon(self.button_controlcenter, ICON_PATH + "controlcenter.png") self.button_controlcenter.connect("button-press-event", self.on_controlcenter_clicked) self.button_controlcenter.connect("enter", self.button_controlcenter_enter) self.button_controlcenter.connect("leave", self.button_controlcenter_leave) self.label3 = self.builder.get_object("label3") self.label3.set_text(_("No items matched")) self.iconSize = 24 self.favCols = 1 self.buildingButtonList = False self.stopBuildingButtonList = False self.rebuildLock = False self.activeFilter = (1, "", self.searchEntry) self.current_suggestion = None self.categoryList = [] self.applicationList = [] self.uncategoriedList = [] self.all_app_list = [] self.categorybutton_list = [] self.all_application_list = [] for mainitems in [ "ukui-applications.menu", "ukui-settings.menu" ]: mymenu = Menu( mainitems ) mymenu.tree.add_monitor( self.menuChanged, None ) def getBamfStatus(self): if self.bamfok: return self.bamfok = True try: self.matcher = Bamf.Matcher.get_default() self.matcher.connect('view-opened', self.viewOpened, None) except Exception as e: #print (e) return self.bamfok = False def clearAllRecent(self, *args, **kargs): if os.path.exists(GLib.get_home_dir() + "/.recentAppLog"): os.remove(GLib.get_home_dir() + "/.recentAppLog") self.updateHistoryBox() def change_icon(self, widget, iconfile): pixbuf = Pixbuf.new_from_file(iconfile) pixbuf = pixbuf.scale_simple(ICON_SIZE, ICON_SIZE, 2) #2 := BILINEAR image = Gtk.Image() image.set_from_pixbuf(pixbuf) widget.set_image(image) def favappbutton_enter(self, *args, **kargs): self.change_icon(self.favappbutton, ICON_PATH + "favapp-active.png") def favappbutton_leave(self, *args, **kargs): self.change_icon(self.favappbutton, ICON_PATH + "favapp.png") def button_shutdown_enter(self, *args, **kargs): self.viewport18.set_name("ViewportPowerEnter") def button_shutdown_leave(self, *args, **kargs): self.viewport18.set_name("ViewportPower") def button_shutdown1_enter(self, *args, **kargs): self.viewport23.set_name("ViewportPowerEnter") def button_shutdown1_leave(self, *args, **kargs): self.viewport23.set_name("ViewportPower") def allappbutton_enter(self, *args, **kargs): self.change_icon(self.allappbutton, ICON_PATH + "allapp-active.png") def allappbutton_leave(self, *args, **kargs): self.change_icon(self.allappbutton, ICON_PATH + "allapp.png") def button_computer_enter(self, *args, **kargs): self.change_icon(self.button_computer, ICON_PATH + "computer-active.png") def button_computer_leave(self, *args, **kargs): self.change_icon(self.button_computer, ICON_PATH + "computer.png") def on_computer_clicked(self, widget ,event): realPath = GLib.get_home_dir() current_desktop = os.getenv("XDG_CURRENT_DESKTOP") if current_desktop == "GNOME": os.system("nautilus %s" % realPath) elif current_desktop == "MATE": os.system("caja %s" % realPath) else: os.system("peony %s" % realPath) def button_controlcenter_enter(self, *args, **kargs): self.change_icon(self.button_controlcenter, ICON_PATH + "controlcenter-active.png") def button_controlcenter_leave(self, *args, **kargs): self.change_icon(self.button_controlcenter, ICON_PATH + "controlcenter.png") def on_controlcenter_clicked(self, widget, event): current_desktop = os.getenv("XDG_CURRENT_DESKTOP") if current_desktop == "GNOME": os.system("gnome-control-center &") elif current_desktop == "MATE": os.system("mate-control-center &") else: os.system("ukui-control-center &") def on_button_user_clicked (self, widget, event): self.ukuiMenuWin.hide() if os.path.exists("/usr/bin/ukui-control-center"): os.system("ukui-control-center -u &") elif os.path.exists("/usr/bin/mate-about-me"): os.system('mate-about-me &') def on_button_showall_clicked(self, widget): self.changeTab(1) def on_button_back_clicked(self, widget): self.changeTab(0) def ShowRecentFile(self, widget): widget.rightbox.hide() RecentInfo = RecManagerInstance.get_items() #wps if widget.appName == _("WPS Writer"): for item in RecentInfo: if item.get_mime_type() in WPS_W: widget.rightbox.show_all() if widget.appName == _("WPS Presentation"): for item in RecentInfo: if item.get_mime_type() in WPS_P: widget.rightbox.show_all() if widget.appName == _("WPS Spreadsheets"): for item in RecentInfo: if item.get_mime_type() in WPS_S: widget.rightbox.show_all() #gtk app if widget.appName == _("Pluma"): for item in RecentInfo: if item.has_application("Pluma"): widget.rightbox.show_all() if widget.appName == _("Eye of MATE Image Viewer"): for item in RecentInfo: if item.has_application("Eye of MATE Image Viewer"): widget.rightbox.show_all() if widget.appName == _("Atril Document Viewer"): for item in RecentInfo: if item.has_application("Atril Document Viewer"): widget.rightbox.show_all() #qt app if widget.appName == _("Qt Creator"): cf = UkuiConfigParser() path = os.path.join(GLib.get_user_config_dir(), 'QtProject/QtCreator.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("RecentFiles", "Files") if recentFiles is not "" and recentFiles != "@Invalid()": widget.rightbox.show_all() if widget.appName == _("Kylin Video"): cf = UkuiConfigParser() path = os.path.join(GLib.get_user_config_dir(), 'kylin-video/kylin-video.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("history", "recents") if recentFiles is not "": widget.rightbox.show_all() def AddFavBtn(self): self.favorites = [] try: self.checkUkuiMenuFolder() if not os.path.isfile(self.favoritesPath): shutil.copyfile("/usr/share/ukui-menu/applications.list", self.favoritesPath) applicationsFile = open(self.favoritesPath, "r") applicationsList = applicationsFile.readlines() applicationsFile.close() for child in self.favoritesBox: child.destroy() position = 0 for app in applicationsList: app = app.strip() if app[0:9] == "location:": favButton = self.favoritesBuildLauncher( app[9:] ) if favButton: favButton.set_name("ButtonApp") favButton.position = position self.favorites.append( favButton ) self.favoritesPositionOnGrid( favButton ) favButton.connect( "drag-data-received", self.onFavButtonDragReorder ) favButton.drag_dest_set( Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.fromFav, Gdk.DragAction.COPY ) favButton.connect( "drag-data-get", self.onFavButtonDragReorderGet ) favButton.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, self.toFav, Gdk.DragAction.COPY ) position += 1 if self.showRecentFile: self.ShowRecentFile(favButton) favButton.connect( "enter", self.showHistory ) favButton.connect( "leave", self.hideHistory ) self.favoritesSave() except Exception as e: print (e) Gdk.flush() def favoritesSave(self): try: self.checkUkuiMenuFolder() appListFile = open(self.favoritesPath, "w") for favorite in self.favorites: if favorite.type == "location": appListFile.write( "location:" + favorite.desktopFile + "\n" ) else: appListFile.write( favorite.type + "\n" ) appListFile.close() except Exception as e: print (e) def isLocationInFavorites(self, location): for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: return True return False def checkUkuiMenuFolder(self): if os.path.exists( os.path.join( os.path.expanduser("~"), ".config", "ukui-menu", "applications" ) ): return True try: os.makedirs( os.path.join( os.path.expanduser("~"), ".config", "ukui-menu", "applications" ) ) return True except: pass return False def favoritesPositionOnGrid(self, favorite): row = 0 col = 0 for fav in self.favorites: if fav.position == favorite.position: break row +=1 self.favoritesBox.attach(favorite, col, col + 1, row, row + 1) #table自适应填充,随fav多少高度跟随改变 def onFavButtonDragReorderGet(self, widget, context, selection, targetType, eventTime ): if targetType == self.TARGET_TYPE_FAV: self.drag_origin = widget.position selection.set( selection.get_target(), 8, str(widget.position).encode()) def onFavButtonDragReorder(self, widget, context, x, y, selection, targetType, time): if targetType == self.TARGET_TYPE_FAV: #self.favoritesReorder( int(selection.data), widget.position ) self.favoritesReorder( self.drag_origin, widget.position ) def favoritesReorder( self, oldposition, newposition ): if oldposition == newposition: return tmp = self.favorites[ oldposition ] if newposition > oldposition: if ( self.favorites[ newposition - 1 ].type == "space" or self.favorites[ newposition - 1 ].type == "separator" ) and self.favCols > 1: newposition = newposition - 1 for i in range( oldposition, newposition ): self.favorites[ i ] = self.favorites[ i + 1 ] self.favorites[ i ].position = i elif newposition < oldposition: for i in range( 0, oldposition - newposition ): self.favorites[ oldposition - i ] = self.favorites[ oldposition - i - 1 ] self.favorites[ oldposition - i ] .position = oldposition - i self.favorites[ newposition ] = tmp self.favorites[ newposition ].position = newposition for fav in self.favorites: self.favoritesBox.remove( fav ) self.favoritesPositionOnGrid( fav ) self.favoritesSave() self.favoritesBox.resize( self.favoritesGetNumRows(), self.favCols ) def callback(self, widget, FileData): Data, FileName = FileData uri, AppName, appname = Data self.ukuiMenuWin.hide() if os.path.exists(FileName): pass else: msg = _("\nFile not exists, it was moved or deleted!\n\nIt will be automatically removed from the history list!\n") md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() self.onRecentFileRemove(None, Data) return if AppName == _("WPS Writer"): os.system("wps %s &" % FileName) if AppName == _("WPS Presentation"): os.system("wpp %s &" % FileName) if AppName == _("WPS Spreadsheets"): os.system("et %s &" % FileName) if AppName == _("Qt Creator"): os.system("qtcreator %s &" % FileName) if AppName == _("Kylin Video"): os.system("kylin-video %s &" % FileName) if AppName == _("Eye of MATE Image Viewer"): os.system("eom %s &" % FileName) if AppName == _("Atril Document Viewer"): os.system("atril %s &" % FileName) if AppName == _("Pluma"): os.system("pluma %s &" % FileName) def favoritesBuildLauncher(self, location): try: ButtonIcon = None #location = string.join(location.split("%20")) for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: return None favButton = FavApplicationLauncher( location, 32, False) if favButton.appExec: favButton.show() favButton.connect( "clicked", lambda w: self.ukuiMenuWin.hide() ) favButton.connect( "button-press-event", self.favPopup ) #favButton.set_tooltip_text( favButton.getTooltip() ) favButton.type = "location" return favButton except Exception as e: print ("s") def entryPopup( self, widget, ev ): if ev.button == 3: self.ukuiMenuWin.stopHiding() def focusOut( self, widget, ev ): self.searchEntry.set_name("Entry") self.searchEntry.set_text(_("Search local program")) iconpath = ICON_PATH + "so.png" pixbuf = Pixbuf.new_from_file(iconpath) pixbuf = pixbuf.scale_simple(16, 16, 2) #2 := BILINEAR self.searchEntry.set_icon_from_pixbuf(1, pixbuf) def focusIn( self, widget, ev ): text = self.searchEntry.get_text() if text == _("Search local program"): self.searchEntry.set_text("") def favPopup( self, widget, ev ): button = MenuApplicationLauncher(widget.desktopFile, 32, "", True, highlight=(False)) button.set_name("ButtonAppHover") addedDesktop = False try: if (os.path.exists(GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP) + "/" + os.path.basename(widget.desktopFile))): addedDesktop = True except Exception as detail: print (detail) text = "" if ev.button == 3: if widget.type == "location": if (os.path.exists(GLib.get_home_dir() + "/.applet")): f = open(GLib.get_home_dir() + "/.applet") text = f.read() mTree = Gtk.Menu() mTree.set_name("myGtkLabel") mTree.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK) launchMenuItem = Gtk.MenuItem(_("Open(_O)")) launchMenuItem.set_use_underline(True) separator1 = Gtk.SeparatorMenuItem() desktopMenuItem = Gtk.MenuItem(_("Add to desktop")) desktopMenuItem.set_use_underline(True) if addedDesktop: desktopMenuItem.set_sensitive(False) panelMenuItem = Gtk.MenuItem(_("Lock to panel(_L)")) panelMenuItem.set_use_underline(True) separator3 = Gtk.SeparatorMenuItem() removeFromFavMenuItem = Gtk.MenuItem(_("Unlock from startup menu")) if len(self.favorites) < 2: removeFromFavMenuItem.set_sensitive(False) separator4 = Gtk.SeparatorMenuItem() propsMenuItem = Gtk.MenuItem(_("Property(_P)")) propsMenuItem.set_use_underline(True) desktopMenuItem.connect("activate", self.add_to_desktop, widget) panelMenuItem.connect("activate", self.add_to_panel, widget) launchMenuItem.connect( "activate", self.onLaunchApp, widget) removeFromFavMenuItem.connect( "activate", self.onFavoritesRemove, widget ) propsMenuItem.connect( "activate", self.onPropsApp, widget) mTree.append(launchMenuItem) mTree.append(separator1) if self.de == "ukui": mTree.append(desktopMenuItem) mTree.append(panelMenuItem) if button.appName in text: panelMenuItem.set_sensitive(False) mTree.append(separator3) mTree.append(removeFromFavMenuItem) mTree.append(separator4) mTree.append(propsMenuItem) mTree.show_all() mTree.connect("deactivate", self.set_item_state, widget) self.ukuiMenuWin.stopHiding() widget.remove(widget.Align1) widget.Align1.remove(widget.HBox1) widget.viewport.show() widget.viewport.add(widget.HBox1) widget.add(widget.viewport) widget.set_name("ButtonAppHover") mTree.attach_to_widget(widget) mTree.popup(None, None, None, None, ev.button, ev.time) def FileMonitorthread(self): _thread.start_new_thread(FileMonitor, (self, )) def get_realPath(self, path, configDir): realPath = GLib.get_home_dir() + path try: config = ConfigObj(GLib.get_home_dir() + "/.config/user-dirs.dirs") chinesePath = config[configDir] chinesePath = subprocess.getoutput("echo "+ chinesePath) if os.path.exists(chinesePath): realPath = chinesePath except Exception as detail: print (detail) return realPath def changeimage(self, settings, key, args = None): usericonPath = get_user_icon() pixbuf = Pixbuf.new_from_file(usericonPath) pixbuf = pixbuf.scale_simple(63, 63, 2) #2 := BILINEAR self.image_user.set_from_pixbuf(pixbuf) def RecentFileChanged(self, settings = None, key = None, args = None): self.showRecentFile = ukuimenu_settings.get_boolean("show-recent-file") self.AddFavBtn() self.updateHistoryBox() def shutdownmenuPopup(self, widget): if True: #1-左右,2-中,3-右 mTree = Gtk.Menu() mTree.set_name("myGtkLabel") shutdown = Gtk.MenuItem(_("Shutdown")) reboot = Gtk.MenuItem(_("Reboot")) separatorMenuItem = Gtk.SeparatorMenuItem() separatorMenuItem.set_visible(True) logout = Gtk.MenuItem(_("Logout")) changeuser = Gtk.MenuItem(_("Switch User")) lock = Gtk.MenuItem(_("Lock Screen")) mTree.append(lock) arm = False machine = platform.machine() if machine == "aarch64": arm = True else: arm = False if not arm: mTree.append(changeuser) mTree.append(logout) mTree.append(separatorMenuItem) mTree.append(reboot) mTree.append(shutdown) mTree.show_all() lock.connect("activate", self.lock, widget) changeuser.connect("activate", self.switchuser, widget) logout.connect("activate", self.logout, widget) reboot.connect("activate", self.reboot, widget) shutdown.connect("activate", self.shutdown, widget) self.ukuiMenuWin.stopHiding() mTree.connect("deactivate", self.set_state, widget) return mTree def set_state(self, menu, widget): self.viewport23.set_sensitive(True) def menu_right_click(self, widget, event): mTree = self.shutdownmenuPopup(widget) mTree.attach_to_widget(widget) mTree.popup(None, None, None, None, event.button, event.time) self.hovermenupopup = True return True # event has been handled def switchuser(self, menu, widget): system_bus = dbus.SystemBus() xdg_seat_path = os.environ['XDG_SEAT_PATH'] obj = system_bus.get_object('org.freedesktop.DisplayManager', xdg_seat_path) interface = dbus.Interface(obj,'org.freedesktop.DisplayManager.Seat') interface.SwitchToGreeter() self.ukuiMenuWin.hide() Gdk.flush() def logout(self, menu, widget): os.system("ukui-session-save --logout-dialog &") self.ukuiMenuWin.hide() Gdk.flush() def reboot(self, menu, widget): session_bus = dbus.SessionBus() obj = session_bus.get_object('org.gnome.SessionManager','/org/gnome/SessionManager') interface = dbus.Interface(obj,'org.gnome.SessionManager') interface.RequestReboot() self.ukuiMenuWin.hide() Gdk.flush() def shutdown(self, menu, widget): os.system("ukui-session-save --shutdown-dialog &") self.ukuiMenuWin.hide() Gdk.flush() def lock(self, menu, widget): os.system( "ukui-screensaver-command -l" ) self.ukuiMenuWin.hide() Gdk.flush() def onShowMenu(self): screenHeight = Gdk.Screen.height() heightPath = os.path.join(GLib.get_home_dir(), ".windowHeight") if (self.currentHisCount + len(self.favAppList) - 1) < 10: self.windowHeight = 505 self.addedHeight = 0 if os.path.exists(heightPath): os.remove(heightPath) else: self.addedHeight = 41*((self.currentHisCount + len(self.favAppList) - 1) - 10) self.windowHeight = 505 + self.addedHeight f = open(heightPath, 'w') f.write(str(self.windowHeight)) f.close() if os.path.exists(heightPath): f = open(heightPath, "r") lines = f.readlines() length = lines[0] aa = int(length) f.close() if self.windowHeight > screenHeight - 60: self.addedHeight = screenHeight - 60 - 505 self.windowHeight = screenHeight - 60 self.content_holder.set_size_request(340, self.windowHeight) self.updateHeight() Gdk.flush() def updateHeight(self): self.layout_left.move(self.viewport_entry, 103, 465+self.addedHeight) self.layout_left.move(self.viewport18, 4, 465+self.addedHeight) self.layout_left.move(self.viewport23, 75, 465+self.addedHeight) self.viewport1.set_size_request(236, 455+self.addedHeight) self.vp_left_container.set_size_request(227, 450+self.addedHeight) self.viewport2.set_size_request(236, 455+self.addedHeight) self.vp_right_container.set_size_request(227, 450+self.addedHeight) def do_plugin(self): self.Todos() def Todos(self): self.searchEntry.connect( "changed", self.Filter ) self.searchEntry.connect( "activate", self.Search ) self.buildButtonList() def buildButtonList(self): if self.buildingButtonList: self.stopBuildingButtonList = True GLib.timeout_add( 100, self.buildButtonList ) return self.stopBuildingButtonList = False self.updateBoxes(False) def changeShowCategoryIcons(self, settings, key, args): categoryIconSize = self.iconSize for child in self.categoriesBox: child.setIconSize( categoryIconSize ) def menuChanged(self, x, y): # wait some miliseconds because there a multiple events send at the same time and we don't want to rebuild the menu for each if self.menuChangedTimer: GLib.source_remove( self.menuChangedTimer ) self.menuChangedTimer = GLib.timeout_add( 100, self.updateBoxes, True ) def updateBoxes(self, menu_has_changed): #update fav app and history app. self.appHistoryList= self.buildAppHistoryList() self.RecentFileChanged() if self.rebuildLock: return self.rebuildLock = True self.menuChangedTimer = None self.loadMenuFiles() self.all_application_list = self.build_application_list() if self.showCategoryMenu: new_category_list = self.buildCategoryList() else: cat = Category(self.all_application_list) new_category_list = cat.category_list() added_category_list = [] removed_category_list = [] if not self.categoryList: added_category_list = new_category_list else: for item in new_category_list: found = False for item2 in self.categoryList: if item2['name'] == item['name'] and \ item2['icon'] == item['icon'] and \ item2['tooltip'] == item['tooltip'] : found = True break if not found: added_category_list.append(item) for item in self.categoryList: found = False for item2 in new_category_list: if item2['name'] == item['name'] and \ item2['icon'] == item['icon'] and \ item2['tooltip'] == item['tooltip'] : found = True break if not found: removed_category_list.append(item) category_icon_size = self.iconSize for item in removed_category_list: try: button = item['button'] self.categoryList.remove(item) button.destroy() del item except Exception as e: print (e) if 1: sorted_category_list = [] for item in self.categoryList: try: self.categoriesBox.remove(item['button']) if self.showCategoryMenu: self.categoriesApp.remove(item['button']) self.categoriesSystem.remove(item['button']) if not self.showCategoryMenu: sorted_category_list.append( ( item['name'], item['button'] ) ) except Exception as e: print (e) if self.showCategoryMenu: self.list = self.buildCategoryList() else: self.list = added_category_list for item in self.list: try: if self.showCategoryMenu: item["filter"] = item["name"] item["button"] = CategoryButton( item["icon"], category_icon_size, [item["name"]], item["filter"]) else: if item["name"] == "附件": item["button"] = CategoryButton( item["icon"], category_icon_size, [_("Accessories")], item["filter"] ) if item["name"] == "办公": item["filter"] = _("Office") if cat.hasOffice == False: continue; item["button"] = CategoryButton( item["icon"], category_icon_size, [_("Office")], item["filter"] ) if item["name"] == "启动": item["button"] = CategoryButton( item["icon"], category_icon_size, [_("StartUp")], item["filter"] ) if item["name"] == "游戏": item["filter"] = _("Games") if cat.hasGame == False: continue; item["button"] = CategoryButton( item["icon"], category_icon_size, [_("Games")], item["filter"] ) if item["name"] == "安卓兼容": item["filter"] = _("Android") if cat.hasAndroid == False: continue; item["button"] = CategoryButton( item["icon"], category_icon_size, [_("Android")], item["filter"] ) item["button"].set_name("ButtonApp") self.categorybutton_list.append(item["button"]) item["button"].connect( "button-press-event", self.FilterAndClear, item["filter"] ) item["button"].connect( "focus-in-event", self.scrollItemIntoView ) #feng item["button"].connect( "enter", self.showGoNext ) item["button"].connect( "leave", self.hideGoNext ) if not self.showCategoryMenu: if server: if item["name"] == "游戏" or item["name"] == "启动": continue; if serverx86: if item["name"] == "游戏" or item["name"] == "启动": continue; if item["name"] == "启动": continue; item["button"].show() self.categoryList.append( item ) sorted_category_list.append( ( item["name"], item["button"] ) ) except Exception as e: print (e) if not self.showCategoryMenu: sorted_category_list.sort() currentApp = False currentSystem = False for item in sorted_category_list: try: if self.showCategoryMenu: self.categoriesBox.set_spacing(1) if item[0] == _("Applications"): self.labelExpanderApp.set_label(_("Applications")) currentApp = True currentSystem = False continue if item[0] == _("System"): self.labelExpanderSystem.set_label(_("System")) currentSystem = True currentApp = False continue if currentApp: self.categoriesApp.pack_start(item[1], False, False, 0) if currentSystem: self.categoriesSystem.pack_start(item[1], False, False, 0) else: self.expanderApp.hide() self.expanderSystem.hide() self.categoriesBox.set_spacing(2) self.categoriesBox.pack_start( item[1], False, False, 0 ) except Exception as e: print (e) if self.showCategoryMenu: new_application_list = self.all_application_list else: new_application_list = cat.application_list() added_application_list = [] removed_application_list = [] #处理分类中有程序安装或删除时的显示更新 if not self.applicationList: added_application_list = new_application_list else: for item in new_application_list: found = False for item2 in self.applicationList: if item['desktop_file_path'] == item2['desktop_file_path']: found = True break if not found: added_application_list.append(item) key = 0 for item in self.applicationList: found = False for item2 in new_application_list: if item['desktop_file_path'] == item2['desktop_file_path']: found = True break if not found: removed_application_list.append(key) else: key += 1 for key in removed_application_list: self.applicationList[key]['button'].destroy() del self.applicationList[key] sorted_application_list = [] for child in self.applicationsBox.get_children(): self.applicationsBox.remove( child ) #for item in self.applicationList: # sorted_application_list.append( ( item["button"].appName, item["button"] ) ) for item in new_application_list: found = False for item1 in added_application_list: if item == item1: button = self.build_application_launcher(item, menu_has_changed) if button: item["button"] = button item["button"].connect( "focus-in-event", self.scrollItemIntoView ) #feng if item["button"].appNoDisplay == True: continue sorted_application_list.append( ( item["button"].appName, item["button"] ) ) self.applicationList.append( item ) found = True continue if not found: button = self.build_application_launcher(item, False) if button: item["button"] = button item["button"].connect( "focus-in-event", self.scrollItemIntoView ) #feng sorted_application_list.append( ( item["button"].appName, item["button"] ) ) self.applicationList.append( item ) if not self.showCategoryMenu: sorted_application_list.sort() launcherNames = [] # Keep track of launcher names so we don't add them twice in the list. for item in sorted_application_list: #for item in new_application_list: launcherName = item[0] button = item[1] self.applicationsBox.pack_start( button, False, False, 0 ) button.show() if launcherName in launcherNames: button.hide() else: launcherNames.append(launcherName) self.expanderRecent.hide() self.uncategorized_list = [] for item in self.all_application_list: found = False for item2 in self.applicationList: if item['desktop_file_path'] == item2['desktop_file_path'] or os.path.basename(item['desktop_file_path']) == os.path.basename(item2['desktop_file_path']): found = True break if not found: self.uncategorized_list.append(copy.deepcopy(item)) new_uncategoried_list = self.uncategorized_list added_uncategoried_list = [] removed_uncategoried_list = [] #处理未分类中有程序的删除或安装时的更新显示 if not self.uncategoriedList: added_uncategoried_list = new_uncategoried_list else: for item in new_uncategoried_list: found = False for item2 in self.uncategoriedList: if item['desktop_file_path'] == item2['desktop_file_path']: found = True break if not found: added_uncategoried_list.append(item) key = 0 for item in self.uncategoriedList: found = False for item2 in new_uncategoried_list: if item['desktop_file_path'] == item2['desktop_file_path']: found = True break if not found: removed_uncategoried_list.append(key) else: key += 1 for key in removed_uncategoried_list: self.uncategoriedList[key]['button'].destroy() del self.uncategoriedList[key] sorted_uncategoried_list = [] for item in self.uncategoriedList: self.categoriesBox.remove( item["button"] ) sorted_uncategoried_list.append( ( item["button"].appName, item["button"] ) ) for item in added_uncategoried_list: button = self.build_application_launcher(item, menu_has_changed) if button: item["button"] = button item["button"].connect( "focus-in-event", self.scrollItemIntoView ) #feng sorted_uncategoried_list.append( ( item["button"].appName, item["button"] ) ) self.uncategoriedList.append( item ) if not self.showCategoryMenu: sorted_uncategoried_list.sort() launcherNames = [] # Keep track of launcher names so we don't add them twice in the list. for item in sorted_uncategoried_list: launcherName = item[0] button = item[1] self.categoriesBox.pack_start( button, False, False, 0 ) button.show() if launcherName in launcherNames: button.hide() else: launcherNames.append(launcherName) sorted_all_application_list = [] for item in self.all_application_list: button = self.build_application_launcher(item, False) if button: item["button"] = button item["button"].connect( "focus-in-event", self.scrollItemIntoView ) #feng sorted_all_application_list.append( ( item['button'].appName, item['button'] ) ) if not self.showCategoryMenu: sorted_all_application_list.sort() self.all_app_list = sorted_all_application_list self.rebuildLock = False def build_application_launcher(self, application, menu_has_changed): button = MenuApplicationLauncher( application['desktop_file_path'], self.iconSize, application["category"], True, 28, highlight = (True and menu_has_changed) ) button.set_name("ButtonApp") if button.appName == "Logout" or button.appName == "Shutdown" or button.appName == "Reboot": return None if button.appExec: button.set_tooltip_text( button.getTooltip() ) button.connect( "clicked", lambda w: self.ukuiMenuWin.hide() ) button.connect( "button-press-event", self.menuPopup ) button.desktop_file_path = application['desktop_file_path'] if button.appName == "Eclipse": button.set_tooltip_text( " Eclipse\n 描述:Eclipse集成开发环境" ) if button.appName == "Qt Creator": button.set_tooltip_text( " Qt Creator\n 描述:Qt Creator集成开发环境" ) return button else: button.destroy() return None def build_application_list(self): application_list = [] def find_applications_recursively(app_list, directory, catName): for item in directory.get_contents(): if item.get_type() == ukuimenu.TYPE_ENTRY: app_list.append( { 'desktop_file_path': item.get_desktop_file_path().decode('utf-8'), 'category': entry.get_name().decode('utf-8') } ) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: find_applications_recursively(app_list, item, catName) for menu in self.menuFiles: root = menu.directory for entry in root.get_contents(): if entry.get_type() == ukuimenu.TYPE_DIRECTORY and len(entry.get_contents()): for item in entry.get_contents(): if item.get_type() == ukuimenu.TYPE_DIRECTORY: find_applications_recursively(application_list, item, entry.get_name().decode('utf-8')) elif item.get_type() == ukuimenu.TYPE_ENTRY: application_list.append({ 'desktop_file_path': item.get_desktop_file_path().decode('utf-8'), 'category': entry.get_name().decode('utf-8') }) return application_list def loadMenuFiles(self): self.menuFiles = [] for mainitems in [ "ukui-applications.menu", "ukui-settings.menu" ]: mymenu = Menu( mainitems ) self.menuFiles.append( mymenu ) mymenu.tree.add_monitor( self.menuChanged, None ) def buildAppHistoryList(self): self.loadMenuFiles() newApplicationsList = [] try: def find_applications_recursively(app_list, directory, catName): for item in directory.get_contents(): if item.get_type() == ukuimenu.TYPE_ENTRY: app_list.append( { "entry": item, "category": catName } ) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: find_applications_recursively(app_list, item, catName) for menu in self.menuFiles: directory = menu.directory for entry in directory.get_contents(): if entry.get_type() == ukuimenu.TYPE_DIRECTORY and len(entry.get_contents()): for item in entry.get_contents(): if item.get_type() == ukuimenu.TYPE_DIRECTORY: find_applications_recursively(newApplicationsList, item, entry.get_name().decode('utf-8')) elif item.get_type() == ukuimenu.TYPE_ENTRY: newApplicationsList.append( { "entry": item, "category": entry.get_name().decode('utf-8') } ) return newApplicationsList except Exception as detail: print (detail) return None def buildCategoryList(self): newCategoryList = [ ] num = 1 for menu in self.menuFiles: if self.showCategoryMenu: name = menu.tree.get_root_directory().get_name().decode('utf-8') icon = menu.tree.get_root_directory().get_icon().decode('utf-8') if len(menu.directory.get_contents()): newCategoryList.append( { "name": name, "icon": icon, "tooltip": name, "filter": name, "index": num } ) if name == _("Applications"): self.expanderApp.show() if name == _("System"): self.expanderSystem.show() else: if name == _("Applications"): self.expanderApp.hide() if name == _("System"): self.expanderSystem.hide() for child in menu.directory.get_contents(): if child.get_type() == ukuimenu.TYPE_DIRECTORY: newCategoryList.append( { "name": child.get_name().decode('utf-8'), "icon": child.get_icon().decode('utf-8'), "tooltip": child.get_name().decode('utf-8'), "filter": child.get_name().decode('utf-8'), "index": num } ) num += 1 return newCategoryList def destroy( self ): self.applicationsBox.destroy() self.categoriesBox.destroy() self.searchResultBox.destroy() self.searchEntry.destroy() def FilterAndClear( self, widget, event, category = None ): for widget1 in self.categorybutton_list: if widget1 == widget: continue else: widget1.aState = False widget1.rightbox.hide() if widget.aState: self.layout_applications_right.hide() self.content_holder.set_size_request(340, self.windowHeight) widget.aState = False widget.set_name("ButtonApp") else: self.layout_applications_right.show() self.content_holder.set_size_request(575, self.windowHeight) widget.aState = True widget.grab_focus() for widget1 in self.categorybutton_list: if widget1 != widget: widget1.aState = False widget1.rightbox.hide() widget.set_name("ButtonAppHover") if self.ButtonHoverWidgetHistory and self.ButtonHoverWidgetHistory != widget: self.ButtonHoverWidgetHistory.set_name("ButtonApp") self.ButtonHoverWidgetHistory = widget self.Filter( widget, category) def Filter(self, widget, category = None): self.filterTimer = None if widget == self.searchEntry: searchText = self.searchEntry.get_text() if searchText == _("Search local program"): return self.layout_applications_right.hide() if False: widget.set_text( "" ) else: text = widget.get_text() if self.lastActiveTab != 2: self.changeTab( 2, clear = False ) text = widget.get_text() if text == "": self.changeTab(1) showns = False # Are any app shown? self.shownList = [] for child in self.searchResultBox: self.searchResultBox.remove(child) num = 1 for num in range(1, 4): for i in self.all_app_list: shown = i[1].filterText( text, num ) if (shown): dupe = False for item in self.shownList: if i[1].desktopFile == item.desktopFile: dupe = True if not dupe: self.shownList.append(i[1]) self.searchResultBox.pack_start(i[1], False, False, 0) i[1].show() if len(self.shownList) == 0: self.searchResultBox.pack_start(self.label3, True, True, 0) self.label3.show() self.activeFilter = (0, text, widget) else: #print "CATFILTER" self.activeFilter = (1, category, widget) if category == "": listedDesktopFiles = [] for i in self.applicationsBox.get_children(): if not i.desktop_file_path in listedDesktopFiles: listedDesktopFiles.append( i.desktop_file_path ) i.show_all() else: i.hide() else: for i in self.applicationsBox.get_children(): if i == self.expanderRecent: self.expanderRecent.hide() continue i.filterCategory( category ) if not self.showCategoryMenu: self.expanderApp.hide() self.expanderSystem.hide() for i in self.categoriesBox.get_children(): if i == self.expanderApp or self.expanderSystem: continue i.released() i.set_relief( Gtk.ReliefStyle.NONE ) self.applicationsScrolledWindow.get_vadjustment().set_value( 0 ) def menuPopup(self, widget, event): button = MenuApplicationLauncher(widget.desktopFile, 32, "", True, highlight=(False)) addedDesktop = False try: if (os.path.exists(GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP) + "/" + os.path.basename(widget.desktopFile))): addedDesktop = True except Exception as detail: print (detail) text = "" if event.button == 3: if (os.path.exists(GLib.get_home_dir() + "/.applet")): f = open(GLib.get_home_dir() + "/.applet") text = f.read() mTree = Gtk.Menu() mTree.set_name("myGtkLabel") launchMenuItem = Gtk.MenuItem(_("Open(_O)")) launchMenuItem.set_use_underline(True) separator1 = Gtk.SeparatorMenuItem() desktopMenuItem = Gtk.MenuItem(_("Add to desktop")) if addedDesktop: desktopMenuItem.set_sensitive(False) panelMenuItem = Gtk.MenuItem(_("Lock to panel(_L)")) panelMenuItem.set_use_underline(True) separator2 = Gtk.SeparatorMenuItem() favoriteMenuItem = Gtk.CheckMenuItem(_("Add to startup menu")) separator3 = Gtk.SeparatorMenuItem() uninstallMenuItem = Gtk.MenuItem(_("Uninstall")) deleteMenuItem = Gtk.MenuItem(_("Remove from list(_R)")) deleteMenuItem.set_use_underline(True) alldelete = Gtk.MenuItem(_("Remove all")) separator4 = Gtk.SeparatorMenuItem() propsMenuItem = Gtk.MenuItem(_("Property(_P)")) propsMenuItem.set_use_underline(True) mTree.append(launchMenuItem) mTree.append(separator1) if self.de == "ukui": mTree.append(desktopMenuItem) mTree.append(panelMenuItem) if button.appName in text: panelMenuItem.set_sensitive(False) mTree.append(separator2) mTree.append(favoriteMenuItem) if widget.isRecentApp: mTree.append(deleteMenuItem) mTree.append(separator4) mTree.append(alldelete) deleteMenuItem.connect("activate", self.delete_from_menu, widget) alldelete.connect("activate", self.clearAllRecent) else: if os.path.exists("/usr/bin/ubuntu-kylin-software-center"): mTree.append(separator3) mTree.append(uninstallMenuItem) uninstallMenuItem.connect ( "activate", self.onUninstallApp, widget ) mTree.append(separator4) mTree.append(propsMenuItem) mTree.show_all() launchMenuItem.connect( "activate", self.onLaunchApp, widget ) desktopMenuItem.connect("activate", self.add_to_desktop, widget) panelMenuItem.connect("activate", self.add_to_panel, widget) if self.isLocationInFavorites( widget.desktopFile ): if len(self.favorites) < 2: favoriteMenuItem.set_sensitive(False) favoriteMenuItem.set_active( True ) favoriteMenuItem.connect( "toggled", self.onRemoveFromFavorites, widget ) else: favoriteMenuItem.set_active( False ) favoriteMenuItem.connect( "toggled", self.onAddToFavorites, widget ) propsMenuItem.connect( "activate", self.onPropsApp, widget) mTree.connect("deactivate", self.set_item_state, widget) widget.set_name("ButtonAppHover") self.ukuiMenuWin.stopHiding() widget.remove(widget.Align1) widget.Align1.remove(widget.HBox1) widget.viewport.show() widget.viewport.add(widget.HBox1) widget.add(widget.viewport) mTree.attach_to_widget(widget) mTree.popup(None, None, None, None, event.button, event.time) def set_item_state(self, menu, widget): button = MenuApplicationLauncher(widget.desktopFile, 32, "", True, highlight=(False)) widget.emit("leave") widget.set_name("ButtonApp") widget.viewport.remove(widget.HBox1) widget.remove(widget.viewport) widget.Align1.add(widget.HBox1) widget.add(widget.Align1) def add_to_desktop(self, widget, desktopEntry): try: os.system("cp \"%s\" \"%s/\"" % (desktopEntry.desktopFile, GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP))) os.system("chmod a+rx %s/*.desktop" % GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP)) except Exception as detail: print (detail) def add_to_panel(self, widget, desktopEntry): self.get_panel() i = 0 panel_schema = Gio.Settings.new("org.ukui.panel") applet_list = panel_schema.get_strv("object-id-list") while True: test_obj = "object_%d" % (i) if test_obj in applet_list: i += 1 else: break path = "/org/ukui/panel/objects/%s/" % (test_obj) new_schema = Gio.Settings.new_with_path("org.ukui.panel.object", path) new_schema.set_string("launcher-location", desktopEntry.desktopFile) new_schema.set_string("object-type", "launcher") new_schema.set_string("toplevel-id", self.panel) new_schema.set_int("position", 50) applet_list.append(test_obj) panel_schema.set_strv("object-id-list", applet_list) def get_panel(self): panelsettings = Gio.Settings.new("org.ukui.panel") applet_list = panelsettings.get_strv("object-id-list") for applet in applet_list: object_schema = Gio.Settings.new_with_path("org.ukui.panel.object", "/org/ukui/panel/objects/%s/" % (applet)) keys = object_schema.list_keys() if "applet-iid" in keys: iid = object_schema.get_string("applet-iid") if iid is not None and iid.find("UkuiMenu") != -1: self.panel = object_schema.get_string("toplevel-id") self.panel_position = object_schema.get_int("position") + 1 def onLaunchApp( self, menu, widget ): widget.execute() self.ukuiMenuWin.hide() def onFavoritesRemove( self, menu, widget ): self.favoritesRemove( widget.position ) def favoritesRemove( self, position ): tmp = self.favorites[ position ] self.favorites.remove( self.favorites[ position ] ) tmp.destroy() for i in range( position, len( self.favorites ) ): self.favorites[ i ].position = i self.favoritesBox.remove( self.favorites[ i ] ) self.favoritesPositionOnGrid( self.favorites[ i ] ) self.favoritesSave() self.favoritesBox.resize( self.favoritesGetNumRows(), self.favCols ) self.updateHistoryBox() def onAddToFavorites(self, menu, widget): self.favoritesAdd( self.favoritesBuildLauncher( widget.desktopFile ) ) self.updateHistoryBox() def onRemoveFromFavorites(self, menu, widget): self.favoritesRemoveLocation(widget.desktopFile) self.updateHistoryBox() def favoritesRemoveLocation(self, location): for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: self.favoritesRemove(fav.position) self.updateHistoryBox() def favoritesAdd(self, favButton, position = -1): if favButton: favButton.set_name("ButtonApp") favButton.position = len(self.favorites) self.favorites.append( favButton ) for fav in self.favorites: self.favoritesBox.remove(fav) self.favoritesPositionOnGrid(fav) favButton.connect("drag-data-received", self.onFavButtonDragReorder) favButton.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.fromFav, Gdk.DragAction.COPY) favButton.connect("drag-data-get", self.onFavButtonDragReorderGet) favButton.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.toFav, Gdk.DragAction.COPY) if position >= 0: self.favoritesReorder(favButton.position, position) self.favoritesSave() self.favoritesBox.resize(self.favoritesGetNumRows(), self.favCols) self.updateHistoryBox() def favoritesGetNumRows(self): rows = 0 col = 0 for fav in self.favorites: if (fav.type == "separator" or fav.type == "space") and col != 0: rows += 1 col = 0 col += 1 if fav.type == "separator" or fav.type == "space": rows += 1 col = 0 if col >= self.favCols: rows += 1 col = 0 return rows def onPropsApp(self, menu, widget): button = MenuApplicationLauncher(widget.desktopFile, 32, "", True, highlight=(False)) self.builder1 = Gtk.Builder() self.builder1.add_from_file( os.path.join( '/', 'usr', 'share', 'ukui-menu', 'plugins', 'property.glade' ) ) dialog = self.builder1.get_object("dialog1") dialog.set_name("myGtkLabel") self.entry_type = self.builder1.get_object("entry_type") self.entry_name = self.builder1.get_object("entry_name") self.entry_command = self.builder1.get_object("entry_command") self.entry_comment = self.builder1.get_object("entry_comment") label_type = self.builder1.get_object("label_type") label_type.set_text(_("Type:")) label_name = self.builder1.get_object("label_name") label_name.set_text(_("Name:")) label_command = self.builder1.get_object("label_command") label_command.set_text(_("Command:")) label_comment = self.builder1.get_object("label_comment") label_comment.set_text(_("Note:")) self.image_app = self.builder1.get_object("image2") icon = button.appIconName self.image_app.set_from_icon_name(icon, Gtk.IconSize.DND) self.image_app.set_pixel_size(60) self.button_close = self.builder1.get_object("button_close") self.button_close.connect("clicked", self.close_dia, dialog) if button.appType == "Application": self.entry_type.set_text(_("Application")) if button.appType == "Application in Terminal": self.entry_type.set_text(_("Application in Terminal")) self.entry_name.set_text(button.appName) self.entry_command.set_text(button.appExec) self.entry_comment.set_text(button.appComment) if button.appName == "Eclipse": self.entry_comment.set_text("Eclipse集成开发环境") if button.appName == "Qt Creator": self.entry_comment.set_text("Qt Creator集成开发环境") dialog.set_title(button.appName + _("Property")) dialog.show() self.ukuiMenuWin.hide() Gdk.flush() def close_dia(self, widget, dialog): dialog.hide() def delete_from_menu(self, menu, widget): recentApp = {} try: delappname = widget.appName.lower() if os.path.exists(GLib.get_home_dir() + "/.recentAppLog"): f = open(GLib.get_home_dir() + "/.recentAppLog", 'r') lines = f.readlines() for line in lines: word = line.strip().split(':') if word[0] == delappname: continue recentApp[word[0]] = int(word[len(word) - 1]) f.close() f = open(GLib.get_home_dir() + "/.recentAppLog", 'w') else: f = open(GLib.get_home_dir() + "/.recentAppLog", 'w') srApp = sorted(iter(recentApp.items()), key = operator.itemgetter(1), reverse=True) for i in range(len(srApp)): f.write(srApp[i][0] + ":" + str(srApp[i][1]) + "\n") f.close() self.updateHistoryBox() except Exception as detail: print (detail) def changeTab( self, tabNum, clear = True ): notebook = self.builder.get_object( "notebook_left" ) if tabNum == 0: self.favappbutton.grab_focus() self.button_user.grab_focus() if self.ButtonHoverWidgetHistory: self.ButtonHoverWidgetHistory.set_name("ButtonApp") self.viewportfav.set_name("ViewportButton") self.viewportallapp.set_name("ViewportNull") self.viewport_entry.set_name("Viewport") self.searchEntry.set_name("Entry") self.favappbutton.set_sensitive(False) self.allappbutton.set_sensitive(True) self.content_holder.set_size_request(340, self.windowHeight) self.layout_applications_right.hide() for widget1 in self.categorybutton_list: widget1.aState = False widget1.rightbox.hide() notebook.set_current_page( 0 ) elif tabNum == 1: self.viewportfav.set_name("ViewportNull") self.viewportallapp.set_name("ViewportButton") self.viewport_entry.set_name("ViewportEntry") self.searchEntry.set_name("EntryActive") self.favappbutton.set_sensitive(True) self.allappbutton.set_sensitive(False) self.searchEntry.grab_focus() notebook.set_current_page( 1 ) elif tabNum == 2: self.content_holder.set_size_request(340, self.windowHeight) #self.updateHeight() self.layout_applications_right.hide() for widget1 in self.categorybutton_list: widget1.aState = False widget1.rightbox.hide() notebook.set_current_page( 2 ) self.lastActiveTab = tabNum def Search( self, widget ): text = self.searchEntry.get_text().strip() if text != "": self.shownList[0].grab_focus() def onUninstallApp( self, menu, widget ): widget.uninstall() self.ukuiMenuWin.hide() def onRemoveFromStartup( self, menu, widget ): widget.removeFromStartup() def ReceiveCallback( self, widget, context, x, y, selection, targetType, time ): if targetType == self.TARGET_TYPE_TEXT: for uri in selection.get_uris(): self.favoritesAdd( self.favoritesBuildLauncher( uri ) ) def showGoNext( self, widget ): widget.rightbox.show_all() def hideGoNext( self, widget ): if not widget.aState: widget.rightbox.hide() def onRecentFileRemove(self, menu, Data): uri, AppName, appname = Data hasapp = False if AppName == "Kylin Video": fileString = "" cf = UkuiConfigParser() path = os.path.join(GLib.get_user_config_dir(), 'kylin-video/kylin-video.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("history", "recents") if recentFiles.endswith(","): recentFiles = recentFiles[:-1] recentFiles = recentFiles.split(", ") for recfile in recentFiles: if recfile == uri: continue fileString = fileString + recfile + ", " fileString = fileString[0:len(fileString)-2] cf.set("history", "recents", fileString) if fileString == "": hasapp = False else: hasapp = True fd = open(path, 'w') cf.write(fd) fd.close() elif AppName == "Qt Creator": uri = uri.encode("unicode_escape").decode('utf-8') uri = uri.replace('\\u', '\\x') fileString = "" cf = UkuiConfigParser() path = os.path.join(GLib.get_user_config_dir(), 'QtProject/QtCreator.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("RecentFiles", "Files") if recentFiles == "@Invalid()": hasapp = False return if recentFiles.endswith(","): recentFiles = recentFiles[:-1] recentFiles = recentFiles.split(", ") for recfile in recentFiles: if recfile == uri: continue fileString = fileString + recfile + ", " fileString = fileString[0:len(fileString)-2] cf.set("RecentFiles", "Files", fileString) if fileString == "": hasapp = False else: hasapp = True fd = open(path, 'w') cf.write(fd) fd.close() else: RecManagerInstance.remove_item(uri) RecentInfo = RecManagerInstance.get_items() for item in RecentInfo: if AppName == "WPS Writer": if item.get_mime_type() in WPS_W: hasapp = True break elif AppName == "WPS Presentation": if item.get_mime_type() in WPS_P: hasapp = True break elif AppName == "WPS Spreadsheets": if item.get_mime_type() in WPS_S: hasapp = True break elif item.has_application(AppName): hasapp = True break if hasapp: self.startsToShow(appname) else: self.expanderRecent.hide() self.layout_applications_right.hide() self.content_holder.set_size_request(340, self.windowHeight) def openDir(self, menu, FileData): Data, FileName = FileData dirname = os.path.dirname(FileName) if os.path.exists(FileName): pass else: msg = _("\nFile not exists, it was moved or deleted!\n\nIt will be automatically removed from the history list!\n") md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() self.onRecentFileRemove(menu, Data) return os.system("peony %s" % dirname) def copyFile(self, menu, FileData): Data, FileName = FileData if os.path.exists(FileName): pass else: msg = _("\nFile not exists, it was moved or deleted!\n\nIt will be automatically removed from the history list!\n") md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() self.onRecentFileRemove(menu, Data) return os.system("cp %s %s/" % (FileName, GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP))) def propertyFile(self, menu, FileData): Data, FileName = FileData if os.path.exists(FileName): pass else: msg = _("\nFile not exists, it was moved or deleted!\n\nIt will be automatically removed from the history list!\n") md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() self.onRecentFileRemove(menu, Data) return os.system("peony -a %s &" % FileName) def recentFilePopup( self, widget, ev, FileData ): Data, FileName = FileData uri, AppName, appname = Data if ev.button == 3: mTree = Gtk.Menu() mTree.set_name("myGtkLabel") mTree.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK) openRecentFile = Gtk.MenuItem(_("Open(_O)")) openRecentFile.set_use_underline(True) openDirRecentFile = Gtk.MenuItem(_("Open directory(_D)")) openDirRecentFile.set_use_underline(True) newDoc = Gtk.MenuItem(_("New(_N)")) newDoc.set_use_underline(True) copyRecentFile = Gtk.MenuItem(_("Copy to desktop(_C)")) copyRecentFile.set_use_underline(True) removeRecentFile = Gtk.MenuItem(_("Remove from list(_R)")) removeRecentFile.set_use_underline(True) propertyRecentFile = Gtk.MenuItem(_("Property(_P)")) propertyRecentFile.set_use_underline(True) openRecentFile.connect( "activate", self.callback, FileData ) openDirRecentFile.connect( "activate", self.openDir, FileData ) copyRecentFile.connect( "activate", self.copyFile, FileData ) removeRecentFile.connect( "activate", self.onRecentFileRemove, Data ) propertyRecentFile.connect( "activate", self.propertyFile, FileData ) mTree.append(openRecentFile) mTree.append(openDirRecentFile) if appname == _("WPS Writer") or appname == _("WPS Presentation") or appname == _("WPS Spreadsheets") or appname == _("Pluma"): mTree.append(newDoc) mTree.append(copyRecentFile) mTree.append(removeRecentFile) mTree.append(propertyRecentFile) mTree.show_all() widget.set_name("ButtonAppHover") mTree.connect("deactivate", lambda w: widget.set_name("ButtonApp")) self.ukuiMenuWin.stopHiding() mTree.attach_to_widget(widget) mTree.popup(None, None, None, None, ev.button, ev.time) def AddRecentBtn( self, Name, RecentImage, Data ): DispName=os.path.basename( Name ) uri, AppName, appname = Data FileData = (Data, Name) RecFButton = Gtk.Button( "", "ok", True ) RecFButton.remove( RecFButton.get_children()[0] ) RecFButton.set_size_request( 200, -1 ) RecFButton.set_relief( Gtk.ReliefStyle.NONE ) RecFButton.connect( "clicked", self.callback, FileData ) RecFButton.connect( "button-press-event", self.recentFilePopup, FileData ) RecFButton.set_name("ButtonApp") RecFButton.show() Box1 = Gtk.Box( orientation=Gtk.Orientation.HORIZONTAL, spacing=5 ) if AppName == "Kylin Video" or AppName == "Qt Creator": if RecentImage: RecentImage.set_size_request( 20, -1 ) Box1.add( RecentImage ) else: ButtonIcon = Gtk.Image() ButtonIcon.set_size_request( 20, -1 ) ButtonIcon.set_from_pixbuf(RecentImage) Box1.add( ButtonIcon ) Label1 = Gtk.Label( DispName ) Label1.set_ellipsize( Pango.EllipsizeMode.END ) Box1.add( Label1 ) RecFButton.add( Box1 ) RecFButton.set_tooltip_text(Name) RecFButton.show_all() self.recentBox.pack_start( RecFButton, False, True, 0 ) def getRecentImage(self, filename): if not os.path.exists(filename): return None gfile = Gio.File.new_for_path(filename) gicon = gfile.query_info("standard::icon", Gio.FileQueryInfoFlags.NONE, None).get_icon() names = Gio.ThemedIcon.get_names(gicon) recentImage = Gtk.Image.new_from_icon_name(names[1], Gtk.IconSize.MENU) return recentImage def startsToShow( self, appname ): self.canShow = True FileString=[] IconString=[] UriString=[] AppName = None if self.ButtonHoverWidgetHistory: self.ButtonHoverWidgetHistory.set_name("ButtonApp") RecentInfo = RecManagerInstance.get_items() cf = UkuiConfigParser() for child in self.applicationsBox.get_children(): if child == self.expanderRecent: self.applicationsBox.remove(self.expanderRecent) child.hide() #qt app if appname == _("SMPlayer"): pass; if appname == _("Kylin Video"): path = os.path.join(GLib.get_user_config_dir(), 'kylin-video/kylin-video.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("history", "recents") recentFiles = recentFiles.split(", ") for recfile in recentFiles: if recfile.endswith(","): recfile = recfile[:-1] FileString.append(recfile) IconString.append(self.getRecentImage(recfile)) UriString.append(recfile) AppName = "Kylin Video" if appname == _("Qt Creator"): path = os.path.join(GLib.get_user_config_dir(), 'QtProject/QtCreator.ini') if os.path.exists(path): cf.read(path) recentFiles = cf.get("RecentFiles", "Files") if recentFiles == "@Invalid()": return recentFiles = recentFiles.replace('\\x', '\\u') recentFiles = recentFiles.encode("utf-8").decode("unicode_escape") recentFiles = recentFiles.split(", ") for recfile in recentFiles: FileString.append(recfile) IconString.append(self.getRecentImage(recfile)) UriString.append(recfile) AppName = "Qt Creator" #wps if appname == _("WPS Writer"): for item in RecentInfo: if item.get_mime_type() in WPS_W: FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "WPS Writer" if appname == _("WPS Presentation"): for item in RecentInfo: if item.get_mime_type() in WPS_P: FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "WPS Presentation" if appname == _("WPS Spreadsheets"): for item in RecentInfo: if item.get_mime_type() in WPS_S: FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "WPS Spreadsheets" #gtk app if appname == _("Eye of MATE Image Viewer"): for item in RecentInfo: if item.has_application("Eye of MATE Image Viewer"): FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "Eye of MATE Image Viewer" if appname == _("Atril Document Viewer"): for item in RecentInfo: if item.has_application("Atril Document Viewer"): FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "Atril Document Viewer" if appname == _("Pluma"): for item in RecentInfo: if item.has_application("Pluma"): FileString.append(item.get_uri_display()) IconString.append(item.get_icon(Gtk.IconSize.MENU)) UriString.append(item.get_uri()) AppName = "Pluma" for child in self.recentBox.get_children(): self.recentBox.remove( child ) loc = 0 if not FileString: self.expanderRecent.hide() self.layout_applications_right.hide() self.content_holder.set_size_request(340, self.windowHeight) self.showHis = False return if len(FileString[0]) < 2: self.expanderRecent.hide() self.layout_applications_right.hide() self.content_holder.set_size_request(340, self.windowHeight) self.showHis = False return recent_file_num = ukuimenu_settings.get_int("recent-file-num") for Name in FileString: if loc > recent_file_num - 1: break if Name != None: self.AddRecentBtn( Name, IconString[loc], (UriString[loc], AppName, appname) ) loc = loc + 1 self.ButtonHoverWidget.set_name("ButtonAppHover") self.ButtonHoverWidgetHistory = self.ButtonHoverWidget self.expanderRecent.show() self.applicationsBox.pack_start( self.expanderRecent, False, False, 0) self.layout_applications_right.show() self.content_holder.set_size_request(575, self.windowHeight) self.labelExpander.set_text(_("Recent(%s)") % str(loc)) self.recentShow = None def showHistory( self, widget ): self.showHis = False self.recentHide = None self.recentShow = None self.ButtonHoverWidget = widget for item in APPLIST: if widget.appName == item: self.showHis = True if not self.showHis: self.recentHide = GLib.timeout_add( 500, self.startsToHide ) return widget.set_state(Gtk.StateType.PRELIGHT) self.recentShow = GLib.timeout_add( 500, self.startsToShow, widget.appName ) def startsToHide( self ): if self.ButtonHoverWidgetHistory: self.ButtonHoverWidgetHistory.set_name("ButtonApp") if self.showHis: return self.expanderRecent.hide() self.layout_applications_right.hide() self.content_holder.set_size_request(340, self.windowHeight) self.recentHide = None def hideHistory( self, widget ): if self.showHis: if self.recentShow: GLib.source_remove( self.recentShow ) if not self.showHis: if self.recentHide: GLib.source_remove( self.recentHide ) def scrollItemIntoView( self, widget, event = None ): viewport = widget.get_parent() while not isinstance( viewport, Gtk.Viewport ): if not viewport.get_parent(): return viewport = viewport.get_parent() aloc = widget.get_allocation() viewport.get_vadjustment().clamp_page(aloc.y, aloc.y + aloc.height) def searchIconPressed(self, entry, icon_pos, event): if self.searchEntry.get_text() != "" and self.searchEntry.get_text() != _("Search local program"): self.searchEntry.set_text("") def searchChanged(self, widget): if self.searchEntry.get_text() != "": iconpath = ICON_PATH + "so-close.png" pixbuf = Pixbuf.new_from_file(iconpath) pixbuf = pixbuf.scale_simple(16, 16, 2) #2 := BILINEAR self.searchEntry.set_icon_from_pixbuf(1, pixbuf) else: iconpath = ICON_PATH + "so.png" pixbuf = Pixbuf.new_from_file(iconpath) pixbuf = pixbuf.scale_simple(16, 16, 2) #2 := BILINEAR self.searchEntry.set_icon_from_pixbuf(1, pixbuf) def viewOpened(self, matcher, view, user_data): recentApp = {} tempName = "" found = False try: viewName = Bamf.View.get_name(view) if viewName == "QQ International" or viewName == "TXMenuWindow": appName = "qq国际版" found = True if viewName == "Mozilla Firefox": appName = "firefox 网络浏览器" found = True if not found: obj = dbus.SessionBus().get_object('org.ayatana.bamf', '/org/ayatana/bamf/matcher') interface = dbus.Interface(obj, 'org.ayatana.bamf.matcher') application = interface.ActiveApplication() obj = dbus.SessionBus().get_object('org.ayatana.bamf', application) interface = dbus.Interface(obj, 'org.ayatana.bamf.application') desktopname = interface.DesktopFile() tempPath = os.path.basename(desktopname) desktopPath = get_desktop_file_path(tempPath) appInfo = Gio.DesktopAppInfo.new_from_filename(desktopname) tempName = Gio.DesktopAppInfo.get_display_name(appInfo) except Exception as e: # print (e) return if tempName.endswith('.py'): return # special applications if "Chromium" in tempName: appName = _("Chromium Browser") found = True elif "Google Chrome" in tempName: appName = "google chrome" found = True elif tempName.lower() == "fcitx config tool": appName = _("Fcitx Config") found = True if not found: for appname in self.appHistoryList: if desktopPath: button = MenuApplicationLauncher(desktopPath, 32, "", True, highlight=(False)) button.set_name("ButtonApp") if button.appName.lower() == appname["entry"].get_name().decode('utf-8').lower(): appName = appname["entry"].get_name().decode('utf-8').lower() found = True break if desktopPath == appname["entry"].get_desktop_file_path().decode('utf-8'): appName = appname["entry"].get_name().decode('utf-8').lower() found = True break if tempName.lower() == appname["entry"].get_name().decode('utf-8').lower(): appName = appname["entry"].get_name().decode('utf-8').lower() found = True break if viewName: if viewName.lower() == appname["entry"].get_name().decode('utf-8').lower(): appName = appname["entry"].get_name().decode('utf-8').lower() found = True break if not found: return #read and write recentAppLog file if os.path.exists(GLib.get_home_dir() + "/.recentAppLog"): f = open(GLib.get_home_dir() + "/.recentAppLog", 'r') lines = f.readlines() for line in lines: word = line.strip().split(':') recentApp[word[0]] = int(word[len(word) - 1]) f.close() f = open(GLib.get_home_dir() + "/.recentAppLog", 'w') else: f = open(GLib.get_home_dir() + "/.recentAppLog", 'w') recentApp[appName] = recentApp.get(appName,0) + 1 srApp = sorted(iter(recentApp.items()), key = operator.itemgetter(1), reverse=True) for i in range(len(srApp)): f.write(srApp[i][0] + ":" + str(srApp[i][1]) + "\n") f.close() self.updateHistoryBox() def updateHistoryBox( self, settings = None, key = None, args = None ): for child in self.recentAppBox: child.destroy() historyDisplayNum = ukuimenu_settings.get_int("recent-app-num") self.currentHisCount = 1 try: applicationsFile = open(self.favoritesPath, "r") applicationsList = applicationsFile.readlines() applicationsFile.close() self.favAppList = applicationsList f = open(GLib.get_home_dir() + "/.recentAppLog", 'r') lines = f.readlines() for line in lines: word = line.strip().split(':') found = False for appname in self.appHistoryList: findDesktop = False for tempdesktop in self.favAppList: temp = tempdesktop.split(":")[1] if appname["entry"].get_desktop_file_path().decode('utf-8') in temp or os.path.basename(appname["entry"].get_desktop_file_path().decode('utf-8')) in temp or os.path.basename(temp) in appname["entry"].get_desktop_file_path().decode('utf-8'): findDesktop = True if word[0].lower() == appname["entry"].get_name().decode('utf-8').lower() and findDesktop == False: button = MenuApplicationLauncher( appname["entry"].get_desktop_file_path().decode('utf-8'), 32, "", True, highlight=(False) ) if button.appExec: #button.set_tooltip_text( button.getTooltip() ) button.connect( "clicked", lambda w: self.ukuiMenuWin.hide() ) button.isRecentApp = True button.connect( "button-press-event", self.menuPopup ) if self.showRecentFile: self.ShowRecentFile(button) button.connect( "enter", self.showHistory ) button.connect( "leave", self.hideHistory ) found = True break if found and (self.currentHisCount <= historyDisplayNum): self.recentAppBox.pack_start( button, False, True, 0 ) button.set_name("ButtonApp") button.show() Gdk.flush() if self.currentHisCount == historyDisplayNum: break else: self.currentHisCount += 1 f.close() except Exception as e: print (e) Gdk.flush() def keyPress( self, widget, event ): if event.string.strip() != "" or event.keyval == Gdk.KEY_BackSpace: self.searchEntry.grab_focus() self.searchEntry.set_position(-1) self.searchEntry.event( event ) return True if event.keyval == Gdk.KEY_Down and self.searchEntry.is_focus(): notebook = self.builder.get_object( "notebook_left" ) page = notebook.get_current_page() if page == 0: self.favoritesBox.get_children()[0].grab_focus() #Todo: focus the favoritesBox but not the recentAppBox now. elif page == 1: self.categoriesBox.get_children()[0].grab_focus() else: self.searchResultBox.get_children()[0].grab_focus() return False ukui-menu/ukui_menu/filemonitor.py0000664000175000017500000001131613237775114016335 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os import os.path import threading import time from gi.repository import GLib try: import pyinotify hasInotify = True except ImportError: hasInotify = False if hasInotify: class FileMonitor(object): def __init__( self ): self.monitorId = 0 self.wm = pyinotify.WatchManager() self.wdds = {} self.callbacks = {} self.notifier = pyinotify.ThreadedNotifier(self.wm, self.fileChanged) self.notifier.setDaemon( True ) self.notifier.start() def addMonitor( self, filename, callback, args = None ): try: mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | pyinotify.IN_MODIFY mId = self.wm.add_watch( filename, mask, rec = True)[filename] if mId >= 0: self.callbacks[mId] = ( callback, args ) except Exception as detail: mId = 0 return mId def removeMonitor( self, monitorId ): if monitorId in self.callbacks: self.wm.rm_watch( monitorId ) del self.callbacks[monitorId] def fileChanged(self, event ): if event.wd in self.callbacks: callback = self.callbacks[event.wd] if callback[1]: GLib.idle_add( callback[0], callback[1] ) else: GLib.idle_add( callback[0] ) else: class _MonitoredFile( object ): def __init__( self, filename, callback, monitorId, args ): self.filename = filename self.callback = callback self.monitorId = monitorId self.args = args self.exists = os.path.exists( self.filename ) if self.exists: self.mtime = os.stat( filename ).st_mtime else: self.mtime = 0 def hasChanged( self ): if os.path.exists( self.filename ): if not self.exists: self.exists = True self.mtime = os.stat( self.filename ).st_mtime return True else: mtime = os.stat( self.filename ).st_mtime if mtime != self.mtime: self.mtime = mtime return True else: if self.exists: self.exists = False return True return False class MonitorThread(threading.Thread): def __init__(self, monitor): threading.Thread.__init__ ( self ) self.monitor = monitor def run(self): while(1): self.monitor.checkFiles() time.sleep(1) class FileMonitor(object): def __init__( self ): self.monitorId = 0 self.monitoredFiles = [] self.monitorThread = MonitorThread( self ) self.monitorThread.setDaemon( True ) self.monitorThread.start() def addMonitor( self, filename, callback, args = None ): self.monitorId += 1 self.monitoredFiles.append( _MonitoredFile( filename, callback, self.monitorId, args ) ) return self.monitorId def removeMonitor( self, monitorId ): for monitored in self.monitoredFiles: if monitorId == monitored.monitorId: self.monitoredFiles.remove( monitored ) break def checkFiles( self ): for monitored in self.monitoredFiles: if monitored.hasChanged(): if monitored.args: GLib.idle_add( monitored.callback, monitored.args ) else: GLib.idle_add( monitored.callback ) monitor = FileMonitor() ukui-menu/ukui_menu/easygsettings.py0000644000175000017500000000750413110762006016663 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. from gi.repository import Gio class EasyGSettings: def __init__( self, schema = None ): self.schema = schema self.settings = Gio.Settings.new(self.schema) self.handlerIds = [ ] def get( self, type, key ): if type == "bool": return self.settings.get_boolean( key ) if type == "string": return self.settings.get_string( key ) if type == "int": return self.settings.get_int( key ) if type == "color": color = self.settings.get_string( key ) if not self.evalColor( color ): self.settings.set_string(key, "#ffffff") return "#ffffff" return color t = type.split("-") if len(t) == 2 and t[0] == "list": return self.settings.get_strv( key ) return self.settings.get( key ) def set( self, type, key, value ): if type == "bool": return self.settings.set_boolean( key, value ) if type == "string": return self.settings.set_string( key, value ) if type == "int": return self.settings.set_int( key, value ) if type == "color": if self.evalColor( value ): return self.settings.set_string( key, value ) else: return self.settings.set_string( key, "#ffffff" ) t = type.split("-") if len(t) == 2 and t[0] == "list": return self.settings.set_strv( key, value ) return self.settings.set( key, value ) def notifyAdd( self, key, callback, args = None ): handlerId = self.settings.connect("changed::"+key, callback, args) self.handlerIds.append( handlerId ) return handlerId def notifyRemove( self, handlerId ): return self.settings.disconnect(handlerId) def notifyRemoveAll( self ): for handlerId in self.handlerIds: self.settings.disconnect( handlerId ) def evalColor(self, colorToTest ): if colorToTest[0] != '#' or len( colorToTest ) != 7: return False for i in colorToTest[1:]: if i not in ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: return False return True def bindGSettingsEntryToVar( self, type, key, obj, varName ): return self.notifyAdd( key, self.setVar, ( type, obj, varName ) ) def setVar( self, settings, key, args ): type, obj, varName = args if type == "string": setattr( obj, varName, settings.get_string(key) ) elif type == "int": setattr( obj, varName, settings.get_int(key) ) elif type == "float": setattr( obj, varName, settings.get_float(key) ) elif type == "bool": setattr( obj, varName, settings.get_boolean(key) ) else: setattr( obj, varName, settings.get_value(key) ) ukui-menu/ukui_menu/execute.py0000664000175000017500000000400213237775114015442 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os def RemoveArgs(Execline): NewExecline = [] Specials=["\"%c\"", "%f","%F","%u","%U","%d","%D","%n","%N","%i","%c","%k","%v","%m","%M", "-caption", "/bin/sh", "sh", "-c", "STARTED_FROM_MENU=yes"] for elem in Execline: elem = elem.replace("'","") elem = elem.replace("\"", "") if elem not in Specials: print (elem) NewExecline.append(elem) return NewExecline # Actually execute the command def Execute( cmd , commandCwd=None): if not commandCwd: cwd = os.path.expanduser( "~" ); else: tmpCwd = os.path.expanduser( commandCwd ); if (os.path.exists(tmpCwd)): cwd = tmpCwd if isinstance( cmd, str ) or isinstance( cmd, str): if (cmd.find("/home/") >= 0) or (cmd.find("xdg-su") >= 0) or (cmd.find("\"") >= 0): print ("running manually...") try: os.chdir(cwd) os.system(cmd + " &") return True except Exception as detail: print (detail) return False cmd = cmd.split() cmd = RemoveArgs(cmd) try: os.chdir( cwd ) string = ' '.join(cmd) string = string + " &" os.system(string) return True except Exception as detail: print (detail) return False ukui-menu/ukui_menu/keybinding.py0000664000175000017500000002663213254644467016145 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2013 Ozcan Esen # Copyright (C) 2008 Luca Bruno # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This a slightly modified version of the globalkeybinding.py file which is part of FreeSpeak. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. import gi import os import threading gi.require_version("Gtk", "3.0") gi.require_version('Wnck', '3.0') from gi.repository import Gtk, Gdk, GdkX11, GObject, GLib, Wnck from Xlib.display import Display from Xlib import X, error SPECIAL_MODS = (["Super_L", ""], ["Super_R", ""], ["Alt_L", ""], ["Alt_R", ""], ["Control_L", ""], ["Control_R", ""], ["Shift_L", ""], ["Shift_R", ""]) class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): try: GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.keymap = Gdk.Keymap().get_default() self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.showscreen = Wnck.Screen.get_default() self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" except Exception as cause: print (("init keybinding error: \n", str(cause))) self.display = None return None def is_hotkey(self, key, modifier): keymatch = False modmatch = False modifier = modifier & ~Gdk.ModifierType.SUPER_MASK modint = int(modifier) if self.get_keycode(key) == self.keycode or self.get_keycode(key) == 134: keymatch = True for ignored_mask in self.ignored_masks: if self.modifiers | ignored_mask == modint | ignored_mask: modmatch = True break return keymatch and modmatch def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def get_keycode(self, keyval): return self.keymap.get_entries_for_keyval(keyval).keys[0].keycode def grab(self, key): if self.display == None: return False accelerator = key accelerator = accelerator.replace("", "") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key self.keycode = self.get_keycode(keyval) self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) result = self.window.grab_key(134, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.display == None: return if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) self.window.ungrab_key(134, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window = None): if self.display == None: return self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object("window", window.get_xid()) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in range(mask+1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): if self.display == None: return self.running = True wait_for_release = False showdesktop = True while self.running: event = self.display.next_event() try: self.current_event_time = event.time if ( event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release ) or ( event.detail == 134 and event.type == X.KeyPress and not wait_for_release ): modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.SyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif ( event.detail == self.keycode and wait_for_release ) or ( event.detail == 134 and wait_for_release ): if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 40 and event.type == X.KeyPress: #super+d self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 40 and event.type == X.KeyRelease: #super+d if showdesktop: self.showscreen.toggle_showing_desktop(True) showdesktop = False else: self.showscreen.toggle_showing_desktop(False) showdesktop = True self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == 33 and event.type == X.KeyPress: #super+p self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 33 and event.type == X.KeyRelease: #super+p self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == 26 and event.type == X.KeyPress: #super+e self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 26 and event.type == X.KeyRelease: #super+e os.system("peony &") self.display.allow_events(X.ReplayKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close() class KeybindingWidget(Gtk.Box): __gsignals__ = { 'accel-edited': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self, desc): super(KeybindingWidget, self).__init__() self.desc = desc self.label = Gtk.Label(desc) if self.desc != "": self.pack_start(self.label, False, False, 0) self.button = Gtk.Button() self.button.set_tooltip_text(_("Click to set a new accelerator key for opening and closing the menu. ") + _("Press Escape or click again to cancel the operation. ") + _("Press Backspace to clear the existing keybinding.")) self.button.connect("clicked", self.clicked) self.button.set_size_request(200, -1) self.pack_start(self.button, False, False, 4) self.show_all() self.event_id = None self.teaching = False def clicked(self, widget): if not self.teaching: Gdk.keyboard_grab(self.get_window(), False, Gdk.CURRENT_TIME) self.button.set_label(_("Pick an accelerator")) self.event_id = self.connect( "key-release-event", self.on_key_release ) self.teaching = True else: if self.event_id: self.disconnect(self.event_id) self.ungrab() self.set_button_text() self.teaching = False def on_key_release(self, widget, event): self.disconnect(self.event_id) self.ungrab() self.event_id = None if event.keyval == Gdk.KEY_Escape: self.set_button_text() self.teaching = False return True if event.keyval == Gdk.KEY_BackSpace: self.teaching = False self.value = "" self.set_button_text() self.emit("accel-edited") return True accel_string = Gtk.accelerator_name( event.keyval, event.state ) accel_string = self.sanitize(accel_string) self.value = accel_string self.set_button_text() self.teaching = False self.emit("accel-edited") return True def sanitize(self, string): accel_string = string.replace("", "") accel_string = accel_string.replace("", "") for single, mod in SPECIAL_MODS: if single in accel_string and mod in accel_string: accel_string = accel_string.replace(mod, "") return accel_string def get_val(self): return self.value def set_val(self, value): self.value = value self.set_button_text() def ungrab(self): Gdk.keyboard_ungrab(Gdk.CURRENT_TIME) def set_button_text(self): if self.value == "": self.button.set_label(_("")) else: self.button.set_label(self.value) ukui-menu/ukui_menu/menueditor.py0000664000175000017500000015700213265322640016155 0ustar fengfeng#!/usr/bin/env python3 # Copyright (C) 2017,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import ukuimenu import gi gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') from gi.repository import GLib, Gio from gi.repository import Gtk, Gdk, GdkPixbuf import os import gettext import locale import codecs import cgi import shutil import subprocess import xml.dom.minidom import xml.parsers.expat #i18n gettext.install("ukui-menu", "/usr/share/locale") DESKTOP_GROUP = GLib.KEY_FILE_DESKTOP_GROUP KEY_FILE_FLAGS = GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS def fillKeyFile(keyfile, items): for key, item in items.items(): if item is None: continue if isinstance(item, bool): keyfile.set_boolean(DESKTOP_GROUP, key, item) elif isinstance(item, Sequence): keyfile.set_string_list(DESKTOP_GROUP, key, item) elif isinstance(item, basestring): keyfile.set_string(DESKTOP_GROUP, key, item) def getUniqueFileId(name, extension): append = 0 while 1: if append == 0: filename = name + extension else: filename = name + '-' + str(append) + extension if extension == '.desktop': path = getUserItemPath() if not os.path.isfile(os.path.join(path, filename)) and not getItemPath(filename): break elif extension == '.directory': path = getUserDirectoryPath() if not os.path.isfile(os.path.join(path, filename)) and not getDirectoryPath(filename): break append += 1 return filename def getUniqueRedoFile(filepath): append = 0 while 1: new_filepath = filepath + '.redo-' + str(append) if not os.path.isfile(new_filepath): break else: append += 1 return new_filepath def getUniqueUndoFile(filepath): filename = os.path.split(filepath)[1] #filename, extension = os.path.split(filepath)[1].split('.') append = 0 while 1: if filename.endswith('desktop'): path = getUserItemPath() elif filename.endswith('directory'): path = getUserDirectoryPath() elif filename.endswith('menu'): path = getUserMenuPath() new_filepath = os.path.join(path, filename +'.undo-' + str(append)) if not os.path.isfile(new_filepath): break else: append += 1 return new_filepath def getItemPath(file_id): for path in GLib.get_system_data_dirs(): file_path = os.path.join(path, 'applications', file_id) if os.path.isfile(file_path): return file_path return None def getUserItemPath(): item_dir = os.path.join(GLib.get_user_data_dir(), 'applications') if not os.path.isdir(item_dir): os.makedirs(item_dir) return item_dir def getDirectoryPath(file_id): for path in GLib.get_system_data_dirs(): file_path = os.path.join(path, 'desktop-directories', file_id) if os.path.isfile(file_path): return file_path return None def getUserMenuPath(): menu_dir = os.path.join(GLib.get_user_config_dir(), 'menus') if not os.path.isdir(menu_dir): os.makedirs(menu_dir) return menu_dir def getUserDirectoryPath(): menu_dir = os.path.join(GLib.get_user_data_dir(), 'desktop-directories') if not os.path.isdir(menu_dir): os.makedirs(menu_dir) return menu_dir def getSystemMenuPath(file_id): for path in GLib.get_system_config_dirs(): file_path = os.path.join(path, 'menus', file_id) if os.path.isfile(file_path): return file_path return None def getUserMenuXml(tree): system_file = getSystemMenuPath(tree.get_menu_file().decode('utf-8')) name = tree.get_root_directory().get_menu_id().decode('utf-8') menu_xml = "\n" menu_xml += "\n " + name + "\n " menu_xml += "" + system_file + "\n\n" return menu_xml def getIcon(item): pixbuf, path = None, None if item is None: return None if isinstance(item, str): iconName = item else: if item.get_icon() == None: return None iconName = item.get_icon().decode('utf-8') if iconName and not '/' in iconName and iconName[-3:] in ('png', 'svg', 'xpm'): iconName = iconName[:-4] icon_theme = Gtk.IconTheme.get_default() try: pixbuf = icon_theme.load_icon(iconName, 24, 0) path = icon_theme.lookup_icon(iconName, 24, 0).get_filename() except: if iconName and '/' in iconName: try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(iconName, 24, 24) path = iconName except: pass if pixbuf is None: if item.get_type() == ukuimenu.TYPE_DIRECTORY: iconName = 'ukui-fs-directory' elif item.get_type() == ukuimenu.TYPE_ENTRY: iconName = 'application-default-icon' try: pixbuf = icon_theme.load_icon(iconName, 24, 0) path = icon_theme.lookup_icon(iconName, 24, 0).get_filename() except: return None if pixbuf is None: return None if pixbuf.get_width() != 24 or pixbuf.get_height() != 24: pixbuf = pixbuf.scale_simple(24, 24, GdkPixbuf.InterpType.HYPER) return pixbuf def removeWhitespaceNodes(node): remove_list = [] for child in node.childNodes: if child.nodeType == xml.dom.minidom.Node.TEXT_NODE: child.data = child.data.strip() if not child.data.strip(): remove_list.append(child) elif child.hasChildNodes(): removeWhitespaceNodes(child) for node in remove_list: node.parentNode.removeChild(node) class Menu: tree = None visible_tree = None path = None dom = None class MenuEditor: def __init__(self): self.locale = locale.getdefaultlocale()[0] self.__loadMenus() self.__undo = [] self.__redo = [] def __loadMenus(self): self.applications = Menu() self.applications.tree = ukuimenu.lookup_tree('ukui-applications.menu', ukuimenu.FLAGS_SHOW_EMPTY|ukuimenu.FLAGS_INCLUDE_EXCLUDED|ukuimenu.FLAGS_INCLUDE_NODISPLAY|ukuimenu.FLAGS_SHOW_ALL_SEPARATORS) self.applications.visible_tree = ukuimenu.lookup_tree('ukui-applications.menu') self.applications.tree.set_sort_key(ukuimenu.SORT_DISPLAY_NAME) self.applications.visible_tree.set_sort_key(ukuimenu.SORT_DISPLAY_NAME) self.applications.path = os.path.join(getUserMenuPath(), self.applications.tree.get_menu_file().decode('utf-8')) try: self.applications.dom = xml.dom.minidom.parse(self.applications.path) except (IOError, xml.parsers.expat.ExpatError): self.applications.dom = xml.dom.minidom.parseString(getUserMenuXml(self.applications.tree)) removeWhitespaceNodes(self.applications.dom) self.settings = Menu() self.settings.tree = ukuimenu.lookup_tree('ukui-settings.menu', ukuimenu.FLAGS_SHOW_EMPTY|ukuimenu.FLAGS_INCLUDE_EXCLUDED|ukuimenu.FLAGS_INCLUDE_NODISPLAY|ukuimenu.FLAGS_SHOW_ALL_SEPARATORS) self.settings.visible_tree = ukuimenu.lookup_tree('ukui-settings.menu') self.settings.tree.set_sort_key(ukuimenu.SORT_DISPLAY_NAME) self.settings.visible_tree.set_sort_key(ukuimenu.SORT_DISPLAY_NAME) self.settings.path = os.path.join(getUserMenuPath(), self.settings.tree.get_menu_file().decode('utf-8')) try: self.settings.dom = xml.dom.minidom.parse(self.settings.path) except (IOError, xml.parsers.expat.ExpatError): self.settings.dom = xml.dom.minidom.parseString(getUserMenuXml(self.settings.tree)) removeWhitespaceNodes(self.settings.dom) self.save(True) def __isVisible(self, item): if item.get_type() == ukuimenu.TYPE_ENTRY: return not (item.get_is_excluded() or item.get_is_nodisplay()) menu = self.__getMenu(item) if menu == self.applications: root = self.applications.visible_tree.get_root_directory() elif menu == self.settings: root = self.settings.visible_tree.get_root_directory() if item.get_type() == ukuimenu.TYPE_DIRECTORY: if self.__findMenu(item.get_menu_id(), root) is None: return False return True def __addUndo(self, items): self.__undo.append([]) for item in items: if isinstance(item, Menu): file_path = item.path elif isinstance(item, tuple): if item[0] == 'Item': file_path = os.path.join(getUserItemPath(), item[1]) if not os.path.isfile(file_path): file_path = getItemPath(item[1]) elif item[0] == 'Menu': file_path = os.path.join(getUserDirectoryPath(), item[1]) if not os.path.isfile(file_path): file_path = getDirectoryPath(item[1]) else: continue elif item.get_type() == ukuimenu.TYPE_DIRECTORY: if item.get_desktop_file_path() is None: continue file_path = os.path.join(getUserDirectoryPath(), os.path.split(item.get_desktop_file_path().decode('utf-8'))[1]) if not os.path.isfile(file_path): file_path = item.get_desktop_file_path().decode('utf-8') elif item.get_type() == ukuimenu.TYPE_ENTRY: file_path = os.path.join(getUserItemPath(), item.get_desktop_file_id().decode('utf-8')) if not os.path.isfile(file_path): file_path = item.get_desktop_file_path().decode('utf-8') else: continue with codecs.open(file_path, 'r', 'utf-8') as f: data = f.read() undo_path = getUniqueUndoFile(file_path) with codecs.open(undo_path, 'w', 'utf-8') as f: f.write(data) self.__undo[-1].append(undo_path) def __getPath(self, menu): names = [] current = menu while current is not None: names.append(current.get_menu_id()) current = current.get_parent() # XXX - don't append root menu name, mozo doesn't # expect it. look into this more. names.pop(-1) return names[::-1] def __getXmlMenuPart(self, element, name): for node in self.__getXmlNodesByName('Menu', element): for child in self.__getXmlNodesByName('Name', node): if child.childNodes[0].nodeValue == name: return node return None def __getXmlMenu(self, path, element, dom): for name in path: found = self.__getXmlMenuPart(element, name) if found is not None: element = found else: element = self.__addXmlMenuElement(element, name, dom) return element def __addXmlMenuElement(self, element, name, dom): if isinstance(name, bytes): name = name.decode('utf-8') node = dom.createElement('Menu') self.__addXmlTextElement(node, 'Name', name, dom) return element.appendChild(node) def __addXmlTextElement(self, element, name, text, dom): if isinstance(name, bytes): name = name.decode('utf-8') if isinstance(text, bytes): text = text.decode('utf-8') for temp in element.childNodes: if temp.nodeName == name: if temp.childNodes[0].nodeValue == text: return node = dom.createElement(name) text = dom.createTextNode(text) node.appendChild(text) return element.appendChild(node) def __addXmlFilename(self, element, dom, filename, type = 'Include'): if isinstance(filename, bytes): filename = filename.decode('utf-8') # remove old filenames for node in self.__getXmlNodesByName(['Include', 'Exclude'], element): if node.childNodes[0].nodeName == 'Filename' and node.childNodes[0].childNodes[0].nodeValue == filename: element.removeChild(node) # add new filename node = dom.createElement(type) node.appendChild(self.__addXmlTextElement(node, 'Filename', filename, dom)) return element.appendChild(node) def __addDeleted(self, element, dom): node = dom.createElement('Deleted') return element.appendChild(node) def __makeKeyFile(self, file_path, kwargs): if 'KeyFile' in kwargs: return kwargs['KeyFile'] keyfile = GLib.KeyFile() if file_path is not None: keyfile.load_from_file(file_path, KEY_FILE_FLAGS) fillKeyFile(keyfile, kwargs) return keyfile def __writeItem(self, item, **kwargs): if item is not None: file_path = item.get_desktop_file_path().decode('utf-8') else: file_path = None keyfile = self.__makeKeyFile(file_path, kwargs) if item is not None: file_id = item.get_desktop_file_id().decode('utf-8') else: file_id = getUniqueFileId(keyfile.get_string(DESKTOP_GROUP, 'Name'), '.desktop') contents, length = keyfile.to_data() with open(os.path.join(getUserItemPath(), file_id), 'w') as f: f.write(contents) return file_id def __writeMenu(self, menu, **kwargs): if menu is not None: file_id = os.path.split(menu.get_desktop_file_path().decode('utf-8'))[1] file_path = menu.get_desktop_file_path().decode('utf-8') keyfile = GLib.KeyFile() keyfile.load_from_file(file_path, KEY_FILE_FLAGS) elif menu is None and 'Name' not in kwargs: raise Exception('New menus need a name') else: file_id = getUniqueFileId(kwargs['Name'], '.directory') keyfile = GLib.KeyFile() fillKeyFile(keyfile, kwargs) contents, length = keyfile.to_data() with open(os.path.join(getUserDirectoryPath(), file_id), 'w') as f: f.write(contents) return file_id def __getXmlNodesByName(self, name, element): for child in element.childNodes: if child.nodeType == xml.dom.Node.ELEMENT_NODE: if isinstance(name, str) and child.nodeName == name: yield child elif isinstance(name, list) or isinstance(name, tuple): if child.nodeName in name: yield child def __addXmlMove(self, element, old, new, dom): if not self.__undoMoves(element, old, new, dom): node = dom.createElement('Move') node.appendChild(self.__addXmlTextElement(node, 'Old', old, dom)) node.appendChild(self.__addXmlTextElement(node, 'New', new, dom)) #are parsed in reverse order, need to put at the beginning return element.insertBefore(node, element.firstChild) def __addXmlLayout(self, element, layout, dom): # remove old layout for node in self.__getXmlNodesByName('Layout', element): element.removeChild(node) # add new layout node = dom.createElement('Layout') for order in layout.order: if order[0] == 'Separator': child = dom.createElement('Separator') node.appendChild(child) elif order[0] == 'Filename': child = self.__addXmlTextElement(node, 'Filename', order[1], dom) elif order[0] == 'Menuname': child = self.__addXmlTextElement(node, 'Menuname', order[1], dom) elif order[0] == 'Merge': child = dom.createElement('Merge') child.setAttribute('type', order[1]) node.appendChild(child) return element.appendChild(node) def __addXmlDefaultLayout(self, element, dom): # remove old default layout for node in self.__getXmlNodesByName('DefaultLayout', element): element.removeChild(node) # add new layout node = dom.createElement('DefaultLayout') node.setAttribute('inline', 'false') return element.appendChild(node) def __createLayout(self, items): layout = Layout() layout.order = [] layout.order.append(['Merge', 'menus']) for item in items: if isinstance(item, tuple): if item[0] == 'Separator': layout.parseSeparator() elif item[0] == 'Menu': layout.parseMenuname(item[1]) elif item[0] == 'Item': layout.parseFilename(item[1]) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: layout.parseMenuname(item.get_menu_id()) elif item.get_type() == ukuimenu.TYPE_ENTRY: layout.parseFilename(item.get_desktop_file_id()) elif item.get_type() == ukuimenu.TYPE_SEPARATOR: layout.parseSeparator() layout.order.append(['Merge', 'files']) return layout def __addItem(self, parent, file_id, dom): xml_parent = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, dom) self.__addXmlFilename(xml_parent, dom, file_id, 'Include') def __positionItem(self, parent, item, before=None, after=None): if after: index = parent.get_contents().index(after) + 1 elif before: index = parent.get_contents().index(before) else: # append the item to the list index = len(parent.get_contents()) contents = parent.get_contents() #if this is a move to a new parent you can't remove the item if item in contents: # decrease the destination index, if we shorten the list if (before and (contents.index(item) < index)) \ or (after and (contents.index(item) < index - 1)): index -= 1 contents.remove(item) contents.insert(index, item) layout = self.__createLayout(contents) dom = self.__getMenu(parent).dom menu_xml = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, dom) self.__addXmlLayout(menu_xml, layout, dom) def __undoMoves(self, element, old, new, dom): nodes = [] matches = [] original_old = old final_old = old #get all elements for node in self.__getXmlNodesByName(['Move'], element): nodes.insert(0, node) #if the matches our old parent we've found a stage to undo for node in nodes: xml_old = node.getElementsByTagName('Old')[0] xml_new = node.getElementsByTagName('New')[0] if xml_new.childNodes[0].nodeValue == old: matches.append(node) #we should end up with this path when completed final_old = xml_old.childNodes[0].nodeValue #undoing s for node in matches: element.removeChild(node) if len(matches) > 0: for node in nodes: xml_old = node.getElementsByTagName('Old')[0] xml_new = node.getElementsByTagName('New')[0] path = os.path.split(xml_new.childNodes[0].nodeValue) if path[0] == original_old: element.removeChild(node) for node in dom.getElementsByTagName('Menu'): name_node = node.getElementsByTagName('Name')[0] name = name_node.childNodes[0].nodeValue if name == os.path.split(new)[1]: #copy app and dir directory info from old root_path = dom.getElementsByTagName('Menu')[0].getElementsByTagName('Name')[0].childNodes[0].nodeValue xml_menu = self.__getXmlMenu(root_path + '/' + new, dom.documentElement, dom) for app_dir in node.getElementsByTagName('AppDir'): xml_menu.appendChild(app_dir) for dir_dir in node.getElementsByTagName('DirectoryDir'): xml_menu.appendChild(dir_dir) parent = node.parentNode parent.removeChild(node) node = dom.createElement('Move') node.appendChild(self.__addXmlTextElement(node, 'Old', xml_old.childNodes[0].nodeValue, dom)) node.appendChild(self.__addXmlTextElement(node, 'New', os.path.join(new, path[1]), dom)) element.appendChild(node) if final_old == new: return True node = dom.createElement('Move') node.appendChild(self.__addXmlTextElement(node, 'Old', final_old, dom)) node.appendChild(self.__addXmlTextElement(node, 'New', new, dom)) return element.appendChild(node) def __getMenu(self, item): root = item.get_parent() if not root: #already at the top root = item else: while True: if root.get_parent(): root = root.get_parent() else: break if root.get_menu_id() == self.applications.tree.get_root_directory().get_menu_id(): return self.applications return self.settings def __findMenu(self, menu_id, parent=None): if parent is None: menu = self.__findMenu(menu_id, self.applications.tree.root) if menu is not None: return menu else: return self.__findMenu(menu_id, self.settings.tree.root) if menu_id == self.applications.tree.get_root_directory().get_menu_id(): return self.applications.tree.get_root_directory() if menu_id == self.settings.tree.get_root_directory().get_menu_id(): return self.settings.tree.get_root_directory() for item in parent.get_contents(): if item.get_type() == ukuimenu.TYPE_DIRECTORY: if item.get_menu_id() == menu_id: return item menu = self.__findMenu(menu_id, item) if menu is not None: return menu def save(self, from_loading=False): for menu in ('applications', 'settings'): with codecs.open(getattr(self, menu).path, 'w', 'utf-8') as f: f.write(getattr(self, menu).dom.toprettyxml()) if not from_loading: self.__loadMenus() def quit(self): for file_name in os.listdir(self.getUserItemPath()): if file_name[-6:-2] in ('redo', 'undo'): file_path = os.path.join(self.getUserItemPath(), file_name) os.unlink(file_path) for file_name in os.listdir(getUserDirectoryPath()): if file_name[-6:-2] in ('redo', 'undo'): file_path = os.path.join(getUserDirectoryPath(), file_name) os.unlink(file_path) for file_name in os.listdir(getUserMenuPath()): if file_name[-6:-2] in ('redo', 'undo'): file_path = os.path.join(getUserMenuPath(), file_name) os.unlink(file_path) def revert(self): for name in ('applications', 'settings'): menu = getattr(self, name) self.revertTree(menu.tree.get_root_directory()) path = os.path.join(getUserMenuPath(), menu.tree.get_menu_file().decode('utf-8')) try: os.unlink(path) except OSError: pass #reload DOM for each menu try: menu.dom = xml.dom.minidom.parse(menu.path) except (IOError, xml.parsers.expat.ExpatError): menu.dom = xml.dom.minidom.parseString(getUserMenuXml(menu.tree)) removeWhitespaceNodes(menu.dom) #reset undo/redo, no way to recover from this self.__undo, self.__redo = [], [] self.save() def revertTree(self, menu): for child in menu.get_contents(): if child.get_type() == ukuimenu.TYPE_DIRECTORY: self.revertTree(child) elif child.get_type() == ukuimenu.TYPE_ENTRY: self.revertItem(child) self.revertMenu(menu) def revertItem(self, item): if not self.canRevert(item): return self.__addUndo([item,]) try: os.remove(item.get_desktop_file_path()) except OSError: pass self.save() def revertMenu(self, menu): if not self.canRevert(menu): return #wtf happened here? oh well, just bail if not menu.get_desktop_file_path(): return self.__addUndo([menu,]) file_id = os.path.split(menu.get_desktop_file_path())[1] path = os.path.join(getUserDirectoryPath(), file_id.decode('utf-8')) try: os.remove(path) except OSError: pass self.save() def undo(self): if len(self.__undo) == 0: return files = self.__undo.pop() redo = [] for file_path in files: new_path = file_path.rsplit('.', 1)[0] redo_path = getUniqueRedoFile(new_path) f_file_path = codecs.open(new_path, 'r', 'utf-8') f_new_path = codecs.open(new_path, 'rw', 'utf-8') f_redo_path = codecs.open(redo_path, 'rw', 'utf-8') data = f_new_path.read() f_redo_path.write(data) data = f_file_path.read() f_new_path.write(data) f_file_path.close() f_new_path.close() f_redo_path.close() os.unlink(file_path) redo.append(redo_path) # reload DOM to make changes stick for name in ('applications', 'settings'): menu = getattr(self, name) try: menu.dom = xml.dom.minidom.parse(menu.path) except (IOError, xml.parsers.expat.ExpatError): menu.dom = xml.dom.minidom.parseString(getUserMenuXml(menu.tree)) removeWhitespaceNodes(menu.dom) self.__redo.append(redo) def redo(self): if len(self.__redo) == 0: return files = self.__redo.pop() undo = [] for file_path in files: new_path = file_path.rsplit('.', 1)[0] undo_path = getUniqueUndoFile(new_path) f_file_path = codecs.open(new_path, 'r', 'utf-8') f_new_path = codecs.open(new_path, 'rw', 'utf-8') f_undo_path = codecs.open(undo_path, 'rw', 'utf-8') data = f_new_path.read() f_undo_path.write(data) data = f_file_path.read() f_new_path.write(data) os.unlink(file_path) undo.append(undo_path) f_file_path.close() f_new_path.close() f_undo_path.close() #reload DOM to make changes stick for name in ('applications', 'settings'): menu = getattr(self, name) try: menu.dom = xml.dom.minidom.parse(menu.path) except (IOError, xml.parsers.expat.ExpatError): menu.dom = xml.dom.minidom.parseString(getUserMenuXml(menu.tree)) removeWhitespaceNodes(menu.dom) self.__undo.append(undo) def getMenus(self, parent=None): if parent is None: yield self.applications.tree.get_root_directory() yield self.settings.tree.get_root_directory() else: for menu in parent.get_contents(): if menu.get_type() == ukuimenu.TYPE_DIRECTORY: yield (menu, self.__isVisible(menu)) def getItems(self, menu): for item in menu.get_contents(): if item.get_type() == ukuimenu.TYPE_SEPARATOR: yield (item, True) else: if item.get_type() == ukuimenu.TYPE_ENTRY and item.get_desktop_file_id().decode('utf-8')[-19:] == '-usercustom.desktop': continue yield (item, self.__isVisible(item)) def canRevert(self, item): if item.get_type() == ukuimenu.TYPE_ENTRY: if getItemPath(item.get_desktop_file_id().decode('utf-8')) is not None: path = getUserItemPath() if os.path.isfile(os.path.join(path, item.get_desktop_file_id().decode('utf-8'))): return True elif item.get_type() == ukuimenu.TYPE_DIRECTORY: if item.get_desktop_file_path(): file_id = os.path.split(item.get_desktop_file_path().decode('utf-8'))[1] else: file_id = item.get_menu_id().decode('utf-8') + '.directory' if getDirectoryPath(file_id) is not None: path = getUserDirectoryPath() if os.path.isfile(os.path.join(path, file_id)): return True return False def setVisible(self, item, visible): dom = self.__getMenu(item).dom if item.get_type() == ukuimenu.TYPE_ENTRY: self.__addUndo([self.__getMenu(item), item]) menu_xml = self.__getXmlMenu(self.__getPath(item.get_parent()), dom.documentElement, dom) if visible: self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id().decode('utf-8'), 'Include') self.__writeItem(item, NoDisplay=False) else: self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id().decode('utf-8'), 'Exclude') self.__writeItem(item, NoDisplay=True) self.__addXmlTextElement(menu_xml, 'AppDir', getUserItemPath(), dom) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: self.__addUndo([self.__getMenu(item), item]) #don't mess with it if it's empty nodisplay = True for index in item.get_contents(): nodisplay = index.get_is_nodisplay() if nodisplay == False: break; if len(item.get_contents()) > 0 and nodisplay: msg = _("\nThe current system is not selected [%s] software, can not display the classification!") % (item.get_name().decode('utf-8')) md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() return None if len(item.get_contents()) == 0: msg = _("\nThe current system is not installed [%s] software, can not display the classification!") % (item.get_name().decode('utf-8')) md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() return None menu_xml = self.__getXmlMenu(self.__getPath(item), dom.documentElement, dom) for node in self.__getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): node.parentNode.removeChild(node) self.__writeMenu(item, NoDisplay=not visible) self.__addXmlTextElement(menu_xml, 'DirectoryDir', getUserDirectoryPath(), dom) self.save() def moveItem(self, item, new_parent, before=None, after=None): undo = [] if item.get_parent() != new_parent: #hide old item self.deleteItem(item) undo.append(item) file_id = self.copyItem(item, new_parent) item = ('Item', file_id) undo.append(item) self.__positionItem(new_parent, item, before, after) undo.append(self.__getMenu(new_parent)) self.__addUndo(undo) self.save() def moveMenu(self, menu, new_parent, before=None, after=None): parent = new_parent #don't move a menu into it's child while parent.get_parent(): parent = parent.get_parent() if parent == menu: return False #don't move a menu into itself if new_parent == menu: return False #can't move between top-level menus if self.__getMenu(menu) != self.__getMenu(new_parent): return False if menu.get_parent() != new_parent: dom = self.__getMenu(menu).dom path = self.__getPath(menu) root_path = path[0] xml_root = self.__getXmlMenu(root_path, dom.documentElement, dom) old_path = path[1:] new_path = self.__getPath(new_parent)[1:] + [menu.get_menu_id()] self.__addXmlMove(xml_root, '/'.join(old_path), '/'.join(new_path), dom) self.__positionItem(new_parent, menu, before, after) self.__addUndo([self.__getMenu(new_parent),]) self.save() class Layout: def __init__(self, node=None): self.order = [] def parseMenuname(self, value): self.order.append(['Menuname', value]) def parseSeparator(self): self.order.append(['Separator']) def parseFilename(self, value): self.order.append(['Filename', value]) def parseMerge(self, merge_type='all'): self.order.append(['Merge', merge_type]) class MenuEditMainWindow: timer = None #hack to make editing menu properties work allow_update = True #drag-and-drop stuff dnd_items = [('MOZO_ITEM_ROW', Gtk.TargetFlags.SAME_APP, 0), ('text/plain', 0, 1)] dnd_menus = [('MOZO_MENU_ROW', Gtk.TargetFlags.SAME_APP, 0)] dnd_both = [dnd_items[0],] + dnd_menus drag_data = None edit_pool = [] def __init__(self): self.file_path = "./" self.editor = MenuEditor() self.tree = Gtk.Builder() self.tree.add_from_file(os.path.join('/', 'usr', 'share', 'ukui-menu', 'menueditor.ui')) self.tree.connect_signals(self) self.setupMenuTree() self.setupItemTree() self.tree.get_object('mainwindow').set_icon_from_file('/usr/share/ukui-menu/icons/start.svg') self.tree.get_object('move_up_button').set_sensitive(False) self.tree.get_object('move_down_button').set_sensitive(False) #self.tree.get_object('edit_revert_to_original').set_label(_("Revert to Original")) self.tree.get_object('mainwindow').set_title(_("Edit Category Menu")) self.tree.get_object('revert_button').set_tooltip_text(_("Restore the default menu layout")) self.tree.get_object('label20').set_text(_("Menus:")) self.tree.get_object('label21').set_text(_("Items:")) self.tree.get_object('label22').set_text(_("Move Up")) self.tree.get_object('label23').set_text(_("Move Down")) self.tree.get_object('label24').set_text(_("Revert all menus to original settings?")) self.tree.get_object('revertdialog').set_title(_("Revert Changes?")) accelgroup = Gtk.AccelGroup() keyval, modifier = Gtk.accelerator_parse('Z') accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_mainwindow_undo) keyval, modifier = Gtk.accelerator_parse('Z') accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_mainwindow_redo) self.tree.get_object('mainwindow').add_accel_group(accelgroup) def run(self): self.loadMenus() self.editor.applications.tree.add_monitor(self.menuChanged, None) self.editor.settings.tree.add_monitor(self.menuChanged, None) self.tree.get_object('mainwindow').show_all() def menuChanged(self, *a): if self.timer: GLib.Source.remove(self.timer) self.timer = None self.timer = GLib.timeout_add(3, self.loadUpdates) def loadUpdates(self): if not self.allow_update: self.timer = None return False menu_tree = self.tree.get_object('menu_tree') item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() update_items = False item_id, separator_path = None, None if iter: update_items = True if items[iter][3].get_type() == ukuimenu.TYPE_DIRECTORY: item_id = os.path.split(items[iter][3].get_desktop_file_path())[1] update_items = True elif items[iter][3].get_type() == ukuimenu.TYPE_ENTRY: item_id = items[iter][3].get_desktop_file_id() update_items = True elif items[iter][3].get_type() == ukuimenu.TYPE_SEPARATOR: item_id = items.get_path(iter).to_string() update_items = True menus, iter = menu_tree.get_selection().get_selected() update_menus = False menu_id = None if iter: if menus[iter][2].get_desktop_file_path(): menu_id = os.path.split(menus[iter][2].get_desktop_file_path())[1] else: menu_id = menus[iter][2].get_menu_id() update_menus = True self.loadMenus() #find current menu in new tree if update_menus: menu_tree.get_model().foreach(self.findMenu, menu_id) menus, iter = menu_tree.get_selection().get_selected() if iter: self.on_menu_tree_cursor_changed(menu_tree) #find current item in new list if update_items: i = 0 for item in item_tree.get_model(): found = False if item[3].get_type() == ukuimenu.TYPE_ENTRY and item[3].get_desktop_file_id() == item_id: found = True if item[3].get_type() == ukuimenu.TYPE_DIRECTORY and item[3].get_desktop_file_path(): if os.path.split(item[3].get_desktop_file_path())[1] == item_id: found = True if item[3].get_type() == ukuimenu.TYPE_SEPARATOR: if not isinstance(item_id, tuple): #we may not skip the increment via "continue" i += 1 continue #separators have no id, have to find them manually #probably won't work with two separators together if (item_id[0] - 1,) == (i,): found = True elif (item_id[0] + 1,) == (i,): found = True elif (item_id[0],) == (i,): found = True if found: item_tree.get_selection().select_path((i,)) self.on_item_tree_cursor_changed(item_tree) break i += 1 self.timer = None return False def on_style_updated(self, *args): #self.loadUpdates() print("aa") def loadMenus(self): self.menu_store.clear() for menu in self.editor.getMenus(): iters = [None]*20 self.loadMenu(iters, menu) menu_tree = self.tree.get_object('menu_tree') menu_tree.set_model(self.menu_store) for menu in self.menu_store: #this might not work for some reason try: menu_tree.expand_to_path(menu.path) except: pass menu_tree.get_selection().select_path((0,)) self.on_menu_tree_cursor_changed(menu_tree) def loadMenu(self, iters, parent, depth=0): if depth == 0: icon = getIcon(parent) iters[depth] = self.menu_store.append(None, (icon, cgi.escape(parent.get_name().decode('utf-8')), parent)) depth += 1 for menu, show in self.editor.getMenus(parent): if show: name = cgi.escape(menu.get_name().decode('utf-8')) else: name = '' + cgi.escape(menu.get_name().decode('utf-8')) + '' icon = getIcon(menu) iters[depth] = self.menu_store.append(iters[depth-1], (icon, name, menu)) self.loadMenu(iters, menu, depth) depth -= 1 def loadItems(self, menu, menu_path): self.item_store.clear() for item, show in self.editor.getItems(menu): menu_icon = None if item.get_type() == ukuimenu.TYPE_SEPARATOR: name = '---' icon = None elif item.get_type() == ukuimenu.TYPE_ENTRY: if show: name = cgi.escape(item.get_display_name().decode('utf-8')) else: name = '' + cgi.escape(item.get_display_name().decode('utf-8')) + '' icon = getIcon(item) else: if show: name = cgi.escape(item.get_name().decode('utf-8')) else: name = '' + cgi.escape(item.get_name().decode('utf-8')) + '' icon = getIcon(item) self.item_store.append((show, icon, name, item)) def waitForNewItemProcess(self, process, parent_id, file_path): if process.poll() is not None: if os.path.isfile(file_path): self.editor.insertExternalItem(os.path.split(file_path)[1], parent_id) return False return True def waitForNewMenuProcess(self, process, parent_id, file_path): if process.poll() is not None: #hack for broken ukui-desktop-item-edit broken_path = os.path.join(os.path.split(file_path)[0], '.directory') if os.path.isfile(broken_path): os.rename(broken_path, file_path) if os.path.isfile(file_path): self.editor.insertExternalMenu(os.path.split(file_path)[1], parent_id) return False return True #this callback keeps you from editing the same item twice def waitForEditProcess(self, process, file_path): if process.poll() is not None: self.edit_pool.remove(file_path) return False return True def findMenu(self, menus, path, iter, menu_id): if not menus[path][2].get_desktop_file_path(): if menu_id == menus[path][2].get_menu_id(): menu_tree = self.tree.get_object('menu_tree') menu_tree.expand_to_path(path) menu_tree.get_selection().select_path(path) return True return False if os.path.split(menus[path][2].get_desktop_file_path())[1] == menu_id: menu_tree = self.tree.get_object('menu_tree') menu_tree.expand_to_path(path) menu_tree.get_selection().select_path(path) return True def setupMenuTree(self): self.menu_store = Gtk.TreeStore(GdkPixbuf.Pixbuf, str, object) menus = self.tree.get_object('menu_tree') column = Gtk.TreeViewColumn(_('Name')) column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) column.set_attributes(cell, pixbuf=0) cell = Gtk.CellRendererText() cell.set_fixed_size(-1, 25) column.pack_start(cell, True) column.set_attributes(cell, markup=1) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) menus.append_column(column) menus.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, self.dnd_menus, Gdk.DragAction.COPY) menus.enable_model_drag_dest(self.dnd_both, Gdk.DragAction.PRIVATE) def setupItemTree(self): items = self.tree.get_object('item_tree') column = Gtk.TreeViewColumn(_('Show')) cell = Gtk.CellRendererToggle() cell.connect('toggled', self.on_item_tree_show_toggled) column.pack_start(cell, True) column.set_attributes(cell, active=0) #hide toggle for separators column.set_cell_data_func(cell, self._cell_data_toggle_func) items.append_column(column) column = Gtk.TreeViewColumn(_('Item')) column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) column.set_attributes(cell, pixbuf=1) cell = Gtk.CellRendererText() cell.set_fixed_size(-1, 25) column.pack_start(cell, True) column.set_attributes(cell, markup=2) items.append_column(column) self.item_store = Gtk.ListStore(bool, GdkPixbuf.Pixbuf, str, object) items.set_model(self.item_store) items.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, self.dnd_items, Gdk.DragAction.COPY) items.enable_model_drag_dest(self.dnd_items, Gdk.DragAction.PRIVATE) def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter, data=None): if model[treeiter][3].get_type() == ukuimenu.TYPE_SEPARATOR: renderer.set_property('visible', False) else: renderer.set_property('visible', True) def on_item_tree_show_toggled(self, cell, path): item = self.item_store[path][3] if item.get_type() == ukuimenu.TYPE_SEPARATOR: return if self.item_store[path][0]: state = self.editor.setVisible(item, False) if state == None: return else: if self.item_store[path][1] == None and item.get_name().decode('utf-8') != _("Android"): msg = _("\n[%s] is a system autostart service software, you should not show it in startup menu!") % (item.get_name().decode('utf-8')) md = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg) md.run() md.destroy() return state = self.editor.setVisible(item, True) if state == None: return self.item_store[path][0] = not self.item_store[path][0] def on_move_up_button_clicked(self, button): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() if not iter: return path = items.get_path(iter) #at top, can't move up if path.get_indices()[0] == 0: return item = items[path][3] before = items[(path[0] - 1,)][3] if item.get_type() == ukuimenu.TYPE_ENTRY: self.editor.moveItem(item, item.get_parent(), before=before) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: self.editor.moveMenu(item, item.get_parent(), before=before) elif item.get_type() == ukuimenu.TYPE_SEPARATOR: self.editor.moveSeparator(item, item.get_parent(), before=before) def on_move_down_button_clicked(self, button): item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() if not iter: return path = items.get_path(iter) #at bottom, can't move down if path.get_indices()[0] == (len(items) - 1): return item = items[path][3] after = items[path][3] if item.get_type() == ukuimenu.TYPE_ENTRY: self.editor.moveItem(item, item.get_parent(), after=after) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: self.editor.moveMenu(item, item.get_parent(), after=after) elif item.get_type() == ukuimenu.TYPE_SEPARATOR: self.editor.moveSeparator(item, item.get_parent(), after=after) def on_item_tree_popup_menu(self, item_tree, event=None): model, iter = item_tree.get_selection().get_selected() if event: #don't show if it's not the right mouse button if event.button != 3: return button = event.button event_time = event.time info = item_tree.get_path_at_pos(int(event.x), int(event.y)) if info is not None: path, col, cellx, celly = info item_tree.grab_focus() item_tree.set_cursor(path, col, 0) else: path = model.get_path(iter) button = 0 event_time = 0 item_tree.grab_focus() item_tree.set_cursor(path, item_tree.get_columns()[0], 0) popup = self.tree.get_object('edit_menu') popup.popup(None, None, None, None, button, event_time) #without this shift-f10 won't work return True def on_item_tree_cursor_changed(self, treeview): items, iter = treeview.get_selection().get_selected() if iter is None: return item = items[iter][3] index = items.get_path(iter).get_indices()[0] can_go_up = index > 0 can_go_down = index < len(items) - 1 self.tree.get_object('move_up_button').set_sensitive(can_go_up) self.tree.get_object('move_down_button').set_sensitive(can_go_down) def on_item_tree_drag_data_get(self, treeview, context, selection, target_id, etime): items, iter = treeview.get_selection().get_selected() self.drag_data = items[iter][3] def on_item_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime): items = treeview.get_model() types_before = (Gtk.TreeViewDropPosition.BEFORE, Gtk.TreeViewDropPosition.INTO_OR_BEFORE) types_into = (Gtk.TreeViewDropPosition.INTO_OR_BEFORE, Gtk.TreeViewDropPosition.INTO_OR_AFTER) types_after = (Gtk.TreeViewDropPosition.AFTER, Gtk.TreeViewDropPosition.INTO_OR_AFTER) if str(selection.get_target()) == 'MOZO_ITEM_ROW': drop_info = treeview.get_dest_row_at_pos(x, y) before = None after = None if self.drag_data is None: return False item = self.drag_data # by default we assume, that the items stays in the same menu destination = item.get_parent() if drop_info: path, position = drop_info target = items[path][3] # move the item to the directory, if the item was dropped into it if (target.get_type() == ukuimenu.TYPE_DIRECTORY) and (position in types_into): # append the selected item to the choosen menu destination = target elif position in types_before: before = target elif position in types_after: after = target else: # this does not happen pass else: path = (len(items) - 1,) after = items[path][3] if item.get_type() == ukuimenu.TYPE_ENTRY: self.editor.moveItem(item, destination, before, after) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: if not self.editor.moveMenu(item, destination, before, after): self.loadUpdates() elif item.get_type() == ukuimenu.TYPE_SEPARATOR: self.editor.moveSeparator(item, destination, before, after) context.finish(True, True, etime) elif str(selection.get_target()) == 'text/plain': if selection.data is None: return False menus, iter = self.tree.get_object('menu_tree').get_selection().get_selected() parent = menus[iter][2] drop_info = treeview.get_dest_row_at_pos(x, y) before = None after = None if drop_info: path, position = drop_info if position in types_before: before = items[path][3] else: after = items[path][3] else: path = (len(items) - 1,) after = items[path][3] file_path = urllib.unquote(selection.data).strip() if not file_path.startswith('file:'): return myfile = Gio.File(uri=file_path) file_info = myfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) content_type = file_info.get_content_type() if content_type == 'application/x-desktop': input_stream = myfile.read() keyfile = GLib.KeyFile() keyfile.load_from_data(input_stream.read()) self.editor.createItem(parent, before, after, KeyFile=keyfile) elif content_type in ('application/x-shellscript', 'application/x-executable'): self.editor.createItem(parent, before, after, Name=os.path.split(file_path)[1].strip(), Exec=file_path.replace('file://', '').strip(), Terminal=False) self.drag_data = None def on_menu_tree_cursor_changed(self, treeview): menus, iter = treeview.get_selection().get_selected() if iter is None: return menu_path = menus.get_path(iter) item_tree = self.tree.get_object('item_tree') item_tree.get_selection().unselect_all() self.loadItems(self.menu_store[menu_path][2], menu_path) self.tree.get_object('move_up_button').set_sensitive(False) self.tree.get_object('move_down_button').set_sensitive(False) def on_revert_button_clicked(self, button): dialog = self.tree.get_object('revertdialog') dialog.set_transient_for(self.tree.get_object('mainwindow')) dialog.show_all() if dialog.run() == Gtk.ResponseType.YES: self.editor.revert() dialog.hide() else: dialog.hide() def on_mainwindow_undo(self, accelgroup, window, keyval, modifier): self.editor.undo() def on_mainwindow_redo(self, accelgroup, window, keyval, modifier): self.editor.redo() def on_menu_tree_drag_data_get(self, treeview, context, selection, target_id, etime): menus, iter = treeview.get_selection().get_selected() self.drag_data = menus[iter][2] def on_menu_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime): menus = treeview.get_model() drop_info = treeview.get_dest_row_at_pos(x, y) if drop_info: path, position = drop_info types = (Gtk.TreeViewDropPosition.INTO_OR_BEFORE, Gtk.TreeViewDropPosition.INTO_OR_AFTER) if position not in types: context.finish(False, False, etime) return False if str(selection.get_target()) in ('MOZO_ITEM_ROW', 'MOZO_MENU_ROW'): if self.drag_data is None: return False item = self.drag_data new_parent = menus[path][2] if item.get_type() == ukuimenu.TYPE_ENTRY: self.editor.copyItem(item, new_parent) elif item.get_type() == ukuimenu.TYPE_DIRECTORY: if not self.editor.moveMenu(item, new_parent): self.loadUpdates() elif item.get_type() == ukuimenu.TYPE_SEPARATOR: self.editor.moveSeparator(item, new_parent) else: context.finish(False, False, etime) context.finish(True, True, etime) self.drag_data = None ukui-menu/ukui_menu/__init__.py0000644000175000017500000000000013110762006015511 0ustar fengfengukui-menu/ukui_menu/easyfiles.py0000664000175000017500000001114413237775114015771 0ustar fengfeng# -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os import os.path import urllib.request, urllib.parse, urllib.error """ def TestForDir(dirname): if not os.path.exists( os.path.join(os.path.expanduser("~"), dirname ) ): os.makedirs( os.path.join( os.path.expanduser("~"), dirname) ) def TestForFiles(filename): if not os.path.exists( os.path.join( os.path.expanduser("~"), filename ) ): file = open( os.path.join( os.path.expanduser("~"), filename ),"w") file.close() def FileExists(f): return os.path.exists(f) """ def GetFilePath(uri): path = urllib.request.url2pathname(uri) # escape special chars path = path.strip('\r\n\x00') # remove \r\n and NULL # get the path to file if path.startswith('file://'): # nautilus, rox path = path[7:] # 7 is len('file://') return path """ def ParseDesktopFile(DFile): # Get the locale lang = os.getenv("LANG") if lang: langArray = lang.split("_") else: langArray = [ None ] locale = langArray[0] or "" localeName = "Name[" + locale + "]=" localeComment = "Comment[" + locale + "]=" localeGenericName = "GenericName[" + locale + "]=" PlaceName=PlaceComment=PlaceExec=PlaceIconName=GenericName=TerminalName="" FileData = [] if FileExists(GetFilePath(DFile))==True: parseData = False openfile = open(GetFilePath(DFile), 'r') datalist = openfile.readlines() openfile.close() for i in datalist: i = i.strip('\r\n\x00') if len(i) != 0: if i[0] == "[" and i[-1] == "]": parseData = "[Desktop Entry]" == i elif parseData: if i[0:5] == "Name=": PlaceName = i[5:] elif i[0:9] == localeName: PlaceName = i[9:] elif i[0:8] == "Comment=": PlaceComment = i[8:] elif i[0:12] == localeComment: PlaceComment = i[12:] elif i[0:5] == "Exec=": PlaceExec = i[5:] elif i[0:5] == "Icon=": PlaceIconName = i[5:] elif i[0:12] == "GenericName=": GenericName = i[12:] elif i[0:16] == localeGenericName: GenericName = i[16:] elif i[0:9] == "Terminal=": TerminalName = i[9:] FileData.append(PlaceName) FileData.append(PlaceComment) FileData.append(PlaceExec) FileData.append(PlaceIconName) FileData.append(GenericName) FileData.append(TerminalName) return FileData return None def WriteListFile(ListToAdd,ItemToAdd,mode): RecentapplicationsFile = open (os.path.join(os.path.expanduser("~"), ListToAdd),"r") RecentapplicationsList = RecentapplicationsFile.readlines() RecentapplicationsList.reverse() RecentapplicationsFile.close() if RecentapplicationsList != []: outfile = open (os.path.join(os.path.expanduser("~"), ListToAdd),mode) outfile.write(ItemToAdd+"\n") outfile.close() else: outfile = open (os.path.join(os.path.expanduser("~"), ListToAdd),mode) outfile.write(ItemToAdd+"\n") outfile.close() def EditDesktopFile(DroppedFile,FileData,ListToAdd): fileHandle = open ( DroppedFile , 'w' ) fileHandle.write ( '[Desktop Entry]\nEncoding=UTF-8\n' ) fileHandle.write ( 'Name='+FileData[0]+'\n') fileHandle.write ( 'Comment='+FileData[1]+'\n') fileHandle.write ( 'Exec='+FileData[2]+'\n') fileHandle.write ( 'Icon='+FileData[3]+'\n') fileHandle.write ( 'GenericName='+FileData[4]+'\n') fileHandle.write ( 'Terminal='+FileData[5]+'\n') fileHandle.close() def WriteDesktopFile(DroppedFile,FileData,ListToAdd): fileHandle = open ( DroppedFile , 'w' ) fileHandle.write ( '[Desktop Entry]\nEncoding=UTF-8\n' ) fileHandle.write ( 'Name='+FileData[0]+'\n') fileHandle.write ( 'Comment='+FileData[1]+'\n') fileHandle.write ( 'Exec='+FileData[2]+'\n') fileHandle.write ( 'Icon='+FileData[3]+'\n') fileHandle.write ( 'GenericName='+FileData[4]+'\n') fileHandle.write ( 'Terminal='+FileData[5]+'\n') fileHandle.close() WriteListFile(ListToAdd,DroppedFile,"a") print "Added to places.list" """ ukui-menu/po/0000755000175000017500000000000013265336430012040 5ustar fengfengukui-menu/po/POTFILES.in0000664000175000017500000000050713237775114013626 0ustar fengfeng[encoding: UTF-8] ukui-menu-editor lib/ukui-menu.py ukui_menu/__init__.py ukui_menu/plugins/__init__.py ukui_menu/plugins/menu.py ukui_menu/easygsettings.py ukui_menu/easyfiles.py ukui_menu/easybuttons.py ukui_menu/execute.py ukui_menu/filemonitor.py ukui_menu/keybinding.py ukui_menu/menueditor.py ukui_menu/pointerMonitor.py ukui-menu/po/ko.po0000664000175000017500000001225413237775114013024 0ustar fengfeng# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: ukui-menu\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-05-24 17:31+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Moordev \n" "Language-Team: LANGUAGE \n" "Language: Korean\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" #: ../lib/ukui-menu.py:57 msgid "Menu" msgstr "" #: ../lib/ukui-menu.py:315 #, fuzzy msgid "Start" msgstr "시작메뉴" #: ../lib/ukui-menu.py:345 #, fuzzy msgid "Startup Menu" msgstr "시작메뉴에 추가" #: ../lib/ukui-menu.py:414 msgid "Reload plugins" msgstr "" #: ../lib/ukui-menu.py:417 msgid "About" msgstr "" #: ../ukui_menu/plugins/menu.py:199 ../ukui_menu/plugins/menu.py:1134 #: ../ukui_menu/plugins/menu.py:1135 msgid "Games" msgstr "게임" #: ../ukui_menu/plugins/menu.py:387 ../ukui_menu/plugins/menu.py:492 msgid "All App" msgstr "모든 앱" #: ../ukui_menu/plugins/menu.py:392 ../ukui_menu/plugins/menu.py:481 msgid "Favorite" msgstr "즐겨찾기" #: ../ukui_menu/plugins/menu.py:422 #, fuzzy msgid "Power" msgstr "시스템종료" #: ../ukui_menu/plugins/menu.py:503 ../ukui_menu/plugins/menu.py:802 #: ../ukui_menu/plugins/menu.py:810 ../ukui_menu/plugins/menu.py:1424 #: ../ukui_menu/plugins/menu.py:1850 msgid "Search local program" msgstr "설치된 프로그램 검색" #: ../ukui_menu/plugins/menu.py:533 msgid "Home" msgstr "" #: ../ukui_menu/plugins/menu.py:541 msgid "Settings" msgstr "" #: ../ukui_menu/plugins/menu.py:549 msgid "No items matched" msgstr "일치된 검색결과가 없습니다" #: ../ukui_menu/plugins/menu.py:850 ../ukui_menu/plugins/menu.py:1507 msgid "Open(_O)" msgstr "열기(_O)" #: ../ukui_menu/plugins/menu.py:853 ../ukui_menu/plugins/menu.py:1510 msgid "Add to desktop" msgstr "바탕화면에 아이콘만들기" #: ../ukui_menu/plugins/menu.py:857 ../ukui_menu/plugins/menu.py:1513 msgid "Lock to panel(_L)" msgstr "패널에 고정(_L)" #: ../ukui_menu/plugins/menu.py:860 msgid "Unlock from startup menu" msgstr "시작메뉴에서 해제" #: ../ukui_menu/plugins/menu.py:864 ../ukui_menu/plugins/menu.py:1523 msgid "Property(_P)" msgstr "속성(_P)" #: ../ukui_menu/plugins/menu.py:926 msgid "Shutdown" msgstr "전원끄기" #: ../ukui_menu/plugins/menu.py:927 msgid "Reboot" msgstr "재부팅" #: ../ukui_menu/plugins/menu.py:930 msgid "Logout" msgstr "로그아웃" #: ../ukui_menu/plugins/menu.py:931 msgid "Switch User" msgstr "사용자 변경" #: ../ukui_menu/plugins/menu.py:932 msgid "Lock Screen" msgstr "화면잠그기" #: ../ukui_menu/plugins/menu.py:1127 msgid "Accessories" msgstr "보조프로그램" #: ../ukui_menu/plugins/menu.py:1129 ../ukui_menu/plugins/menu.py:1130 msgid "Office" msgstr "사무" #: ../ukui_menu/plugins/menu.py:1132 msgid "StartUp" msgstr "시작메뉴" #: ../ukui_menu/plugins/menu.py:1380 msgid "All" msgstr "모두" #: ../ukui_menu/plugins/menu.py:1380 msgid "Show all applications" msgstr "모든 프로그램 보이기" #: ../ukui_menu/plugins/menu.py:1516 msgid "Add to startup menu" msgstr "시작메뉴에 추가" #: ../ukui_menu/plugins/menu.py:1518 msgid "Uninstall" msgstr "삭제" #: ../ukui_menu/plugins/menu.py:1519 msgid "Remove from list(_R)" msgstr "리스트에서 없애기(_R)" #: ../ukui_menu/plugins/menu.py:1521 msgid "Remove all" msgstr "모두 없애기" #: ../ukui_menu/plugins/menu.py:1718 msgid "Type:" msgstr "유형:" #: ../ukui_menu/plugins/menu.py:1720 msgid "Name:" msgstr "이름:" #: ../ukui_menu/plugins/menu.py:1722 msgid "Command:" msgstr "명령:" #: ../ukui_menu/plugins/menu.py:1724 msgid "Note:" msgstr "노트:" #: ../ukui_menu/plugins/menu.py:1733 msgid "Application" msgstr "프로그램" #: ../ukui_menu/plugins/menu.py:1735 msgid "Application in Terminal" msgstr "터미널에서 실행되는 프로그램" #: ../ukui_menu/plugins/menu.py:1744 msgid "Property" msgstr "속성" #: ../ukui_menu/plugins/menu.py:1897 msgid "Chromium Browser" msgstr "크로미움 브라우저" #: ../ukui_menu/plugins/menu.py:1903 msgid "Fcitx Config" msgstr "Fcitx 설정" #: ../ukui_menu/easybuttons.py:416 #, fuzzy msgid "Comment:" msgstr "명령:" #: ../ukui_menu/keybinding.py:200 msgid "Click to set a new accelerator key for opening and closing the menu. " msgstr "" #: ../ukui_menu/keybinding.py:201 msgid "Press Escape or click again to cancel the operation. " msgstr "" #: ../ukui_menu/keybinding.py:202 msgid "Press Backspace to clear the existing keybinding." msgstr "" #: ../ukui_menu/keybinding.py:215 msgid "Pick an accelerator" msgstr "" #: ../ukui_menu/keybinding.py:267 msgid "" msgstr "" #~ msgid "Computer " #~ msgstr "내 컴퓨터" #~ msgid "CtrlCenter" #~ msgstr "제어센터" #~ msgid "" #~ "Couldn't save favorites. Check if you have write access to ~/.config/mate-" #~ "menu" #~ msgstr "" #~ "즐겨찾기에 추가할 수 없습니다. ~/.config/mate-menu에 권한이 씨는지 확인해" #~ "주시길 바랍니다" #~ msgid "Install package '%s'" #~ msgstr "'%s'패키지 설치" ukui-menu/po/tr.po0000664000175000017500000001256013237775114013040 0ustar fengfeng# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: ukui-menu\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-05-24 17:31+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Cihan Alkan \n" "Language-Team: LANGUAGE \n" "Language: Turkish\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" #: ../lib/ukui-menu.py:57 msgid "Menu" msgstr "Menü" #: ../lib/ukui-menu.py:315 #, fuzzy msgid "Start" msgstr "Başlat" #: ../lib/ukui-menu.py:345 #, fuzzy msgid "Startup Menu" msgstr "Başlangıç Menüsü" #: ../lib/ukui-menu.py:414 msgid "Reload plugins" msgstr "Eklentileri yeniden yükle" #: ../lib/ukui-menu.py:417 msgid "About" msgstr "Hakkında" #: ../ukui_menu/plugins/menu.py:199 ../ukui_menu/plugins/menu.py:1134 #: ../ukui_menu/plugins/menu.py:1135 msgid "Games" msgstr "Oyunlar" #: ../ukui_menu/plugins/menu.py:387 ../ukui_menu/plugins/menu.py:492 msgid "All App" msgstr "Tüm Uyg." #: ../ukui_menu/plugins/menu.py:392 ../ukui_menu/plugins/menu.py:481 msgid "Favorite" msgstr "Favoriler" #: ../ukui_menu/plugins/menu.py:422 msgid "Power" msgstr "Kapat" #: ../ukui_menu/plugins/menu.py:503 ../ukui_menu/plugins/menu.py:802 #: ../ukui_menu/plugins/menu.py:810 ../ukui_menu/plugins/menu.py:1424 #: ../ukui_menu/plugins/menu.py:1850 msgid "Search local program" msgstr "Program ara" #: ../ukui_menu/plugins/menu.py:533 msgid "Home" msgstr "Ev" #: ../ukui_menu/plugins/menu.py:541 msgid "Settings" msgstr "Ayarlar" #: ../ukui_menu/plugins/menu.py:549 msgid "No items matched" msgstr "Eşleşen öğe yok" #: ../ukui_menu/plugins/menu.py:850 ../ukui_menu/plugins/menu.py:1507 msgid "Open(_O)" msgstr "_Aç" #: ../ukui_menu/plugins/menu.py:853 ../ukui_menu/plugins/menu.py:1510 msgid "Add to desktop" msgstr "Masaüstüne ekle" #: ../ukui_menu/plugins/menu.py:857 ../ukui_menu/plugins/menu.py:1513 msgid "Lock to panel(_L)" msgstr "_Panele kilitle" #: ../ukui_menu/plugins/menu.py:860 msgid "Unlock from startup menu" msgstr "Başlangıç menüsünden kilidi kaldır" #: ../ukui_menu/plugins/menu.py:864 ../ukui_menu/plugins/menu.py:1523 msgid "Property(_P)" msgstr "_Özellik" #: ../ukui_menu/plugins/menu.py:926 msgid "Shutdown" msgstr "Bilgisayarı Kapat" #: ../ukui_menu/plugins/menu.py:927 msgid "Reboot" msgstr "Yeniden Başlat" #: ../ukui_menu/plugins/menu.py:930 msgid "Logout" msgstr "Kullanıcı Çıkışı" #: ../ukui_menu/plugins/menu.py:931 msgid "Switch User" msgstr "Kullanıcı Değiştir" #: ../ukui_menu/plugins/menu.py:932 msgid "Lock Screen" msgstr "Ekranı Kilitle" #: ../ukui_menu/plugins/menu.py:1127 msgid "Accessories" msgstr "Donatılar" #: ../ukui_menu/plugins/menu.py:1129 ../ukui_menu/plugins/menu.py:1130 msgid "Office" msgstr "Ofis" #: ../ukui_menu/plugins/menu.py:1132 msgid "StartUp" msgstr "Başlangıç" #: ../ukui_menu/plugins/menu.py:1380 msgid "All" msgstr "Tümü" #: ../ukui_menu/plugins/menu.py:1380 msgid "Show all applications" msgstr "Tüm uygulamaları göster" #: ../ukui_menu/plugins/menu.py:1516 msgid "Add to startup menu" msgstr "Başlangıç menüsüne ekle" #: ../ukui_menu/plugins/menu.py:1518 msgid "Uninstall" msgstr "Kaldır" #: ../ukui_menu/plugins/menu.py:1519 msgid "Remove from list(_R)" msgstr "_Listeden çıkar" #: ../ukui_menu/plugins/menu.py:1521 msgid "Remove all" msgstr "Tümünü çıkar" #: ../ukui_menu/plugins/menu.py:1718 msgid "Type:" msgstr "Tür:" #: ../ukui_menu/plugins/menu.py:1720 msgid "Name:" msgstr "İsim:" #: ../ukui_menu/plugins/menu.py:1722 msgid "Command:" msgstr "Komut:" #: ../ukui_menu/plugins/menu.py:1724 msgid "Note:" msgstr "Not:" #: ../ukui_menu/plugins/menu.py:1733 msgid "Application" msgstr "Uygulama" #: ../ukui_menu/plugins/menu.py:1735 msgid "Application in Terminal" msgstr "Terminal Uygulamaları" #: ../ukui_menu/plugins/menu.py:1744 msgid "Property" msgstr "Özellik" #: ../ukui_menu/plugins/menu.py:1897 msgid "Chromium Browser" msgstr "Chromium Tarayıcı" #: ../ukui_menu/plugins/menu.py:1903 msgid "Fcitx Config" msgstr "Fcitx Yapılandırması" #: ../ukui_menu/easybuttons.py:416 #, fuzzy msgid "Comment:" msgstr "Açıklama:" #: ../ukui_menu/keybinding.py:200 msgid "Click to set a new accelerator key for opening and closing the menu. " msgstr "Menüyü açmak ve kapatmak için yeni bir hızlandırıcı anahtarı ayarlamak için tıklayın." #: ../ukui_menu/keybinding.py:201 msgid "Press Escape or click again to cancel the operation. " msgstr "İşlemi iptal etmek için Escape tuşuna basın veya tekrar tıklayın." #: ../ukui_menu/keybinding.py:202 msgid "Press Backspace to clear the existing keybinding." msgstr "Varolan anahtar bağlamasını temizlemek için Geri tuşuna basın." #: ../ukui_menu/keybinding.py:215 msgid "Pick an accelerator" msgstr "Hızlandırıcı seçin" #: ../ukui_menu/keybinding.py:267 msgid "" msgstr "" #~ msgid "Computer " #~ msgstr "Bilgisayar" #~ msgid "CtrlCenter" #~ msgstr "CtrlCenter" #~ msgid "" #~ "Couldn't save favorites. Check if you have write access to ~/.config/mate-" #~ "menu" #~ msgstr "" #~ "Couldn't save favorites. Check if you have write access to ~/.config/mate-" #~ "menu" #~ msgid "Install package '%s'" #~ msgstr "Install package '%s'" ukui-menu/po/ukui-menu.pot0000664000175000017500000002216313265336430014511 0ustar fengfeng# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-16 12:02+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../ukui-menu-editor:38 msgid "" "\n" "Current menu mode is not category mode, please switch and try again!" msgstr "" #: ../lib/ukui-menu.py:60 msgid "Menu" msgstr "" #: ../lib/ukui-menu.py:265 ../lib/ukui-menu.py:411 msgid "Normal Menu" msgstr "" #: ../lib/ukui-menu.py:267 ../lib/ukui-menu.py:413 msgid "Category Menu" msgstr "" #. self.tree.get_object('edit_revert_to_original').set_label(_("Revert to Original")) #: ../lib/ukui-menu.py:269 ../ukui_menu/menueditor.py:883 msgid "Edit Category Menu" msgstr "" #: ../lib/ukui-menu.py:358 msgid "Start" msgstr "" #: ../lib/ukui-menu.py:391 msgid "Startup Menu" msgstr "" #: ../lib/ukui-menu.py:403 msgid "Menu Property" msgstr "" #: ../lib/ukui-menu.py:405 msgid "Menu Type:" msgstr "" #: ../lib/ukui-menu.py:406 msgid "This is used to set up the menu type." msgstr "" #: ../lib/ukui-menu.py:408 msgid "File History:" msgstr "" #: ../lib/ukui-menu.py:409 msgid "This is used to set up whether the file history is displayed." msgstr "" #: ../lib/ukui-menu.py:429 msgid "Recent file number:" msgstr "" #: ../lib/ukui-menu.py:431 msgid "Recent app number:" msgstr "" #: ../lib/ukui-menu.py:446 msgid "Reset to default(_R)" msgstr "" #: ../lib/ukui-menu.py:453 msgid "Close(_C)" msgstr "" #: ../lib/ukui-menu.py:610 msgid "Reload plugins" msgstr "" #: ../lib/ukui-menu.py:613 ../ukui_menu/plugins/menu.py:1957 msgid "Property" msgstr "" #. wps #: ../ukui_menu/plugins/menu.py:125 ../ukui_menu/plugins/menu.py:732 #: ../ukui_menu/plugins/menu.py:904 ../ukui_menu/plugins/menu.py:2204 #: ../ukui_menu/plugins/menu.py:2311 msgid "WPS Writer" msgstr "" #: ../ukui_menu/plugins/menu.py:126 ../ukui_menu/plugins/menu.py:736 #: ../ukui_menu/plugins/menu.py:906 ../ukui_menu/plugins/menu.py:2204 #: ../ukui_menu/plugins/menu.py:2318 msgid "WPS Presentation" msgstr "" #: ../ukui_menu/plugins/menu.py:127 ../ukui_menu/plugins/menu.py:740 #: ../ukui_menu/plugins/menu.py:908 ../ukui_menu/plugins/menu.py:2204 #: ../ukui_menu/plugins/menu.py:2325 msgid "WPS Spreadsheets" msgstr "" #. qt app #: ../ukui_menu/plugins/menu.py:128 ../ukui_menu/plugins/menu.py:758 #: ../ukui_menu/plugins/menu.py:910 ../ukui_menu/plugins/menu.py:2295 msgid "Qt Creator" msgstr "" #. qt app #: ../ukui_menu/plugins/menu.py:129 ../ukui_menu/plugins/menu.py:2280 msgid "SMPlayer" msgstr "" #: ../ukui_menu/plugins/menu.py:130 ../ukui_menu/plugins/menu.py:766 #: ../ukui_menu/plugins/menu.py:912 ../ukui_menu/plugins/menu.py:2282 msgid "Kylin Video" msgstr "" #. gtk app #: ../ukui_menu/plugins/menu.py:131 ../ukui_menu/plugins/menu.py:749 #: ../ukui_menu/plugins/menu.py:914 ../ukui_menu/plugins/menu.py:2333 msgid "Eye of MATE Image Viewer" msgstr "" #: ../ukui_menu/plugins/menu.py:132 ../ukui_menu/plugins/menu.py:753 #: ../ukui_menu/plugins/menu.py:916 ../ukui_menu/plugins/menu.py:2340 msgid "Atril Document Viewer" msgstr "" #. gtk app #: ../ukui_menu/plugins/menu.py:133 ../ukui_menu/plugins/menu.py:745 #: ../ukui_menu/plugins/menu.py:918 ../ukui_menu/plugins/menu.py:2204 #: ../ukui_menu/plugins/menu.py:2347 msgid "Pluma" msgstr "" #: ../ukui_menu/plugins/menu.py:248 ../ukui_menu/plugins/menu.py:1286 #: ../ukui_menu/plugins/menu.py:1289 msgid "Games" msgstr "" #: ../ukui_menu/plugins/menu.py:253 ../ukui_menu/plugins/menu.py:1291 #: ../ukui_menu/plugins/menu.py:1294 ../ukui_menu/menueditor.py:1129 msgid "Android" msgstr "" #: ../ukui_menu/plugins/menu.py:258 ../ukui_menu/plugins/menu.py:1279 #: ../ukui_menu/plugins/menu.py:1282 msgid "Office" msgstr "" #: ../ukui_menu/plugins/menu.py:400 msgid "Recent" msgstr "" #: ../ukui_menu/plugins/menu.py:444 ../ukui_menu/plugins/menu.py:564 msgid "All App" msgstr "" #: ../ukui_menu/plugins/menu.py:449 ../ukui_menu/plugins/menu.py:553 msgid "Favorite" msgstr "" #: ../ukui_menu/plugins/menu.py:494 msgid "Power" msgstr "" #: ../ukui_menu/plugins/menu.py:575 ../ukui_menu/plugins/menu.py:945 #: ../ukui_menu/plugins/menu.py:953 ../ukui_menu/plugins/menu.py:1653 #: ../ukui_menu/plugins/menu.py:2435 msgid "Search local program" msgstr "" #: ../ukui_menu/plugins/menu.py:605 msgid "Home" msgstr "" #: ../ukui_menu/plugins/menu.py:613 msgid "Settings" msgstr "" #: ../ukui_menu/plugins/menu.py:621 msgid "No items matched" msgstr "" #: ../ukui_menu/plugins/menu.py:898 ../ukui_menu/plugins/menu.py:2141 #: ../ukui_menu/plugins/menu.py:2154 ../ukui_menu/plugins/menu.py:2167 msgid "" "\n" "File not exists, it was moved or deleted!\n" "\n" "It will be automatically removed from the history list!\n" msgstr "" #: ../ukui_menu/plugins/menu.py:977 ../ukui_menu/plugins/menu.py:1732 #: ../ukui_menu/plugins/menu.py:2183 msgid "Open(_O)" msgstr "" #: ../ukui_menu/plugins/menu.py:980 ../ukui_menu/plugins/menu.py:1735 msgid "Add to desktop" msgstr "" #: ../ukui_menu/plugins/menu.py:984 ../ukui_menu/plugins/menu.py:1738 msgid "Lock to panel(_L)" msgstr "" #: ../ukui_menu/plugins/menu.py:987 msgid "Unlock from startup menu" msgstr "" #: ../ukui_menu/plugins/menu.py:991 ../ukui_menu/plugins/menu.py:1748 #: ../ukui_menu/plugins/menu.py:2193 msgid "Property(_P)" msgstr "" #: ../ukui_menu/plugins/menu.py:1057 msgid "Shutdown" msgstr "" #: ../ukui_menu/plugins/menu.py:1058 msgid "Reboot" msgstr "" #: ../ukui_menu/plugins/menu.py:1061 msgid "Logout" msgstr "" #: ../ukui_menu/plugins/menu.py:1062 msgid "Switch User" msgstr "" #: ../ukui_menu/plugins/menu.py:1063 msgid "Lock Screen" msgstr "" #: ../ukui_menu/plugins/menu.py:1277 msgid "Accessories" msgstr "" #: ../ukui_menu/plugins/menu.py:1284 msgid "StartUp" msgstr "" #: ../ukui_menu/plugins/menu.py:1327 ../ukui_menu/plugins/menu.py:1328 #: ../ukui_menu/plugins/menu.py:1596 ../ukui_menu/plugins/menu.py:1601 msgid "Applications" msgstr "" #: ../ukui_menu/plugins/menu.py:1332 ../ukui_menu/plugins/menu.py:1333 #: ../ukui_menu/plugins/menu.py:1598 ../ukui_menu/plugins/menu.py:1603 msgid "System" msgstr "" #: ../ukui_menu/plugins/menu.py:1741 msgid "Add to startup menu" msgstr "" #: ../ukui_menu/plugins/menu.py:1743 msgid "Uninstall" msgstr "" #: ../ukui_menu/plugins/menu.py:1744 ../ukui_menu/plugins/menu.py:2191 msgid "Remove from list(_R)" msgstr "" #: ../ukui_menu/plugins/menu.py:1746 msgid "Remove all" msgstr "" #: ../ukui_menu/plugins/menu.py:1931 msgid "Type:" msgstr "" #: ../ukui_menu/plugins/menu.py:1933 msgid "Name:" msgstr "" #: ../ukui_menu/plugins/menu.py:1935 msgid "Command:" msgstr "" #: ../ukui_menu/plugins/menu.py:1937 msgid "Note:" msgstr "" #: ../ukui_menu/plugins/menu.py:1946 msgid "Application" msgstr "" #: ../ukui_menu/plugins/menu.py:1948 msgid "Application in Terminal" msgstr "" #: ../ukui_menu/plugins/menu.py:2185 msgid "Open directory(_D)" msgstr "" #: ../ukui_menu/plugins/menu.py:2187 msgid "New(_N)" msgstr "" #: ../ukui_menu/plugins/menu.py:2189 msgid "Copy to desktop(_C)" msgstr "" #: ../ukui_menu/plugins/menu.py:2388 #, python-format msgid "Recent(%s)" msgstr "" #: ../ukui_menu/plugins/menu.py:2482 msgid "Chromium Browser" msgstr "" #: ../ukui_menu/plugins/menu.py:2488 msgid "Fcitx Config" msgstr "" #: ../ukui_menu/easybuttons.py:406 msgid "Comment:" msgstr "" #: ../ukui_menu/keybinding.py:219 msgid "Click to set a new accelerator key for opening and closing the menu. " msgstr "" #: ../ukui_menu/keybinding.py:220 msgid "Press Escape or click again to cancel the operation. " msgstr "" #: ../ukui_menu/keybinding.py:221 msgid "Press Backspace to clear the existing keybinding." msgstr "" #: ../ukui_menu/keybinding.py:234 msgid "Pick an accelerator" msgstr "" #: ../ukui_menu/keybinding.py:286 msgid "" msgstr "" #: ../ukui_menu/menueditor.py:782 #, python-format msgid "" "\n" "The current system is not selected [%s] software, can not display the " "classification!" msgstr "" #: ../ukui_menu/menueditor.py:788 #, python-format msgid "" "\n" "The current system is not installed [%s] software, can not display the " "classification!" msgstr "" #: ../ukui_menu/menueditor.py:884 msgid "Restore the default menu layout" msgstr "" #: ../ukui_menu/menueditor.py:885 msgid "Menus:" msgstr "" #: ../ukui_menu/menueditor.py:886 msgid "Items:" msgstr "" #: ../ukui_menu/menueditor.py:887 msgid "Move Up" msgstr "" #: ../ukui_menu/menueditor.py:888 msgid "Move Down" msgstr "" #: ../ukui_menu/menueditor.py:889 msgid "Revert all menus to original settings?" msgstr "" #: ../ukui_menu/menueditor.py:890 msgid "Revert Changes?" msgstr "" #: ../ukui_menu/menueditor.py:1075 msgid "Name" msgstr "" #: ../ukui_menu/menueditor.py:1091 msgid "Show" msgstr "" #: ../ukui_menu/menueditor.py:1099 msgid "Item" msgstr "" #: ../ukui_menu/menueditor.py:1130 #, python-format msgid "" "\n" "[%s] is a system autostart service software, you should not show it in " "startup menu!" msgstr "" ukui-menu/po/zh_CN.po0000664000175000017500000002465713265322640013417 0ustar fengfeng# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-03-01 16:54+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../ukui-menu-editor:38 msgid "" "\n" "Current menu mode is not category mode, please switch and try again!" msgstr "\n当前菜单模式不是分类模式,请切换后再试!" #: ../lib/ukui-menu.py:60 msgid "Menu" msgstr "菜单" #: ../lib/ukui-menu.py:269 ../lib/ukui-menu.py:399 msgid "Normal Menu" msgstr "常规菜单" #: ../lib/ukui-menu.py:271 ../lib/ukui-menu.py:401 msgid "Category Menu" msgstr "分类菜单" #. self.tree.get_object('edit_revert_to_original').set_label(_("Revert to Original")) #: ../lib/ukui-menu.py:273 ../ukui_menu/menueditor.py:872 msgid "Edit Category Menu" msgstr "编辑分类菜单" #: ../lib/ukui-menu.py:349 msgid "Start" msgstr "开始" #: ../lib/ukui-menu.py:379 msgid "Startup Menu" msgstr "开始菜单" #: ../lib/ukui-menu.py:391 msgid "Menu Property" msgstr "菜单属性" #: ../lib/ukui-menu.py:393 msgid "Menu Type:" msgstr "菜单类型:" #: ../lib/ukui-menu.py:394 msgid "This is used to set up the menu type." msgstr "该项用来设置菜单类型。" #: ../lib/ukui-menu.py:396 msgid "File History:" msgstr "文件历史:" #: ../lib/ukui-menu.py:397 msgid "This is used to set up whether the file history is displayed." msgstr "该项用来设置是否显示文件历史记录。" #: ../lib/ukui-menu.py:417 msgid "Recent file number:" msgstr "历史文件个数:" #: ../lib/ukui-menu.py:419 msgid "Recent app number:" msgstr "历史应用个数:" #: ../lib/ukui-menu.py:434 msgid "Reset to default(_R)" msgstr "重置为默认值(_R)" #: ../lib/ukui-menu.py:441 msgid "Close(_C)" msgstr "关闭(_C)" #: ../lib/ukui-menu.py:578 msgid "Reload plugins" msgstr "重载插件" #: ../lib/ukui-menu.py:581 ../ukui_menu/plugins/menu.py:1966 msgid "Property" msgstr "属性" #. wps #: ../ukui_menu/plugins/menu.py:125 ../ukui_menu/plugins/menu.py:739 #: ../ukui_menu/plugins/menu.py:911 ../ukui_menu/plugins/menu.py:2203 #: ../ukui_menu/plugins/menu.py:2311 msgid "WPS Writer" msgstr "WPS 文字" #: ../ukui_menu/plugins/menu.py:126 ../ukui_menu/plugins/menu.py:743 #: ../ukui_menu/plugins/menu.py:913 ../ukui_menu/plugins/menu.py:2203 #: ../ukui_menu/plugins/menu.py:2318 msgid "WPS Presentation" msgstr "WPS 演示" #: ../ukui_menu/plugins/menu.py:127 ../ukui_menu/plugins/menu.py:747 #: ../ukui_menu/plugins/menu.py:915 ../ukui_menu/plugins/menu.py:2203 #: ../ukui_menu/plugins/menu.py:2325 msgid "WPS Spreadsheets" msgstr "WPS 表格" #. qt app #: ../ukui_menu/plugins/menu.py:128 ../ukui_menu/plugins/menu.py:765 #: ../ukui_menu/plugins/menu.py:917 ../ukui_menu/plugins/menu.py:2295 msgid "Qt Creator" msgstr "" #. qt app #: ../ukui_menu/plugins/menu.py:129 ../ukui_menu/plugins/menu.py:2280 msgid "SMPlayer" msgstr "" #: ../ukui_menu/plugins/menu.py:130 ../ukui_menu/plugins/menu.py:773 #: ../ukui_menu/plugins/menu.py:919 ../ukui_menu/plugins/menu.py:2282 msgid "Kylin Video" msgstr "麒麟影音" #. gtk app #: ../ukui_menu/plugins/menu.py:131 ../ukui_menu/plugins/menu.py:756 #: ../ukui_menu/plugins/menu.py:921 ../ukui_menu/plugins/menu.py:2333 msgid "Eye of MATE Image Viewer" msgstr "MATE 之眼图像查看器" #: ../ukui_menu/plugins/menu.py:132 ../ukui_menu/plugins/menu.py:760 #: ../ukui_menu/plugins/menu.py:923 ../ukui_menu/plugins/menu.py:2340 msgid "Atril Document Viewer" msgstr "Atril 文档查看器" #. gtk app #: ../ukui_menu/plugins/menu.py:133 ../ukui_menu/plugins/menu.py:752 #: ../ukui_menu/plugins/menu.py:925 ../ukui_menu/plugins/menu.py:2203 #: ../ukui_menu/plugins/menu.py:2347 msgid "Pluma" msgstr "" #: ../ukui_menu/plugins/menu.py:245 ../ukui_menu/plugins/menu.py:1294 #: ../ukui_menu/plugins/menu.py:1297 msgid "Games" msgstr "游戏" #: ../ukui_menu/plugins/menu.py:250 ../ukui_menu/plugins/menu.py:1299 #: ../ukui_menu/plugins/menu.py:1302 ../ukui_menu/menueditor.py:1118 msgid "Android" msgstr "安卓兼容" #: ../ukui_menu/plugins/menu.py:255 ../ukui_menu/plugins/menu.py:1287 #: ../ukui_menu/plugins/menu.py:1290 msgid "Office" msgstr "办公" #: ../ukui_menu/plugins/menu.py:397 msgid "Recent" msgstr "最近" #: ../ukui_menu/plugins/menu.py:451 ../ukui_menu/plugins/menu.py:571 msgid "All App" msgstr "所有程序" #: ../ukui_menu/plugins/menu.py:456 ../ukui_menu/plugins/menu.py:560 msgid "Favorite" msgstr "常用软件" #: ../ukui_menu/plugins/menu.py:501 msgid "Power" msgstr "关机" #: ../ukui_menu/plugins/menu.py:582 ../ukui_menu/plugins/menu.py:952 #: ../ukui_menu/plugins/menu.py:960 ../ukui_menu/plugins/menu.py:1661 #: ../ukui_menu/plugins/menu.py:2435 msgid "Search local program" msgstr "搜索本地程序" #: ../ukui_menu/plugins/menu.py:612 msgid "Home" msgstr "我的电脑" #: ../ukui_menu/plugins/menu.py:620 msgid "Settings" msgstr "控制面板" #: ../ukui_menu/plugins/menu.py:628 msgid "No items matched" msgstr "没有与搜索条件匹配的项" #: ../ukui_menu/plugins/menu.py:905 ../ukui_menu/plugins/menu.py:2150 #: ../ukui_menu/plugins/menu.py:2163 msgid "" "\n" "File not exists, it was moved or deleted!\n" "\n" "It will be automatically removed from the history list!\n" msgstr "\n文件不存在,它已经被移动或删除!\n\n它将被自动从历史列表中删除!\n" #: ../ukui_menu/plugins/menu.py:984 ../ukui_menu/plugins/menu.py:1740 #: ../ukui_menu/plugins/menu.py:2182 msgid "Open(_O)" msgstr "打开(_O)" #: ../ukui_menu/plugins/menu.py:987 ../ukui_menu/plugins/menu.py:1743 msgid "Add to desktop" msgstr "添加到桌面" #: ../ukui_menu/plugins/menu.py:991 ../ukui_menu/plugins/menu.py:1746 msgid "Lock to panel(_L)" msgstr "锁定到任务栏(_L)" #: ../ukui_menu/plugins/menu.py:994 msgid "Unlock from startup menu" msgstr "从[开始菜单]解锁" #: ../ukui_menu/plugins/menu.py:998 ../ukui_menu/plugins/menu.py:1756 #: ../ukui_menu/plugins/menu.py:2192 msgid "Property(_P)" msgstr "属性(_P)" #: ../ukui_menu/plugins/menu.py:1065 msgid "Shutdown" msgstr "关机" #: ../ukui_menu/plugins/menu.py:1066 msgid "Reboot" msgstr "重启" #: ../ukui_menu/plugins/menu.py:1069 msgid "Logout" msgstr "注销" #: ../ukui_menu/plugins/menu.py:1070 msgid "Switch User" msgstr "切换用户" #: ../ukui_menu/plugins/menu.py:1071 msgid "Lock Screen" msgstr "锁屏" #: ../ukui_menu/plugins/menu.py:1285 msgid "Accessories" msgstr "附件" #: ../ukui_menu/plugins/menu.py:1292 msgid "StartUp" msgstr "开始" #: ../ukui_menu/plugins/menu.py:1335 ../ukui_menu/plugins/menu.py:1336 #: ../ukui_menu/plugins/menu.py:1604 ../ukui_menu/plugins/menu.py:1609 msgid "Applications" msgstr "应用程序" #: ../ukui_menu/plugins/menu.py:1340 ../ukui_menu/plugins/menu.py:1341 #: ../ukui_menu/plugins/menu.py:1606 ../ukui_menu/plugins/menu.py:1611 msgid "System" msgstr "系统" #: ../ukui_menu/plugins/menu.py:1749 msgid "Add to startup menu" msgstr "附到[开始菜单]" #: ../ukui_menu/plugins/menu.py:1751 msgid "Uninstall" msgstr "卸载" #: ../ukui_menu/plugins/menu.py:1752 ../ukui_menu/plugins/menu.py:2190 msgid "Remove from list(_R)" msgstr "从列表中删除(_R)" #: ../ukui_menu/plugins/menu.py:1754 msgid "Remove all" msgstr "删除所有" #: ../ukui_menu/plugins/menu.py:1940 msgid "Type:" msgstr "类型:" #: ../ukui_menu/plugins/menu.py:1942 msgid "Name:" msgstr "名字:" #: ../ukui_menu/plugins/menu.py:1944 msgid "Command:" msgstr "命令:" #: ../ukui_menu/plugins/menu.py:1946 msgid "Note:" msgstr "注释:" #: ../ukui_menu/plugins/menu.py:1955 msgid "Application" msgstr "应用程序" #: ../ukui_menu/plugins/menu.py:1957 msgid "Application in Terminal" msgstr "终端应用" #: ../ukui_menu/plugins/menu.py:2184 msgid "Open directory(_D)" msgstr "打开目录(_D)" #: ../ukui_menu/plugins/menu.py:2186 msgid "New(_N)" msgstr "新建(_N)" #: ../ukui_menu/plugins/menu.py:2188 msgid "Copy to desktop(_C)" msgstr "复制到桌面(_C)" #: ../ukui_menu/plugins/menu.py:2388 #, python-format msgid "Recent(%s)" msgstr "最近(%s)" #: ../ukui_menu/plugins/menu.py:2482 msgid "Chromium Browser" msgstr "谷歌浏览器" #: ../ukui_menu/plugins/menu.py:2488 msgid "Fcitx Config" msgstr "小企鹅配置" #: ../ukui_menu/easybuttons.py:406 msgid "Comment:" msgstr "描述:" #: ../ukui_menu/keybinding.py:215 msgid "Click to set a new accelerator key for opening and closing the menu. " msgstr "" #: ../ukui_menu/keybinding.py:216 msgid "Press Escape or click again to cancel the operation. " msgstr "" #: ../ukui_menu/keybinding.py:217 msgid "Press Backspace to clear the existing keybinding." msgstr "" #: ../ukui_menu/keybinding.py:230 msgid "Pick an accelerator" msgstr "" #: ../ukui_menu/keybinding.py:282 msgid "" msgstr "" #: ../ukui_menu/menueditor.py:782 #, python-format msgid "" "\n" "The current system is not selected [%s] software, can not display the " "classification!" msgstr "\n当前系统未勾选[%s]类软件,无法显示该分类!" #: ../ukui_menu/menueditor.py:788 #, python-format msgid "" "\n" "The current system is not installed [%s] software, can not display the " "classification!" msgstr "\n当前系统未安装[%s]类软件,无法显示该分类!" #: ../ukui_menu/menueditor.py:873 msgid "Restore the default menu layout" msgstr "恢复默认菜单布局" #: ../ukui_menu/menueditor.py:874 msgid "Menus:" msgstr "菜单:" #: ../ukui_menu/menueditor.py:875 msgid "Items:" msgstr "项数:" #: ../ukui_menu/menueditor.py:876 msgid "Move Up" msgstr "上移" #: ../ukui_menu/menueditor.py:877 msgid "Move Down" msgstr "下移" #: ../ukui_menu/menueditor.py:878 msgid "Revert all menus to original settings?" msgstr "将全部菜单还原到原始设置吗?" #: ../ukui_menu/menueditor.py:879 msgid "Revert Changes?" msgstr "还原更改吗?" #: ../ukui_menu/menueditor.py:1064 msgid "Name" msgstr "名称" #: ../ukui_menu/menueditor.py:1080 msgid "Show" msgstr "显示" #: ../ukui_menu/menueditor.py:1088 msgid "Item" msgstr "项目" #: ../ukui_menu/menueditor.py:1119 #, python-format msgid "" "\n" "[%s] is a system autostart service software, you should not show it in " "startup menu!" msgstr "\n[%s]是一个系统自启动服务软件,你不应该把它显示到开始菜单中!" ukui-menu/setup.py0000775000175000017500000001323313254644467013155 0ustar fengfeng#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2015 by Mike Gabriel # Copyright: 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os import sys from glob import glob from setuptools import setup import DistUtilsExtra.command.build_extra import DistUtilsExtra.command.build_i18n import DistUtilsExtra.command.clean_i18n # to update i18n .mo files (and merge .pot file into .po files) run on Linux: # ,,python setup.py build_i18n -m'' # silence pyflakes, __VERSION__ is properly assigned below... __VERSION__ = '0.0.0.0' for line in open('lib/ukui-menu.py').readlines(): if (line.startswith('__VERSION__')): exec(line.strip()) break PROGRAM_VERSION = __VERSION__ def datafilelist(installbase, sourcebase): datafileList = [] for root, subFolders, files in os.walk(sourcebase): fileList = [] for f in files: fileList.append(os.path.join(root, f)) datafileList.append((root.replace(sourcebase, installbase), fileList)) return datafileList data_files = [ ('{prefix}/share/man/man1'.format(prefix=sys.prefix), glob('data/*.1')), ('{prefix}/share/glib-2.0/schemas'.format(prefix=sys.prefix), glob('data/org.ukui.ukui-menu.gschema.xml'), ), ('{prefix}/share/ukui-panel/applets'.format(prefix=sys.prefix), ['data/org.ukui.panel.UkuiMenuApplet.ukui-panel-applet']), ('{prefix}/share/dbus-1/services'.format(prefix=sys.prefix), ['data/org.ukui.panel.applet.UkuiMenuAppletFactory.service']), ('{prefix}/share/ukui-menu'.format(prefix=sys.prefix), ['data/ukui-menu.glade', 'data/menu-property.glade', 'data/menueditor.ui', 'data/popup.xml', 'data/ukuimenu.css', 'data/applications.list', ], ), ('{prefix}/share/ukui-menu/plugins'.format(prefix=sys.prefix), ['data/plugins/ukuimenu.glade', 'data/plugins/property.glade', ], ), ('{prefix}/share/ukui-menu/icons'.format(prefix=sys.prefix), ['data/icons/ukui-logo.svg', 'data/icons/allapp-active.png', 'data/icons/allapp.png', 'data/icons/arrow-left.png', 'data/icons/arrow-right-line.png', 'data/icons/arrow-right.png', 'data/icons/computer-active.png', 'data/icons/computer.png', 'data/icons/controlcenter-active.png', 'data/icons/controlcenter.png', 'data/icons/favapp-active.png', 'data/icons/favapp.png', 'data/icons/sep.png', 'data/icons/shutdown.svg', 'data/icons/so-close.png', 'data/icons/so.png', 'data/icons/start.svg', 'data/icons/stock_person.png', ], ), ('{prefix}/lib/ukui-menu'.format(prefix=sys.prefix), glob('lib/*')), ] data_files.extend(datafilelist('{prefix}/share/locale'.format(prefix=sys.prefix), 'build/mo')) cmdclass ={ "build" : DistUtilsExtra.command.build_extra.build_extra, "build_i18n" : DistUtilsExtra.command.build_i18n.build_i18n, "clean": DistUtilsExtra.command.clean_i18n.clean_i18n, } setup( name = "ukui-menu", version = PROGRAM_VERSION, description = "An advanced menu for UKUI. Supports filtering, favorites, autosession, and many other features.", license = 'GPLv2+', author = 'Huan Peng', url = 'https://github.com/ukui/ukui-menu', packages = [ 'ukui_menu', 'ukui_menu.plugins', ], package_dir = { '': '.', }, data_files = data_files, install_requires = [ 'setuptools', ], scripts = ['ukui-menu', 'ukui-menu-editor'], cmdclass = cmdclass, ) ukui-menu/data/0000755000175000017500000000000013257102075012330 5ustar fengfengukui-menu/data/menu-property.glade0000664000175000017500000002667213254644467016210 0ustar fengfeng 1 30 10 1 10 1 30 10 1 10 230 400 False center-always True False 15 vertical 21 True False 15 True False 菜单类型: 0 False True 0 常规菜单 True True False True True radiobutton2 False True 1 分类菜单 True True False True True False True 10 2 False True 0 True False 15 True False 文件历史: 0 False True 0 True True 4 False True 1 False True 1 True False 15 True False 历史文件个数: 0 False True 0 True True 4 2 1 number adjustment1 0.01 True if-valid 10 False True 1 False True 2 True False 15 True False 历史应用个数: 0 False True 0 True True 4 2 0 1 number adjustment2 0.01 True if-valid False True 1 False True 3 True False none button True True True 200 20 15 False True end 4 True False none button True True True 15 171 False True end 5 ukui-menu/data/ukuimenu.css0000664000175000017500000001201013257102075014700 0ustar fengfeng@define-color ukui_bg_color #f5f5f5; @define-color ukui_fg_color #000000; @define-color ukui_tooltip_bg_color #fafafa; @define-color ukui_tooltip_fg_color #000000; @define-color ukui_text_color #000000; @define-color ukui_vp_color #ffffff; @define-color ukui_vp_border_color #bbbbbb; @define-color ukuimenu_color #3b9dc5; @define-color ukuiside_color #155670; #Window { background-color: @ukuimenu_color; border-color: @ukuiside_color; font-size: 13px; } #EventBox { background-color: @ukui_bg_color; font-size: 13px; } #ViewportNull { background-color: transparent; border-width: 1px; border-color: transparent; font-size: 13px; } #ViewportButton { background-color: @ukui_vp_color; border-width: 1px; border-style: solid; border-radius: 0px; border-left-color: @ukui_vp_border_color; border-right-color: @ukui_vp_color; border-top-color: @ukui_vp_border_color; border-bottom-color: @ukui_vp_border_color; font-size: 13px; } #Viewport { padding: 0px 0px; background-color: @ukui_vp_color; border-width: 1px; border-style: solid; border-radius: 0px; border-color: @ukui_vp_border_color; font-size: 13px; } #Viewport1 { padding: 0px 0px; background-color: @ukui_vp_color; border-width: 0px; border-style: solid; border-radius: 0px; border-color: @ukui_vp_border_color; font-size: 13px; } #ViewportEntry { padding: 0px 0px; border-radius: 0px; background-color: @ukui_vp_color; border-width: 1px; border-color: shade (@ukuimenu_color, 0.90); color: @ukui_fg_color; font-size: 13px; } #ViewportPower { background-color: @ukui_vp_color; border-width: 1px; border-style: solid; border-radius: 0px; border-color: shade (@ukuimenu_color, 0.90); font-size: 13px; } #ViewportPowerEnter { background-color: shade (@ukuimenu_color, 0.90); border-width: 1px; border-style: solid; border-radius: 0px; border-color: shade (@ukuimenu_color, 1.05); font-size: 13px; } #Viewport1 { background-color: @ukui_vp_color; border-style: solid; border-radius: 0px; border-width: 0px; font-size: 13px; } #Button { border: 1px solid transparent; border-radius: 0px; background-color: transparent; font-size: 13px; } #Button:hover { border-radius: 0px; border-width: 1px; background-image: none; background-color: #f8f8f9; border-color: #b3b3b3; } #Button:active { border-radius: 0px; border-width: 1px; border-image-source: none; border-color: @ukui_vp_border_color; } #ButtonPower { min-width: 18px; padding: 0px 0px; background-color: transparent; border-width: 0px; border-color: transparent; font-size: 13px; } #ButtonPower:hover { background-color: #daebfc; background-image: none; border: 0px transparent; } #ButtonPower:active { background-color: #daebfc; background-image: none; border: 0px transparent; } #ButtonApp { padding: 2px 2px; border-radius: 0px; border-width: 1px; background-color: transparent; font-size: 13px; } #ButtonApp:hover { border-radius: 0px; background-image: none; background-color: shade (@ukuimenu_color, 1.89); border-color: @ukui_vp_border_color; border-width: 1px; border-image-source: none; } #ButtonApp:hover:focus { border-radius: 0px; border-width: 1px; background-image: none; background-color: #daebfc; } #ButtonAppHover { padding: 2px 2px; border-radius: 0px; border-style: solid; border-width: 1px; border-color: shade (@ukuimenu_color, 1.35); background-image: none; background-color: shade (@ukuimenu_color, 1.89); font-size: 13px; } #ButtonApp:active { border-radius: 0px; border-width: 1px; background-image: none; background-color: shade (@ukuimenu_color, 1.73); border-color: shade (@ukuimenu_color, 1.35); } #ButtonApp:disabled { color: @ukui_fg_color; text-shadow: none; border-width: 1px; background-color: shade (@ukuimenu_color, 1.73); border-color: shade (@ukuimenu_color, 1.35); } #ButtonApp:active:hover { border-radius: 0px; border-width: 1px; background-image: none; border-image-source: none; background-color: shade (@ukuimenu_color, 1.73); } #Entry { border-radius: 0px; padding: 0px 0px; background-color: @ukui_vp_color; border: 0px transparent; color: #999999; font-size: 13px; } #EntryActive { border-radius: 0px; padding: 0px 0px; background-color: @ukui_vp_color; border: 0px transparent; color: @ukui_fg_color; font-size: 13px; } tooltip, tooltip.background, .tooltip, .tooltip.background { padding: 4px; border-style: solid; border-width: 1px; border-color: @ukui_vp_border_color; border-radius: 0px; background-color: @ukui_tooltip_bg_color; color: @ukui_tooltip_fg_color; text-shadow: none; } #Button label:disabled { color: @ukui_fg_color; text-shadow: none; box-shadow: none; } #ButtonApplet { padding: 0px 0px 0px 0px; border: 0px transparent; border-radius: 0px; background-image: none; background-color: transparent; box-shadow:none; -gtk-icon-shadow: none; } #ButtonApplet:hover { -gtk-icon-shadow: 1px 1px black; -gtk-icon-effect: highlight; } #ButtonClicked { padding: 0px 0px 0px 0px; border: 0px transparent; border-radius: 0px; background-image: none; background-color: rgba(0, 0, 0, 0.2); box-shadow: none; -gtk-icon-shadow: none; } #ButtonClicked:hover { -gtk-icon-shadow: 1px 1px black; -gtk-icon-effect: highlight; } ukui-menu/data/org.ukui.ukui-menu.gschema.xml0000664000175000017500000000353113245137611020146 0ustar fengfeng ['menu'] plugin list default plugin list here true The state of user icon This indicates the state of user icon true The state of recent opened file This indicates the state of recent opened file true The permission state of recent opened file This indicates the permission state of recent opened file false current menu state This indicates the state of the menu false determine if show recent file or not determine if show recent file or not 10 determine the numbers of recent file showed determine the numbers of recent file showed 10 determine the numbers of recent applications showed determine the numbers of recent applications showed ukui-menu/data/plugins/0000755000175000017500000000000013245137611014012 5ustar fengfengukui-menu/data/plugins/ukuimenu.glade0000664000175000017500000021024413245137611016657 0ustar fengfeng True False applications-other True False 4 go-previous True False computer True False applications-system True False applications-other True True False 4 go-next True False system-shutdown-symbolic True False 10 gtk-media-play-ltr True False 63 computer False False 1 True False 1 102 97 True False 1 101 30 True False Ukui center -1 68 73 73 True True True image_user 12 3 5 96 38 True False 我的电脑 96 37 True False True image_computer none 3 238 96 38 True False 控制面板 96 37 True False True image_controlcenter none 3 285 236 455 True False 236 455 True False 236 455 True False 0 227 449 True False 0 227 449 True True False False True False 0 True True 0 never never False True False True False True False True 1 0 True False 100 0 True False gtk-missing-image False True 1 True False True True 0 never False True False True False True False True True 0 True True 1 True False 所有软件 28 True True True image_showall none 0 False True end 1 True False 100 0 True False gtk-missing-image False False 2 2 True True 2 2 True False 0 True True False True False True False start 2 True True True True True False 4 False False 195 0 True True True expander 0 True True 0 True True True True True False 4 False False 195 True False True expander 0 True True 1 True True 1 0 True False 100 0 True False gtk-missing-image False False 1 True False 常用软件 28 True True True image_back none 0 False True 2 2 1 True True 0 False True False True False True False 没有与搜索条件匹配的项 True True 1 2 4 3 103 -1 True False True False 0 233 455 False 0 227 449 True False 0 True True 0 False True False 0 True False 0 True True True True True False 4 208 True False 4 True False aaaaa False True 0 1 True False center 3 True vertical False True 1 True True 0 4 4 338 -1 60 26 True False 关 机 70 24 True True True image_shutdown none 467 13 26 True False 13 24 True False True image_shutdown1 none 74 467 100 6 True False gtk-missing-image 228 102 37 True False 96 37 True False 常用软件 96 37 True True False image_favapp none 3 138 102 37 True False 96 37 True False start 所有软件 96 37 True False True image_allapp none 3 185 228 24 True False 228 24 True True False True none 0 edit-find False 103 466 ukui-menu/data/plugins/property.glade0000644000175000017500000002503313110762006016670 0ustar fengfeng True False dialog-close 413 210 False 属性 applications-utilities dialog 400 210 True False 2 40 True False end 关闭(C) True True True image1 False False 1 False False end 0 0 170 True False 0 0 64 64 True False True False gtk-missing-image 10 15 69 28 True False 类型(T): 88 15 69 28 True False 名称(N): 88 50 69 28 True False 命令(A): 88 90 69 28 True False 注释(M): 88 125 245 28 True True False False False True True 159 15 245 28 True True False True False False True True 159 50 245 28 True True False True False False True True 159 90 245 28 True True False True False False True True 159 125 False False 1 button_close ukui-menu/data/icons/0000755000175000017500000000000013254644467013460 5ustar fengfengukui-menu/data/icons/ukui-logo.svg0000644000175000017500000000601513110762006016073 0ustar fengfeng ukui-menu/data/icons/allapp.png0000644000175000017500000000176013110762006015420 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp "{eIDATxSA 0[M6ۡQ(BUkU_'Bf>bzr8}p@Jd+2\mAݝe`gTIENDB`ukui-menu/data/icons/favapp.png0000644000175000017500000000224513110762006015423 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp z%IDATxڜ=@w7^ΖĚpZxނY0K}ovѾ A<[rpW[X3s W4*kp0y Gp/r㷘=JWU Fe:$IT*R2B10Lܢ:bv.^Â]z.jNaqr5|3p1"x\Ym:RY(L/#~[;Va$}obRp0΀E{u^V^0ɟԞarR6I2L01 O`@kUjNIENDB`ukui-menu/data/icons/computer-active.png0000644000175000017500000000171113110762006017252 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp XW>IDATxb?%y3E&@iF2gb0jrJOBIj^f+.10 ehCLIENDB`ukui-menu/data/icons/stock_person.png0000644000175000017500000000350613110762006016660 0ustar fengfengPNG  IHDR@@% tEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp S_IDATxOQg@[-S `XF-0ڨ,Q*F㋾cHb4(`\hhB`% ʹCLxsi{{~̽wΝVŸ#\pJQ,>W$ɓTLDI٥!beyp$ƃ,%FvPSp<#]ƹiǂuwZ V))ѢhUAN>qn.]|ldRF);p*8CE2TTɜMSz`H'|L.wt]Rz5Wy0_pu{6O8k`4-MIENDB`ukui-menu/data/icons/controlcenter-active.png0000644000175000017500000000225613110762006020302 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp *J#IDATxӱO@% LYp??7!)L$q09nE |~5%& \Iw{MygX%$=G"6#L+% |QÙ4H:.1*k^9Q<&D= m-pbOknCY?z]kf `m*/-CEUϮUŷx+TwvJll \׆ZU‰x5;7M%j凘xd}lNo\ 0*DqK;IIENDB`ukui-menu/data/icons/icon.png0000644000175000017500000000205613110762006015076 0ustar fengfengPNG  IHDRĴl;sRGBbKGD pHYs B(xtIME#2XIDAT8U[hUffg2{Iv&eI6M&mH(Ԫ j_!T,}UbL/bj4xdcl63{n3>Bl.9ې zc& sߞV;[;;uX}WƳ?Y6>jxf@(u|d0p0%S~Y ދ>iSУykuf_K)v*|e#|v뺆i/jtKB)9]ua8/L_ˮXvОֵ8`.WzHyJV$PjvԞtqoTѮjr>YC\֋g SH#<R)Ne\" @`e di뛿d+U,m2|Y-2*%eFٰQ6a}[s"gEOal.lx ' SKSe\0s0̵ī?3 *Y&:}!\7atH N2ru(Wr]zJF#Ł_A.ETS*'8ќxĥg0/ѵx p3+Y˲}xAnHB `Z%]nR6J{R~b"t*S% GBPqHtCrTfֿjƏ8?!ƢhIENDB`ukui-menu/data/icons/allapp-active.png0000644000175000017500000000173713110762006016675 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp ? fTIDATxb?%RX7Sd %d@!0÷Z_ yXq7glh'$s#@ߨS;IENDB`ukui-menu/data/icons/arrow-right.png0000644000175000017500000000206613110762006016414 0ustar fengfengPNG  IHDR15;tEXtSoftwareAdobe ImageReadyqe<iiTXtXML:com.adobe.xmp )cIDATxblhhf``p L@\Ğ@ya Gcb 0Hj+HpTQ1YD3qI%)IENDB`ukui-menu/data/icons/computer.png0000644000175000017500000000174313110762006016006 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp yUXIDATxb?%%=="X4#31P`.`9s&ɚg `?(N5_2O(3ˍHWJ0܉b@7IENDB`ukui-menu/data/icons/start.svg0000664000175000017500000000172113254644467015341 0ustar fengfeng 画板 1 ukui-menu/data/icons/dotted.png0000644000175000017500000000031413110762006015424 0ustar fengfengPNG  IHDR8bKGD pHYs  tIME lQtEXtCommentCreated with The GIMPd%n0IDATH1 0 Oq?N)|빒|^'c 0 0 0 7{ %IENDB`ukui-menu/data/icons/favapp-active.png0000644000175000017500000000223413110762006016672 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp (ZIDATxڜ1 @E7cֶm+AOWH,l IDATxڤӱNPր'W@bĉpagE!aUF,t`wg NP|1piC's_\a8v˚=KgV.B\3T̋Y>fo*Acͯp',w<+|ffTc;` 9Wh#IDATxӱO@L 0Ȩ#@LXƤe"1]&v&vc.8P{+4-1qO{m+yΞMK8ی,.0ŧL5R+qJ\7abU0ܵrJ;>6Pǭ*X;6FFK5soіM, '(f}v=4_b9Gh*wwvWJll `kVPLךxZ*&[7~ 0UDEIENDB`ukui-menu/data/icons/shutdown.svg0000644000175000017500000000215013110762006016027 0ustar fengfengPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp XIDATxb?%B׀ LS0h .y$gΜe@lBCFl^be ǀ@ Ua@/<Zj1 CDA 6f+ €@̍̀+@b@ XF,y+8rlLb] 'YX$obk grPiC|Õ) ~ Q 6adLb /wY_eIENDB`ukui-menu/data/icons/arrow-right-line.png0000644000175000017500000000172413110762006017341 0ustar fengfengPNG  IHDR ]tEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp .fIIDATxlA }8X08uOLBfFY@U%~r+ARgiEs2UYձ- 0SIENDB`ukui-menu/data/icons/arrow-left.png0000644000175000017500000000206113110762006016224 0ustar fengfengPNG  IHDR15;tEXtSoftwareAdobe ImageReadyqe<iiTXtXML:com.adobe.xmp 2t^IDATxblhh`^ ^ ĭ,Hہ3  8 {Oab@0Hj H3TQ U0 O mXC+IENDB`ukui-menu/data/icons/sep.png0000644000175000017500000000164313110762006014736 0ustar fengfengPNG  IHDR tEXtSoftwareAdobe ImageReadyqe<!iTXtXML:com.adobe.xmp DIDATxb .ITIDATxb?%B0 ;w&V cbdF\M!61@$:tbY#.㌅ 0mCIENDB`ukui-menu/data/menueditor.ui0000664000175000017500000006556613237775114015076 0ustar fengfeng True False 600 530 True False 5 Main Menu center normal True False 2 True False end gtk-revert-to-saved True True True False Restore the default menu layout True False False 1 gtk-close True True True True False True False False 2 False True end 0 True False 5 False 6 True True 200 True False 6 True False _Menus: True menu_tree True 0 False False 0 True True in True True True False True True 1 False True True False 6 True False It_ems: True item_tree True 0 False False 0 True False 6 True True in True True True True True 0 True False 6 True False 12 True False 6 start True True True False True False 0 0 True False 2 True False gtk-go-up False False 0 True False Move Up True False False 1 False False 0 True True True False True False 0 0 True False 2 True False gtk-go-down False False 0 True False Move Down True False False 1 False False 1 True True 1 False True 1 True True 1 True True True True 0 True True 1 revert_button close_button False 5 Revert Changes? False dialog True False 2 True False end gtk-cancel True True True False True False False 0 gtk-revert-to-saved True True True False True False False 1 False True end 0 True False 5 8 True False 0 gtk-dialog-question 6 False True 0 True False Revert all menus to original settings? 0 0 True True 1 True True 1 cancel_revert_button button2 ukui-menu/data/org.ukui.panel.UkuiMenuApplet.ukui-panel-applet0000664000175000017500000000043413237775114023372 0ustar fengfeng[Applet Factory] Id=UkuiMenuAppletFactory InProcess=false Location=/usr/lib/ukui-menu/ukui-menu.py Name=UKUI Menu Applet Factory Description=Advanced UKUI Menu [UkuiMenuApplet] Name=UKUI Menu Description=Advanced UKUI Menu Icon=start-here UkuiComponentId=OAFIID:UKUI_UkuiMenuApplet; ukui-menu/data/org.ukui.panel.applet.UkuiMenuAppletFactory.service0000664000175000017500000000014613237775114024311 0ustar fengfeng[D-BUS Service] Name=org.ukui.panel.applet.UkuiMenuAppletFactory Exec=/usr/lib/ukui-menu/ukui-menu.py ukui-menu/data/applications.list0000644000175000017500000000006113110762006015702 0ustar fengfenglocation:/usr/share/applications/firefox.desktop ukui-menu/data/ukui-menu.10000644000175000017500000000076013110762006014326 0ustar fengfeng.TH ukui\-menu 1 "" "" .SH NAME ukui-menu \- Advanced menu for the UKUI desktop environment's panel .SH SYNOPSIS .B ukui-menu \fR[option] .SH DESCRIPTION An advanced menu for UKUI. Supports filtering, favorites, autosession, and many other features. .SH OPTIONS .TP .B [\-\-]help\fR,\fB [\-]h\fR,\fB [\-]? Display command line options. .TP .B [\-\-]clean\fR,\fB [\-\-]clear\fR,\fB [\-\-]reset Restore the UKUI Menu settings to default by deleting the settings file (\fI~/.config/ukui-menu/\fR). ukui-menu/data/ukui-menu.glade0000664000175000017500000000274613237775114015270 0ustar fengfeng False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK 1 False popup-menu True True False False True False True False True False ukui-menu/data/popup.xml0000664000175000017500000000040413237775114014225 0ustar fengfeng ukui-menu/data/ukui-menu-editor.10000664000175000017500000000011513237775114015624 0ustar fengfeng.TH ukui\-menu\-editor 1 "" "" .SH NAME ukui-menu-editor \- ukui menu editor ukui-menu/lib/0000755000175000017500000000000013267547200012171 5ustar fengfengukui-menu/lib/ukui-remove-application.py0000775000175000017500000001054413237775114017331 0ustar fengfeng#! /usr/bin/python3 # coding: utf-8 # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015-2016 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gdk, GObject try: import sys import os import subprocess import threading import tempfile import gettext import dbus except Exception as detail: print (detail) sys.exit(1) from subprocess import Popen # i18n gettext.install("ukui-menu", "/usr/share/locale") class RemoveExecuter(threading.Thread): def __init__(self, package): threading.Thread.__init__(self) self.package = package self.iface = self.init_dbus_ifaces() def run(self): removePackages = str.split(self.package) for pkg in removePackages: self.iface.remove(pkg) def init_dbus_ifaces(self): try: bus = dbus.SystemBus() except: print ("could not initiate dbus") return False try: obj = bus.get_object("com.ubuntukylin.softwarecenter","/") iface = dbus.Interface(obj, "com.ubuntukylin.softwarecenter") return iface except: print ("Get dbus failed") class ukuiRemoveWindow: def __init__(self, desktopFile): self.desktopFile = desktopFile (status, output) = subprocess.getstatusoutput("dpkg -S " + self.desktopFile) package = output[:output.find(":")] if status != 0: warnDlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, _("This application has been removed. Are you sure remove the menu form the startup menu?")) warnDlg.vbox.set_spacing(10) response = warnDlg.run() if response == Gtk.ResponseType.YES : print (("removing '%s'" % self.desktopFile)) os.system("rm -f '%s'" % self.desktopFile) os.system("rm -f '%s.desktop'" % self.desktopFile) warnDlg.destroy() sys.exit(0) warnDlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("The following packages will be removed:")) warnDlg.vbox.set_spacing(10) treeview = Gtk.TreeView() column1 = Gtk.TreeViewColumn(_("Packages will be removed")) renderer = Gtk.CellRendererText() column1.pack_start(renderer, False) column1.add_attribute(renderer, "text", 0) treeview.append_column(column1) model = Gtk.ListStore(str) dependenciesString = subprocess.getoutput("apt-get -s -q remove " + package + " | grep Remv") dependencies = str.split(dependenciesString, "\n") for dependency in dependencies: dependency = dependency.replace("Remv ", "") model.append([dependency]) treeview.set_model(model) treeview.show() scrolledwindow = Gtk.ScrolledWindow() scrolledwindow.set_shadow_type(Gtk.ShadowType.ETCHED_OUT) scrolledwindow.set_size_request(300, 150) scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) scrolledwindow.add(treeview) scrolledwindow.show() warnDlg.get_content_area().add(scrolledwindow) response = warnDlg.run() if response == Gtk.ResponseType.OK : executer = RemoveExecuter(package) executer.start() elif response == Gtk.ResponseType.CANCEL : sys.exit(0) warnDlg.destroy() Gtk.main() if __name__ == "__main__": mainwin = ukuiRemoveWindow(sys.argv[1]) Gtk.main() ukui-menu/lib/category.xml0000664000175000017500000000341613245137611014533 0ustar fengfeng ukui-menu/lib/ukui-menu.py0000775000175000017500000005677513267547200014513 0ustar fengfeng#! /usr/bin/python3 # -*- coding: utf-8 -*- # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015-2016 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. __VERSION__='1.0.3' import gc import gi import gettext import os import platform import subprocess import sys import traceback import signal gi.require_version("Gtk", "3.0") gi.require_version('UkuiPanelApplet', '4.0') from ctypes import * from gi.repository import Gtk, GLib, GdkPixbuf, Gdk, GObject from gi.repository import UkuiPanelApplet from gi.repository import Gio from ukui_menu.plugins.menu import pluginclass try: import xdg.Config import ukui_menu.keybinding as keybinding import ukui_menu.pointerMonitor as pointerMonitor import ukui_menu.menueditor as menueditor except Exception as e: print (e) sys.exit( 1 ) signal.signal(signal.SIGINT, signal.SIG_DFL) GObject.threads_init() # i18n gettext.install("ukui-menu", "/usr/share/locale") NAME = _("Menu") os.environ['GNOME_DESKTOP_SESSION_ID'] = "this-is-deprecated" xdg.Config.setWindowManager('MATE') from ukui_menu.execute import * class MainWindow( object ): """This is the main class for the application""" def __init__( self, keybinder, settings ): self.keybinder = keybinder self.settings = settings self.data_path = os.path.join( '/', 'usr', 'share', 'ukui-menu' ) self.icon = "/usr/share/ukui-menu/icons/ukui-logo.svg" # Load UI file and extract widgets builder = Gtk.Builder() builder.add_from_file(os.path.join( self.data_path, "ukui-menu.glade" )) self.window = builder.get_object( "mainWindow" ) self.eventbox = builder.get_object( "eventbox1" ) self.paneholder = builder.get_object( "paneholder" ) self.border = builder.get_object( "border" ) builder.connect_signals(self) self.borderwidth = 1 self.border.set_padding( self.borderwidth, self.borderwidth, self.borderwidth, self.borderwidth ) defaultStyle = self.getDefaultStyle() color = defaultStyle.lookup_color('panel_normal_border_color')[1] if color == Gdk.Color(red=0, green=0, blue=0): self.window.modify_bg( Gtk.StateType.NORMAL, Gdk.color_parse( "#014276" )) else: self.window.modify_bg( Gtk.StateType.NORMAL, color ) self.eventbox.set_name("EventBox") self.window.connect( "key-press-event", self.onKeyPress ) self.window.connect( "focus-in-event", self.onFocusIn ) #设置window透明 #self.window.set_app_paintable(True) #self.window.connect( "draw", self.onDrawEvent ) #self.window.connect( "screen-changed", self.onScreenChanged ) #self.paneholder.set_app_paintable(True) #self.paneholder.connect( "draw", self.onDrawEvent ) #self.paneholder.connect( "screen-changed", self.onScreenChanged ) #self.onScreenChanged(None) self.loseFocusId = self.window.connect( "focus-out-event", self.onFocusOut ) self.loseFocusBlocked = False self.offset = 0 self.window.stick() self.PopulatePlugins() def on_window1_destroy (self, widget, data = None): Gtk.main_quit() sys.exit(0) def PopulatePlugins( self ): PluginPane = Gtk.EventBox() PluginPane.show() PaneLadder = Gtk.Box( orientation=Gtk.Orientation.VERTICAL ) PluginPane.add( PaneLadder ) ImageBox = Gtk.EventBox() ImageBox.show() self.pluginlist = self.settings.get_strv( "plugins-list" ) self.plugins = {} self.showCategoryMenu = self.settings.get_boolean("show-category-menu") try: MyPlugin = pluginclass(self, self.showCategoryMenu) except Exception as e: print (e) MyPlugin.content_holder.show() VBox1 = Gtk.Box( orientation=Gtk.Orientation.VERTICAL ) VBox1.show() #Add plugin to Plugin Box under heading button MyPlugin.content_holder.reparent( VBox1 ) #Add plugin to main window PaneLadder.pack_start( VBox1 , True, True, 0) PaneLadder.show() if MyPlugin.window: MyPlugin.window.destroy() try: if hasattr( MyPlugin, 'do_plugin' ): MyPlugin.do_plugin() heightPath = os.path.join(GLib.get_home_dir(), ".windowHeight") if os.path.exists(heightPath): f = open(heightPath, "r") lines = f.readlines() length = lines[0] MyPlugin.windowHeight = int(length) f.close() MyPlugin.content_holder.set_size_request(345, MyPlugin.windowHeight ) except Exception as e: print (e) self.plugins["menu"] = MyPlugin self.paneholder.pack_start( ImageBox, False, False, 0 ) self.paneholder.pack_start( PluginPane, False, False, 0 ) def getDefaultStyle( self ): widget = Gtk.EventBox() widget.show() return Gtk.rc_get_style(widget) def RegenPlugins( self, *args, **kargs ): for item in self.paneholder: item.destroy() for plugin in list(self.plugins.values()): if hasattr( plugin, "destroy" ): plugin.destroy() try: del plugin except: pass try: del self.plugins except: pass gc.collect() self.PopulatePlugins() def onKeyPress( self, widget, event ): if event.keyval == Gdk.KEY_Escape: self.hide() return True return False def show( self ): for plugin in list(self.plugins.values()): if hasattr( plugin, "onShowMenu" ): plugin.onShowMenu() self.window.present() self.window.get_window().focus( Gdk.CURRENT_TIME ) for plugin in list(self.plugins.values()): if hasattr( plugin, "changeTab" ): plugin.changeTab( 0 ) Gdk.flush() def hide( self ): for plugin in list(self.plugins.values()): if hasattr( plugin, "onHideMenu" ): plugin.onHideMenu() self.window.hide() def onFocusIn( self, *args ): if self.loseFocusBlocked: self.window.handler_unblock( self.loseFocusId ) self.loseFocusBlocked = False return False def onFocusOut( self, *args): if self.window.get_visible(): self.hide() return False def onDrawEvent( self, *args ): cr = Gdk.cairo_create(self.window.get_window()) cr.set_source_rgba(0, 0, 0, 0.7) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() #cr.destroy() return False def onScreenChanged( self, *args ): screen = self.window.get_screen() visual = screen.get_rgba_visual() self.window.set_visual(visual) screen = self.paneholder.get_screen() visual = screen.get_rgba_visual() self.paneholder.set_visual(visual) def stopHiding( self ): if not self.loseFocusBlocked: self.window.handler_block( self.loseFocusId ) self.loseFocusBlocked = True class MenuWin( object ): def __init__( self, applet, iid ): self.data_path = os.path.join('/','usr','share','ukui-menu') self.applet = applet self.mainwin = None self.actionNormal = Gtk.Action(name="UkuiNormalMenu", label=_("Normal Menu"), tooltip=None, stock_id="gtk-about") self.actionNormal.connect("activate", self.LoadNormalMenu) self.actionCategory = Gtk.Action(name="UkuiCategoryMenu", label=_("Category Menu"), tooltip=None, stock_id="gtk-about") self.actionCategory.connect("activate", self.LoadCategoryMenu) self.editCategory = Gtk.Action(name="EditCategoryMenu", label=_("Edit Category Menu"), tooltip=None, stock_id="gtk-edit") self.editCategory.connect("activate", self.EditCategoryMenu) self.createPanelButton() self.applet.set_flags( UkuiPanelApplet.AppletFlags.EXPAND_MINOR ) self.button.connect( "button-press-event", self.showMenu ) GLib.timeout_add(1000, self.InitMenu ) def InitMenu( self ): self.settings = Gio.Settings.new("org.ukui.ukui-menu") self.settings.connect("changed::show-category-menu", self.changeMenuState) self.settings.connect("changed::permission-changed", self.changeMenuState) self.state = self.settings.get_boolean("show-category-menu") self.keybinder = keybinding.GlobalKeyBinding() self.hotkeyText = "Super_L" self.mainwin = MainWindow( self.keybinder, self.settings ) self.mainwin.window.connect( "map-event", self.onWindowMap ) self.mainwin.window.connect( "unmap-event", self.onWindowUnmap ) self.mainwin.window.connect( "realize", self.onRealize ) self.mainwin.window.connect( "size-allocate", lambda *args: self.positionMenu() ) self.mainwin.window.connect( "focus-out-event", lambda *args: self.button.set_name("ButtonApplet") ) self.bind_hot_key() self.applet.set_can_focus(False) self.pointerMonitor = pointerMonitor.PointerMonitor() self.pointerMonitor.connect("activate", self.onPointerOutside) return False def onWindowMap( self, *args ): self.applet.get_style_context().set_state( Gtk.StateFlags.SELECTED ) if self.keybinder == None: return else: self.keybinder.set_focus_window( self.mainwin.window.get_window() ) return False def onWindowUnmap( self, *args ): self.applet.get_style_context().set_state( Gtk.StateFlags.NORMAL ) if self.keybinder == None: return else: self.keybinder.set_focus_window() return False def onRealize( self, *args): self.pointerMonitor.addWindowToMonitor( self.mainwin.window.get_window() ) self.pointerMonitor.addWindowToMonitor( self.applet.get_window() ) self.pointerMonitor.start() return False def onPointerOutside(self, *args): self.mainwin.hide() return True def onBindingPress(self, binder): self.toggleMenu() return True def changeIcon(self, settings, key, args = None): self.panel_size = self.settings_panel.get_int("size") self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("/usr/share/ukui-menu/icons/start.svg") self.pixbuf = self.pixbuf.scale_simple(self.panel_size, self.panel_size, 2) self.button_icon.set_from_pixbuf(self.pixbuf) self.button_box.set_size_request(self.panel_size + 10, -1) Gdk.flush() def createPanelButton( self ): self.settings_panel = Gio.Settings.new_with_path("org.ukui.panel.toplevel", "/org/ukui/panel/toplevels/bottom/") self.settings_panel.connect("changed::size", self.changeIcon) self.panel_size = self.settings_panel.get_int("size") style_provider = Gtk.CssProvider() try: css = open( os.path.join('/', 'usr', 'share', 'ukui-menu', 'ukuimenu.css'), 'rb') css_data = css.read() css.close() style_provider.load_from_data(css_data) Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) except Exception as e: print (e) self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("/usr/share/ukui-menu/icons/start.svg") self.pixbuf = self.pixbuf.scale_simple(self.panel_size, self.panel_size, 2) self.button_icon = Gtk.Image.new() self.button_icon.set_from_pixbuf(self.pixbuf) self.button_icon.set_tooltip_text(_("Start")) self.button = Gtk.Button.new() self.button.set_name("ButtonApplet") self.button.set_image(self.button_icon) self.button_box = Gtk.Box() self.button_box.set_size_request(self.panel_size + 10, -1) self.button_box.pack_start( self.button , True, True, 0) self.button_icon.set_padding( 0, 0 ) self.button_box.set_homogeneous( False ) self.button_box.show_all() self.applet.add( self.button_box ) self.applet.set_background_widget( self.applet ) def bind_hot_key( self ): try: if self.hotkeyText != "": self.keybinder.grab( self.hotkeyText ) self.keybinder.connect("activate", self.onBindingPress) self.keybinder.start() # Binding menu to hotkey print (("Binding to Hot Key: " + self.hotkeyText)) except Exception as cause: print ("** WARNING ** - Menu Hotkey Binding Error") print (("Error Report :\n", str(cause))) pass def showAboutDialog( self, action, userdata = None ): about = Gtk.AboutDialog() about.set_name("Ukui Menu") about.set_version(__VERSION__) about.set_comments( _("Startup Menu") ) about.set_logo( GdkPixbuf.Pixbuf.new_from_file("/usr/share/ukui-menu/icons/ukui-logo.svg") ) about.connect( "response", lambda dialog, r: dialog.destroy() ) about.show() def showPropertyDialog( self, action, userdata = None ): builder = Gtk.Builder() builder.add_from_file(os.path.join( '/', 'usr', 'share', 'ukui-menu', "menu-property.glade" )) window = builder.get_object("window") window.set_resizable(False) window.set_default_icon(GdkPixbuf.Pixbuf.new_from_file("/usr/share/ukui-menu/icons/start.svg")) window.set_title(_("Menu Property")) labelMenu = builder.get_object("label1") labelMenu.set_text(_("Menu Type:")) labelMenu.set_tooltip_text(_("This is used to set up the menu type.")) labelRecent = builder.get_object("label2") labelRecent.set_text(_("File History:")) labelRecent.set_tooltip_text(_("This is used to set up whether the file history is displayed.")) self.radioButton1 = builder.get_object("radiobutton1") self.radioButton1.set_label(_("Normal Menu")) self.radioButton2 = builder.get_object("radiobutton2") self.radioButton2.set_label(_("Category Menu")) if self.mainwin.showCategoryMenu: self.radioButton2.set_active(True) else: self.radioButton1.set_active(True) self.radioButton1.connect("button-press-event", self.radioToggled) self.radioButton2.connect("button-press-event", self.radioToggled) self.switchButton = builder.get_object("switchButton") state = self.mainwin.settings.get_boolean("show-recent-file") if state: self.switchButton.set_state(True) else: self.switchButton.set_state(False) self.switchButton.connect("notify::active", self.Switched) label3 = builder.get_object("label3") label3.set_text(_("Recent file number:")) label4 = builder.get_object("label4") label4.set_text(_("Recent app number:")) self.recent_file_num_bt = builder.get_object("recent_file_num_bt") if not state: self.recent_file_num_bt.set_sensitive(False) self.recent_file_num_bt.connect("value-changed", self.value_file_changed) recent_file_num = self.mainwin.settings.get_int("recent-file-num") self.recent_file_num_bt.set_value(recent_file_num) self.recent_app_num_bt = builder.get_object("recent_app_num_bt") self.recent_app_num_bt.connect("value-changed", self.value_app_changed) recent_app_num = self.mainwin.settings.get_int("recent-app-num") self.recent_app_num_bt.set_value(recent_app_num) self.resetButton = builder.get_object("button2") self.resetButton.set_label(_("Reset to default(_R)")) self.resetButton.set_use_underline(True) self.resetButton.connect("clicked", self.reset_to_default) self.closeButton = builder.get_object("button1") image = Gtk.Image.new_from_icon_name("gtk-close", Gtk.IconSize.MENU) self.closeButton.set_image(image) self.closeButton.set_label(_("Close(_C)")) self.closeButton.set_use_underline(True) self.closeButton.connect("clicked", lambda w: window.close()) window.show() def reset_to_default( self, widget ): self.mainwin.settings.reset("show-category-menu") self.mainwin.settings.reset("show-recent-file") self.mainwin.settings.reset("recent-file-num") self.mainwin.settings.reset("recent-app-num") state = self.mainwin.settings.get_boolean("show-category-menu") if state: self.radioButton2.set_active(True) else: self.radioButton1.set_active(True) state = self.mainwin.settings.get_boolean("show-recent-file") if state: self.switchButton.set_state(True) else: self.switchButton.set_state(False) self.recent_file_num_bt.set_value(self.mainwin.settings.get_int("recent-file-num")) self.recent_app_num_bt.set_value(self.mainwin.settings.get_int("recent-app-num")) def value_file_changed( self, widget ): num = widget.get_value_as_int() self.mainwin.settings.set_int("recent-file-num", num) def value_app_changed( self, widget ): num = widget.get_value_as_int() self.mainwin.settings.set_int("recent-app-num", num) def Switched( self, widget, user_data ): state = self.switchButton.get_state() if state: self.mainwin.settings.set_boolean("show-recent-file", True) self.recent_file_num_bt.set_sensitive(True) else: self.mainwin.settings.set_boolean("show-recent-file", False) self.recent_file_num_bt.set_sensitive(False) def radioToggled( self, widget, event ): if widget == self.radioButton1: self.mainwin.settings.set_boolean("show-category-menu", False) elif widget == self.radioButton2: self.mainwin.settings.set_boolean("show-category-menu", True) def showMenu( self, widget = None, event = None ): if event == None or event.button == 1: self.toggleMenu() # show right click menu elif event.button == 3: self.create_menu() # allow middle click and drag elif event.button == 2: self.mainwin.hide() def toggleMenu( self ): if self.mainwin is None: return if self.applet.get_style_context().get_state() & Gtk.StateFlags.SELECTED: self.button.set_name("ButtonApplet") self.mainwin.hide() else: self.positionMenu() self.button.set_name("ButtonClicked") self.mainwin.show() def positionMenu( self ): if self.mainwin is None: return # Get our own dimensions & position ourWidth = self.mainwin.window.get_size()[0] ourHeight = self.mainwin.window.get_size()[1] + self.mainwin.offset # Get the dimensions/position of the widgetToAlignWith entryX = self.applet.get_window().get_origin().x entryY = self.applet.get_window().get_origin().y entryWidth, entryHeight = self.applet.get_allocation().width, self.applet.get_allocation().height entryHeight = entryHeight + self.mainwin.offset # Get the screen dimensions screenHeight = Gdk.Screen.height() screenWidth = Gdk.Screen.width() if self.applet.get_orient() == UkuiPanelApplet.AppletOrient.UP or self.applet.get_orient() == UkuiPanelApplet.AppletOrient.DOWN: if entryX + ourWidth < screenWidth or entryX + entryWidth / 2 < screenWidth / 2: # Align to the left of the entry newX = entryX else: # Align to the right of the entry newX = entryX + entryWidth - ourWidth if entryY + entryHeight / 2 < screenHeight / 2: # Align to the bottom of the entry newY = entryY + entryHeight else: newY = entryY - ourHeight else: if entryX + entryWidth / 2 < screenWidth / 2: # Align to the left of the entry newX = entryX + entryWidth else: # Align to the right of the entry newX = entryX - ourWidth if entryY + ourHeight < screenHeight or entryY + entryHeight / 2 < screenHeight / 2: # Align to the bottom of the entry newY = entryY else: newY = entryY - ourHeight + entryHeight # -"Move window" self.mainwin.window.move( newX, newY ) def changeMenuState(self, settings, key, args = None): if self.mainwin is None: return self.mainwin.RegenPlugins() def LoadNormalMenu( self, *args, **kargs ): if self.mainwin is None: return self.mainwin.settings.set_boolean("show-category-menu", False) os.system("killall ukui-menu-editor") self.state = False def LoadCategoryMenu( self, *args, **kargs ): if self.mainwin is None: return self.mainwin.settings.set_boolean("show-category-menu", True) self.state = True def EditCategoryMenu( self, *args, **kargs ): os.system("ukui-menu-editor &") # this callback is to create a context menu def create_menu(self): if self.mainwin is None: return action_group = Gtk.ActionGroup(name="context-menu") action_group.add_action(self.actionNormal) action_group.add_action(self.actionCategory) action_group.add_action(self.editCategory) state = self.settings.get_boolean("show-category-menu") if state: self.actionNormal.set_visible(True) self.actionCategory.set_visible(False) self.editCategory.set_visible(True) else: self.actionNormal.set_visible(False) self.actionCategory.set_visible(True) self.editCategory.set_visible(False) action = Gtk.Action(name="UkuiMenuReload", label=_("Reload plugins"), tooltip=None, stock_id="gtk-refresh") action.connect("activate", self.mainwin.RegenPlugins) action_group.add_action(action) action = Gtk.Action(name="UkuiMenuAbout", label=_("Property"), tooltip=None, stock_id="gtk-execute") action.connect("activate", self.showPropertyDialog) action_group.add_action(action) action_group.set_translation_domain ("ukui-menu") xml = os.path.join( self.data_path, "popup.xml" ) self.applet.setup_menu_from_file(xml, action_group) def applet_factory( applet, iid, data ): MenuWin( applet, iid ) applet.show() return True def quit_all(widget): Gtk.main_quit() sys.exit(0) UkuiPanelApplet.Applet.factory_main("UkuiMenuAppletFactory", True, UkuiPanelApplet.Applet.__gtype__, applet_factory, None) ukui-menu/ukui-menu0000775000175000017500000000230413237775114013275 0ustar fengfeng#!/usr/bin/env python3 # Copyright (C) 2007-2014 Clement Lefebvre # Copyright (C) 2015-2016 Martin Wimpress # Copyright (C) 2016,Tianjin KYLIN Information Technology Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. import os import sys if len(sys.argv) > 1: if (sys.argv[1] in ["help", "h", "-?", "--help", "-h", "?"]): print("UKUI Menu - Advanced UKUI Menu\n") print("options:") print(" [--]help, [-]h Display this help.") else: os.system("/usr/lib/ukui-menu/ukui-menu.py")