
    e]                        d dl mZmZmZmZ d dlmZ d dlZd dlZd dl	Z	d dl
Z
d dlZd dlmZ d dlmZ d dlZ G d d      Z G d d	      Z G d
 d      Z G d de      Z G d dej*                        Z G d dee      Zy)    )RLockcurrentThreadLockEvent)dequeN)UIBase)ExitNotifyThreadc                   >    e Zd Zd Zd Zd Zd
dZd Zd Zd Z	d Z
y	)
CursesUtilc                 N    t               | _        t               | _        i | _        y N)r   iolocktframe_lockcolormap)selfargskwargss      0/usr/share/offlineimap3/offlineimap/ui/Curses.py__init__zCursesUtil.__init__    s!    g 7 H    c                 F    t        j                  | j                  |         S )z<Return the curses color pair, that corresponds to the color.)curses
color_pairr   )r   col_names     r   curses_colorpairzCursesUtil.curses_colorpair)   s       x!899r   c           	      6   d| j                   d<   t        j                  dt        j                  t        j                         d| j                   d<   t        j
                  }dt        j
                  dfdt        j                  dfdt        j                  dfd	t        j                  dfd
t        j                  dfdt        j                  dfdt        j                  dff}d}|D ]1  \  }}}|dz  }|| j                   |<   t        j                  |||       3 y)z,Initialize the curses color pairs available.r   white   bannerblackFblueredpurplecyangreenorangeN)r   r   	init_pairCOLOR_WHITE
COLOR_BLUECOLOR_BLACK	COLOR_REDCOLOR_MAGENTA
COLOR_CYANCOLOR_GREENCOLOR_YELLOW)r   bcolcolorsinamefcolbolds          r   init_colorpairszCursesUtil.init_colorpairs.   s    "#gF..0A0AB"#h!!f((%0V&&.F$$e,v++U3V&&.f((%0v**E24  & 	,D$FA"#DMM$Qd+	,r   c                 8    | j                   j                  |      S )zLocks the Curses ui thread.

        Can be invoked multiple times from the owning thread. Invoking
        from a non-owning thread blocks and waits until it has been
        unlocked by the owning thread.)r   acquire)r   blocks     r   lockzCursesUtil.lockF   s     {{""5))r   c                 8    | j                   j                          y)a  Unlocks the Curses ui thread.

        Decrease the lock counter by one and unlock the ui thread if the
        counter reaches 0.  Only call this method when the calling
        thread owns the lock. A RuntimeError is raised if this method is
        called when the lock is unlocked.N)r   releaser   s    r   unlockzCursesUtil.unlockO   s     	r   c                     | j                          	  ||i | | j                          y# | j                          w xY w)z'Perform an operation with full locking.N)r:   r>   )r   targetr   r   s       r   exec_lockedzCursesUtil.exec_lockedY   s2     				D#F#KKMDKKMs   + =c                 ,    d }| j                  |       y )Nc                  h    t         j                  j                          t        j                          y r   )r   panelupdate_panelsdoupdate r   r   lockedstuffz'CursesUtil.refresh.<locals>.lockedstuffc   s    LL&&(OOr   )rA   )r   rH   s     r   refreshzCursesUtil.refreshb   s    	 	%r   c                     t        | d      S )Nstdscr)hasattrr=   s    r   isactivezCursesUtil.isactivei   s    tX&&r   N)T)__name__
