/neatx/lib/app/nxdialog.py

http://neatx.googlecode.com/ · Python · 264 lines · 148 code · 47 blank · 69 comment · 5 complexity · 2bf286411f30ced367bbbc9aacf4eb3c MD5 · raw file

  1. #
  2. #
  3. # Copyright (C) 2008 Google Inc.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful, but
  11. # WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. # General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18. # 02110-1301, USA.
  19. """nxdialog program for handling dialog display."""
  20. # If an "NX_CLIENT" environment variable is not provided to nxagent
  21. # nxcomp library assumes this script is located in /usr/NX/bin/nxclient
  22. import pygtk
  23. pygtk.require("2.0")
  24. import gtk
  25. import logging
  26. import optparse
  27. import os
  28. import signal
  29. import sys
  30. from neatx import cli
  31. from neatx import constants
  32. from neatx import errors
  33. from neatx import utils
  34. PROGRAM = "nxdialog"
  35. DISCONNECT = 1
  36. TERMINATE = 2
  37. CANCEL_TEXT = "Cancel"
  38. DISCONNECT_TEXT = "Disconnect"
  39. TERMINATE_TEXT = "Terminate"
  40. class PullDownMenu:
  41. """Shows a popup menu to disconnect/terminate session.
  42. """
  43. def __init__(self, window_id):
  44. """Initializes this class.
  45. @type window_id: int
  46. @param window_id: X11 window id of target window
  47. """
  48. self._window_id = window_id
  49. self._result = None
  50. def Show(self):
  51. """Shows popup and returns result.
  52. """
  53. win = gtk.gdk.window_foreign_new(self._window_id)
  54. menu = gtk.Menu()
  55. menu.connect("deactivate", self._MenuDeactivate)
  56. # TODO: Show title item in bold font
  57. title = gtk.MenuItem(label="Neatx session")
  58. title.set_sensitive(False)
  59. menu.append(title)
  60. disconnect = gtk.MenuItem(label=DISCONNECT_TEXT)
  61. disconnect.connect("activate", self._ItemActivate, DISCONNECT)
  62. menu.append(disconnect)
  63. terminate = gtk.MenuItem(label=TERMINATE_TEXT)
  64. terminate.connect("activate", self._ItemActivate, TERMINATE)
  65. menu.append(terminate)
  66. menu.append(gtk.SeparatorMenuItem())
  67. cancel = gtk.MenuItem(label=CANCEL_TEXT)
  68. menu.append(cancel)
  69. menu.show_all()
  70. menu.popup(parent_menu_shell=None, parent_menu_item=None,
  71. func=self._PosMenu, data=win,
  72. button=0, activate_time=gtk.get_current_event_time())
  73. gtk.main()
  74. return self._result
  75. def _ItemActivate(self, _, result):
  76. self._result = result
  77. gtk.main_quit()
  78. def _MenuDeactivate(self, _):
  79. logging.debug("Aborting pulldown menu")
  80. gtk.main_quit()
  81. def _PosMenu(self, menu, parent):
  82. """Positions menu at the top center of the parent window.
  83. """
  84. # Get parent geometry and origin
  85. (_, _, win_width, win_height, _) = parent.get_geometry()
  86. (win_x, win_y) = parent.get_origin()
  87. # Calculate width of menu
  88. (menu_width, menu_height) = menu.size_request()
  89. # Calculate center
  90. x = win_x + ((win_width - menu_width) / 2)
  91. return (x, win_y, True)
  92. def ShowYesNoSuspendBox(title, text):
  93. """Shows a message box to disconnect/terminate session.
  94. @type title: str
  95. @param title: Message box title
  96. @type text: str
  97. @param text: Message box text
  98. @return: Choosen action
  99. """
  100. dlg = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION, flags=gtk.DIALOG_MODAL)
  101. dlg.set_title(title)
  102. dlg.set_markup(text)
  103. dlg.add_button(DISCONNECT_TEXT, DISCONNECT)
  104. dlg.add_button(TERMINATE_TEXT, TERMINATE)
  105. dlg.add_button(CANCEL_TEXT, gtk.RESPONSE_CANCEL)
  106. res = dlg.run()
  107. if res in (DISCONNECT, TERMINATE):
  108. return res
  109. # Everything else is cancel
  110. return None
  111. def HandleSessionAction(agentpid, action):
  112. """Execute session action choosen by user.
  113. @type agentpid: int
  114. @param agentpid: Nxagent process id as passed by command line
  115. @type action: int or None
  116. @param action: Choosen action
  117. """
  118. if action == DISCONNECT:
  119. logging.info("Disconnecting from session, sending SIGHUP to %s", agentpid)
  120. os.kill(agentpid, signal.SIGHUP)
  121. elif action == TERMINATE:
  122. logging.info("Terminating session, sending SIGTERM to process %s",
  123. agentpid)
  124. os.kill(agentpid, signal.SIGTERM)
  125. elif action is None:
  126. logging.debug("Dialog canceled, nothing to do")
  127. else:
  128. raise NotImplementedError()
  129. def ShowSimpleMessageBox(icon, title, text):
  130. """Shows a simple message box.
  131. @type icon: QMessageBox.Icon
  132. @param icon: Icon for message box
  133. @type title: str
  134. @param title: Message box title
  135. @type text: str
  136. @param text: Message box text
  137. """
  138. dlg = gtk.MessageDialog(type=icon, flags=gtk.DIALOG_MODAL,
  139. buttons=gtk.BUTTONS_OK)
  140. dlg.set_title(title)
  141. dlg.set_markup(text)
  142. dlg.run()
  143. class NxDialogProgram(cli.GenericProgram):
  144. def BuildOptions(self):
  145. options = cli.GenericProgram.BuildOptions(self)
  146. options.extend([
  147. optparse.make_option("--caption", type="string", dest="caption"),
  148. optparse.make_option("--dialog", type="string", dest="dialog_type"),
  149. optparse.make_option("--display", type="string", dest="display"),
  150. optparse.make_option("--message", type="string", dest="text"),
  151. optparse.make_option("--parent", type="int", dest="agentpid"),
  152. optparse.make_option("--window", type="int", dest="window"),
  153. ])
  154. return options
  155. def Run(self):
  156. """Disconnect/terminate NX session upon user's request.
  157. """
  158. logging.debug("Nxdialog options: %r", self.options)
  159. dlgtype = self.options.dialog_type
  160. if dlgtype not in constants.VALID_DLG_TYPES:
  161. logging.error("Dialog type '%s' not supported", dlgtype)
  162. sys.exit(constants.EXIT_FAILURE)
  163. if dlgtype in (constants.DLG_TYPE_PULLDOWN,
  164. constants.DLG_TYPE_YESNOSUSPEND) and not self.options.agentpid:
  165. logging.error("Agent pid not supplied via --parent")
  166. sys.exit(constants.EXIT_FAILURE)
  167. if self.options.caption:
  168. message_caption = self.options.caption
  169. else:
  170. message_caption = sys.argv[0]
  171. if self.options.text:
  172. message_text = self.options.text
  173. else:
  174. message_text = ""
  175. if self.options.display:
  176. os.environ["DISPLAY"] = self.options.display
  177. if dlgtype == constants.DLG_TYPE_OK:
  178. ShowSimpleMessageBox(gtk.MESSAGE_INFO, message_caption, message_text)
  179. elif dlgtype in (constants.DLG_TYPE_ERROR, constants.DLG_TYPE_PANIC):
  180. ShowSimpleMessageBox(gtk.MESSAGE_ERROR, message_caption, message_text)
  181. elif dlgtype == constants.DLG_TYPE_PULLDOWN:
  182. HandleSessionAction(self.options.agentpid,
  183. PullDownMenu(self.options.window).Show())
  184. elif dlgtype == constants.DLG_TYPE_YESNOSUSPEND:
  185. HandleSessionAction(self.options.agentpid,
  186. ShowYesNoSuspendBox(message_caption, message_text))
  187. else:
  188. # TODO: Implement all dialog types
  189. raise errors.GenericError("Dialog type %r not implemented" %
  190. self.options.dialog_type)
  191. def Main():
  192. logsetup = utils.LoggingSetup(PROGRAM)
  193. NxDialogProgram(logsetup).Main()