__module____qualname__r   r   r6   r:   r>   rA   rI   rM   rG   r   r   r   r      s+    I:
,0*&'r   r   c                   6    e Zd ZdZd Zd	dZd Zd Zd Zd Z	y)
CursesAccountFramezNotable instance variables:

    - account: corresponding Account()
    - children
    - ui
    - key
    - window: curses window associated with an account
    c                 `    g | _         |r|nd| _        || _        d| _        d| _        d| _        y)zG
        :param account: An Account() or None (for eg SyncrunnerThread)z*ControlNr   )childrenaccountuiwindowacc_numlocation)r   rV   rU   s      r   r   zCursesAccountFrame.__init__w   s2     ")wzr   c                      |rd|dz  |dz  fz  nd} j                   d|d j                  dd fd} j                  j                  |       t	               _        y	)
zVDraw the account status string.

        secs tells us how long we are going to sleep.z%3d:%02d<   activez: [z] z>12.12z: c                  t    	 j                   j                  dd        y # t        j                  $ r Y y w xY wNr   )rW   addstrr   error)accstrr   s   r   r_   z.CursesAccountFrame.drawleadstr.<locals>.addstr   s4    ""1a0<< s   ! 77N)rX   rU   rV   rA   lenrY   )r   secssleepstrr_   ra   s   `   @r   drawleadstrzCursesAccountFrame.drawleadstr   sY    
 <@:TBY 77X)-xN	 	F#Fr   c                 $   || _         || _        | j                          | j                  j	                  | j                   j
                         | j                  D ]4  }|j                  || j                  d       | xj                  dz  c_        6 y)zRegister an curses win and a hotkey as Account window.

        :param curses_win: the curses window associated with an account
        :param acc_num: int denoting the hotkey associated with this account.r   r   N)	rW   rX   re   rV   rA   noutrefreshrT   updaterY   )r   
curses_winrX   childs       r   	setwindowzCursesAccountFrame.setwindow   so     !DKK334]] 	ELLT]]A6MMQM	r   c                     t        | j                  | j                  | j                  d      }| xj                  dz  c_        | j                  j                  |       |S )z_Create a new ThreadFrame and append it to self.children.

        :returns: The new ThreadFramer   r   )CursesThreadFramerV   rW   rY   rT   append)r   tfs     r   get_new_tframez!CursesAccountFrame.get_new_tframe   sE    
 twwT]]AFR 	r   c                     | j                  |       | j                  j                  | j                  j                         t        j                  |       | j                  j                         S )zmShow how long we are going to sleep and sleep.

        :returns: Boolean, whether we want to abort the sleep)	re   rV   rA   rW   rI   timesleeprU   get_abort_event)r   	sleepsecsremainingsecss      r   sleepingzCursesAccountFrame.sleeping   sM    
 	'DKK//0

9||++--r   c                 .   t        | j                  t        j                  j                        rg| j
                  j                  d| j                  z         | j                  j                  j                  d| j                  j                  z  dd       yy)z8Request that we stop sleeping asap and continue to sync.z%Requested synchronization for acc: %sz
Account %s	skipsleep1N)

isinstancerU   offlineimapaccountsAccountrV   infoconfigsetr3   r=   s    r   syncnowzCursesAccountFrame.syncnow   sl    
 dllK$8$8$@$@AGGLL@4<<OPLL##L4<<3D3D$D$/6 Br   Nr   )
rN   rO   rP   __doc__r   re   rk   rp   rw   r   rG   r   r   rR   rR   m   s%    $$.6r   rR   c                   0    e Zd ZdZd ZddZd Zd Zd Zy)	rm   z-curses_color: current color pair for logging.c                 p    || _         || _        || _        || _        t	        j
                  d      | _        y)z_
        :param ui: is a Blinkenlights() instance
        :param acc_win: curses Account windowr   N)rV   rW   xyr   r   curses_color)r   rV   acc_winr   r   s        r   r   zCursesThreadFrame.__init__   s3    
 "--a0r   c                 x    || j                   j                  |      z  | _        || _        | j	                          y)zDraw the thread symbol '@' in the specified color

        :param color: Curses colorname
        :param modifier: Curses modified, such as curses.A_BOLD
        N)rV   r   r   	colornamedisplay)r   colormodifiers      r   setcolorzCursesThreadFrame.setcolor   s0     %tww'?'?'FFr   c                 F      fd} j                   j                  |       y )Nc                      	  j                   j                   j                   j                  d j                          j                   j                          y # t
        j                  $ r Y 0w xY w)N@)rW   addchr   r   r   r   r`   rI   r=   s   r   locked_displayz1CursesThreadFrame.display.<locals>.locked_display   sX    !!$&&$&&#t7H7HI KK! << s   <A A0/A0)rV   rA   )r   r   s   ` r   r   zCursesThreadFrame.display   s    	" 	N+r   c                 N    || _         || _        || _        | j                          y)z<Update the xy position of the '.' (and possibly the aframe).N)rW   r   r   r   )r   r   r   r   s       r   rh   zCursesThreadFrame.update   s"     r   c                 &    | j                  d       y Nr    )r   r=   s    r   	std_colorzCursesThreadFrame.std_color   s    gr   Nr   )	rN   rO   rP   r   r   r   r   rh   r   rG   r   r   rm   rm      s    7	1		,r   rm   c                   @     e Zd ZdZ fdZd Zd Zd Zd Zd Z	 xZ
S )InputHandlerz+Listens for input via the curses interfacesc                     t         t        |           d | _        || _        t               | _        t               | _        | j                          y r   )
superr   r   char_handlerrV   r   enabledr   	inputlockstart)r   rV   	__class__s     r   r   zInputHandler.__init__   s:    lD*, w

r   c              #   H  K   | j                   j                          | j                   j                         r^| j                  5  | j                  j
                  j                         }ddd       dk7  r| | j                   j                         r]yy# 1 sw Y   /xY ww)zReturn the key pressed or -1.

        Wait until `enabled` and loop internally every stdscr.timeout()
        msecs, releasing the inputlock.
        :returns: char or None if disabled while in hereN)r   waitis_setr   rV   rK   getch)r   chars     r   get_next_charzInputHandler.get_next_char  sw      	ll!!# .ww~~++-.rz
	 ll!!#. .s$   AB"%B(+B"B"BB"c                 V    	 | j                         }|D ]  }| j                  |        )r   )r   r   )r   char_genr   s      r   runzInputHandler.run  s5    ))+H  (!!$'( r   c                     | j                   5  || _        || j                  j                          n| j                  j	                          ddd       y# 1 sw Y   yxY w)a  Sets a character callback handler.

        If a key is pressed it will be passed to this handler. Keys
        include the curses.KEY_RESIZE key.

        callback is a function taking a single arg -- the char pressed.
        If callback is None, input will be ignored.N)r   r   r   clearr   )r   callbacks     r   set_char_hdlrzInputHandler.set_char_hdlr  sN     ^^ 	# (D""$  "	# 	# 	#s   ?AAc                 l    | j                   j                          | j                  j                          y)zCall this method when you want exclusive input control.

        Make sure to call input_release afterwards! While this lockis
        held, input can go to e.g. the getpass input.N)r   r   r   r8   r=   s    r   input_acquirezInputHandler.input_acquire,  s$     	 r   c                 l    | j                   j                          | j                  j                          y)z1Call this method when you are done getting input.N)r   r<   r   r   r=   s    r   input_releasezInputHandler.input_release5  s$     	 r   )rN   rO   rP   r   r   r   r   r   r   r   __classcell__r   s   @r   r   r      s$    5(#"!r   r   c                       e Zd ZdZd Zy)CursesLogHandlerz?self.ui has been set to the UI class before anything is invokedc                    t         j                  j                  | |      }| j                  j	                         j
                  }| j                  j                  j                          | j                  j                          	 | j                  j                  j                         \  }}|s|r%| j                  j                  j                  d       | j                  j                  j                  ||       | j                  j                  j                          | j                  j                  j                          | j                  j!                          | j                  j                  j#                          y # | j                  j!                          | j                  j                  j#                          w xY w)N
   )loggingStreamHandlerformatrV   gettfr   r   r8   r:   logwingetyxr   r_   rg   rK   rI   r>   r<   )r   recordlog_strr   r   r   s         r   emitzCursesLogHandler.emit?  s   ''..tV<,, 	##%
	*77>>'')DAqA$$R(GGNN!!'51GGNN&&(GGNN""$GGNNGG'') GGNNGG'')s   B>F A GN)rN   rO   rP   r   r   rG   r   r   r   r   <  s
    I*r   r   c                   &    e Zd ZdZ fdZd Zd Zd Z fdZ fdZ	 fdZ
 fd	Z fd
Z fdZ fdZ fdZ fdZ fdZ fdZ fdZ fdZd" fd	Z fdZd Zd Z fdZd Zd Zd Zd#dZd$dZd Zd Z d Z! fd Z"d! Z# xZ$S )%Blinkenlightsa  Curses-cased fancy UI.

    Notable instance variables self. ....:

       - stdscr: THe curses std screen
       - bannerwin: The top line banner window
       - width|height: The total curses screen dimensions
       - logheight: Available height for the logging part
       - log_con_handler: The CursesLogHandler()
       - threadframes:
       - accframes[account]: 'Accountframe'c                 V    t        t        | 
  |i | t        j                  |        y r   )r   r   r   r   r   r   r   r   s      r   r   zBlinkenlights.__init__c  s%    mT+T<V<D!r   c                     t               }t        j                  d      | _        |j	                  | j                         | j
                  j                  |       |S )zBackend specific console handler.

        Sets up things and adds them to self.logger.
        :returns: The logging.Handler() for console outputz%(message)s)r   r   	Formatter	formattersetFormatterlogger
addHandler)r   chs     r   setup_consolehandlerz"Blinkenlights.setup_consolehandlerh  sI      !**=9
'r" 	r   c                 z   t         j                  j                         st         j                  j                         ryt        j
                  j                  dd      syt         j                  dd dk  st         j                  dd dk\  r*	 t        j                          t        j                          yy#  Y yxY w)	z6Returns true if the backend is usable ie Curses works.FTERMNr      )      )r   r   r   T)sysstdoutisattystdinosenvirongetversion_infor   initscrendwin)ss    r   isusablezBlinkenlights.isusablez  s     zz  "syy'7'7'9zz~~fd+ Aa 6)S-=-=a-Bi-O  s   (B6 6B:c                 d   i | _         i | _        i | _        t               | _        t        j                         | _        t        j                          t        j                          | j                  j                  d       | j                  j                  d       t        j                          d | _        	 t        j                  d      | _        | j                  j                          | j                  j!                          | j#                          | | j$                  _        | j)                          t+        |       | _        | j,                  j/                  | j0                         | j3                         j5                  d       | j7                  t8        j:                         y #  Y xY w)Nr   i  r   r"   )availablethreadframesthreadframes	accframesr   aflockr   r   rK   noechocbreakkeypadtimeoutstart_color	oldcursorcurs_setr   rI   r6   _log_con_handlerrV   setupwindowsr   inputhandlerr   on_keypressedr   r   r   r|   r   r=   s    r   init_bannerzBlinkenlights.init_banner  s-   %'"fnn&1D!	#__Q/DN 	#' (.''(:(:;

e$		+$$%	s   8F+ +F/c                 d    | j                         j                  d       t        t        |   |  y)z=Output that we start syncing an account (and start counting).r#   N)r   r   r   r   acctr   r   r   s     r   r   zBlinkenlights.acct  s(     	

h'mT'.r   c                 d    | j                         j                  d       t        t        |   |  y Nr   )r   r   r   r   
connectingr   s     r   r   zBlinkenlights.connecting  s&    

g&mT-t4r   c                 d    | j                         j                  d       t        t        |   |  y Nr!   )r   r   r   r   syncfoldersr   s     r   r   zBlinkenlights.syncfolders  &    

f%mT.5r   c                 d    | j                         j                  d       t        t        |   |  y Nr$   )r   r   r   r   syncingfolderr   s     r   r   zBlinkenlights.syncingfolder  &    

f%mT0$7r   c                 d    | j                         j                  d       t        t        |   |  y r   )r   r   r   r   skippingfolderr   s     r   r  zBlinkenlights.skippingfolder  s&    

f%mT148r   c                 d    | j                         j                  d       t        t        |   |  y )Nr%   )r   r   r   r   loadmessagelistr   s     r   r  zBlinkenlights.loadmessagelist  s&    

g&mT2D9r   c                 d    | j                         j                  d       t        t        |   |  y r   )r   r   r   r   syncingmessagesr   s     r   r  zBlinkenlights.syncingmessages  s&    

f%mT2D9r   c                 d    | j                         j                  d       t        t        |   |  y Nr"   )r   r   r   r   ignorecopyingmessager   s     r   r  z"Blinkenlights.ignorecopyingmessage  s&    

e$mT7>r   c                 d    | j                         j                  d       t        t        |   |  y )Nr&   )r   r   r   r   copyingmessager   s     r   r
  zBlinkenlights.copyingmessage  s&    

h'mT148r   c                 d    | j                         j                  d       t        t        |   |  y r  )r   r   r   r   deletingmessagesr   s     r   r  zBlinkenlights.deletingmessages  s&    

e$mT3T:r   c                 d    | j                         j                  d       t        t        |   |  y r   )r   r   r   r   addingflagsr   s     r   r  zBlinkenlights.addingflags  r   r   c                 d    | j                         j                  d       t        t        |   |  y r   )r   r   r   r   deletingflagsr   s     r   r  zBlinkenlights.deletingflags  r   r   c                 d    | j                         j                  d       t        t        |   |  y r   )r   r   r   r   callhookr   s     r   r  zBlinkenlights.callhook  s&    

g&mT+T2r   c                     | j                         j                  dt        j                         t        t
        |   |       y r  )r   r   r   A_BOLDr   r   warn)r   msgminorr   s      r   r  zBlinkenlights.warn  s,    

eV]]3mT',r   c                 T   | j                  |      }| j                  5  || j                  |   v rQ| j                  |   |   }|j                  d       | j                  |   j                  |       | j                  |   |= d d d        t        t        | #  |       y # 1 sw Y   xY wr   )	getthreadaccountr   r   r   r   rn   r   r   threadExited)r   threadaccro   r   s       r   r  zBlinkenlights.threadExited  s    ##F+ 	3**3//&&s+F3G$**3/66r:%%c*62	3 	mT/7	3 	3s   A#BB'c                 2   t               }| j                         }| j                  5  	 || j                  |   v r| j                  |   |   cddd       S 	 t        | j                  |         r.| j                  |   j                         }|j                          n| j                  |      j                         }|| j                  |   |<   ddd       |S # t        $ r) i | j                  |<   t               | j                  |<   Y w xY w# 1 sw Y   S xY w)z/Return the ThreadFrame() of the current thread.N)r   r  r   r   KeyErrorr   r   rb   popleftr   getaccountframerp   )r   
cur_threadr  ro   s       r   r   zBlinkenlights.gettf  s    #_
##% 	4:!2!23!77,,S1*=		4 	4 8 4--c23//4<<>))#.==?13Dc":.!	4" 	  :)+!!#&27'**3/:	4" 	s/   D"CA8D/D	DD		DDc                    |t         j                  k(  r| j                          y |dk  s|dkD  ry t        |      dk(  rE| j	                  d       t
        j                  j                  j                  | j                  d       	 t        t        |            }|t        | j                        k\  ry | j                  | j                  |         j                          y # t        $ r Y y w xY w)Nr      qzRequested shutdown via 'q'r   )r   
KEY_RESIZE
resizetermchrr  r|   r}   r~   set_abort_eventr   int
ValueErrorrb   hotkeysr   r   )r   keyindexs      r   r   zBlinkenlights.on_keypressed  s    &###OO7cCis8s?II23  ((88aH	CME C%%T\\%0199;  		s   C 	C*)C*c                     | j                         j                  d       | j                  d|dz  |dz  fz         t        t        |   ||      S )Nr"   zNext sync in %d:%02dr[   )r   r   r   r   r   rs   )r   ru   rU   r   s      r   rs   zBlinkenlights.sleep&  sJ    

e$		(ININ+KKL]D/	7CCr   c                     |s| j                         j                  d       | j                  | j                               }|j	                  ||      S r   )r   r   r   r  rw   )r   ru   rv   accframes       r   rw   zBlinkenlights.sleeping+  sE    JJL!!'*''(=(=(?@  M::r   c                 <    | j                  | j                  d       y)zResize the current windows.TN)rA   r   r=   s    r   r&  zBlinkenlights.resizeterm2  s     	**D1r   c                 .    t        j                  |        y r   )r   mainExceptionr=   s    r   r3  zBlinkenlights.mainException7  s    T"r   c                    | j                   j                          | j                          	 | j                  d       | j                  d|z         | j                  j                          | j                  j                         }| j                          | j                   j                          t        |t              r|j                  d      S |S # | j                          | j                   j                          w xY w)Nz *** Input Requiredz* *** Please enter password for user '%s': zutf-8)encoding)r   r   r:   r  r   rI   getstrr>   r   r{   bytesdecode)r   usernamer   errmsgpasswords        r   getpasszBlinkenlights.getpass:  s    '') 					.II+,IIB  KK!{{))+HKKM++- h&??G?44 KKM++-s   AC ,C?c                 H   | j                   j                         \  | _        | _        | j                  t	        | j
                        z
  dz
  | _        |rt        j                  | j                  | j                        r*t        j                  | j                  | j                         | j                  j                  d| j                         | j                  j                  | j                  | j                         | j                   j                          | j                   j                          nXt        j                  d| j                  dd      | _
        t        j                  | j                  | j                  dd      | _        | j!                          | j                  j#                  d       | j                  j%                  d       | j'                          | j
                  j)                         | _        | j                  dz
  }d}g | _        | j*                  D ]h  }t        j                  d| j                  |d      }| j
                  |   j/                  ||       | j,                  j1                  |       |dz  }|dz  }j t        j2                          y)zSetup and draw bannerwin and logwin.

        If `resize`, don't create new windows, just adapt size. This
        function should be invoked with CursesUtils.locked().r   r   TN)rK   getmaxyxheightwidthrb   r   	logheightr   is_term_resizedr&  	bannerwinresizer   r   rg   newwindraw_bannerwinidlokscrollokdraw_logwinkeysr}   r+  rk   rn   rF   )r   rD  posr-  rU   r   s         r   r   zBlinkenlights.setupwindowsP  s    #'++"6"6"8TZs4>>'::Q>%%dkk4::>!!$++tzz:NN!!!TZZ0KKt~~tzz:KKKK##%#]]1djj!Q?DN --

AqIDK$T" ++-kkAo}} 	GmmAtzz3:GNN7#--gu=LL(QJE1HC	 	r   c                    t        j                         r#t         j                  | j                  d      z  }nt         j                  }| j
                  j                          | j
                  j                  d|       t        j                  dt        j                  }dt        d| j                  t        t        j                        z
  t        |      z
  dz
        z  }||t        j                  }| j
                  j                  dd|| j                  dz
  |       | j
                  j!                          y)zDraw the top-line banner line.r    r   r   N)r   
has_colorsr  r   	A_REVERSErC  r   bkgdr|   __productname____version__maxr@  rb   __copyright__addnstrrg   )r   r   stringspacess       r   rF  zBlinkenlights.draw_bannerwinu  s     MMD$9$9($CCE$$EC''77'335s1tzzC0I0I,JJ!$V -/0 1 3 3#V[-F-FGq!VTZZ!^UC""$r   c                 P   t        j                         rt        j                  d      }nt         j                  }| j                  j                  dd       | j                  j                          | j                  j                  d|       | j                  j                          y)z(Re)draw the current logwindow.r   rM  N)	r   rN  r   A_NORMALr   mover   rP  rg   )r   r   s     r   rI  zBlinkenlights.draw_logwin  sq     %%a(EOOEAe$!r   c                    | j                   5  || j                  v r| j                  |   cddd       S t        | |      | j                  |<   | j                  d       ddd       | j                  |   S # 1 sw Y   xY w)zpReturn an AccountFrame() corresponding to acc_name.

        Note that the *control thread uses acc_name `None`.NT)rD  )r   r   rR   r   )r   acc_names     r   r   zBlinkenlights.getaccountframe  s{    
 [[ 	+4>>)~~h/	+ 	+
 (:$'IDNN8$T*	+ ~~h''	+ 	+s   A6+A66A?c                 t   t        j                          | j                  j                  d       t        j                          t        j
                          | j                  j                  | j                         t        j                  |        t        t        | 6  | _        t        t        | :  |i | y r^   )r   nocbreakrK   r   echor   r   removeHandlerr   r   r   r   r   r  	terminater   s      r   ra  zBlinkenlights.terminate  s{    1 	!!$"7"78##D)-3	mT,d=f=r   c                 0    t        j                  | |       y r   )r   threadException)r   r  s     r   rc  zBlinkenlights.threadException  s    tV,r   r   r   )F)%rN   rO   rP   r   r   r   r   r   r   r   r   r   r  r  r  r  r
  r  r  r  r  r  r  r   r   rs   rw   r&  r3  r<  r   rF  rI  r   ra  rc  r   r   s   @r   r   r   V  s    
/"
$("&H/5689::?9;683
-82<,D
;2
#,#J%"
"(>-r   r   )	threadingr   r   r   r   collectionsr   rr   r   r   r   r   offlineimap.ui.UIBaser   offlineimap.threadutilr	   r|   r   rR   rm   r   r   r   r   rG   r   r   <module>rh     s   $ 8 7   
 	   ( 3 K' K'\U6 U6p- -`D# DN*w,, *4[-FJ [-r   