schedule.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. import threading
  6. # GTK module ( Graphical interface
  7. import gi
  8. gi.require_version('Gtk', '3.0')
  9. from gi.repository import Gtk
  10. from gi.repository import GLib
  11. from gi.repository import Gdk
  12. import cairo
  13. # Own modules
  14. from settings import settings
  15. from settings import talk
  16. from project_manager import pm_project
  17. from studio import analytics
  18. from studio import studio_nodes
  19. from studio import studio_dialogs
  20. from studio import story
  21. from studio import analytics
  22. from studio import checklist
  23. #UI modules
  24. from UI import UI_elements
  25. from UI import UI_color
  26. ################################################################################
  27. # This file is going to handle SCHEDULING. Primarily the output of the schedules
  28. # to the screen. But also probably a lot of other things related to schediles.
  29. ################################################################################
  30. def filter(project, dates):
  31. ############################################################################
  32. # This function is called on reading of analytics read. It will check whether
  33. # schedules are finished. Or if they are even found. Using studio/checklist.py
  34. # as a help.
  35. ############################################################################
  36. checkcashe = {} # Temporary checklist cashe.
  37. delete = [] # Stuff I want to delete later
  38. for date in dates:
  39. for i in ["assets", "scenes", "files"]:
  40. if i in dates[date]:
  41. for url in dates[date][i]:
  42. for num, entry in enumerate(dates[date][i][url]):
  43. if dates[date][i][url][num][1] == "schedule":
  44. # First of all. I didn't think about it when
  45. # writting the analytics read
  46. # that I will need to know whether schedules
  47. # are [Checked] or [Un-Checked]
  48. if not "[Checked]" in dates[date][i][url][num]\
  49. and not "[Un-Checked]" in dates[date][i][url][num]:
  50. dates[date][i][url][num].insert(3, "[Un-Checked]")
  51. # Then let's actually try to find whether the task
  52. # is checked.
  53. # For now let's do in a simple way. Because
  54. # if the whole checklist is 100% done. Then
  55. # obviously the task is also checked.
  56. path = dates[date][i][url][num][2]
  57. try:
  58. # Let's check if path exists in the project first.
  59. if os.path.exists(project+"/dev"+url+path):
  60. path = project+"/dev"+url+path
  61. elif os.path.exists(project+"/rnd"+url+path):
  62. path = project+"/rnd"+url+path
  63. elif os.path.exists(project+"/set/"+path):
  64. path = project+"/set/"+path
  65. elif os.path.exists(project+"/"+path):
  66. path = project+"/"+path
  67. if path not in checkcashe:
  68. checkcashe[path] = checklist.get_list(path)
  69. # Here we check whether the checklist is 100%
  70. if checkcashe[path]["fraction"] == 1.0:
  71. dates[date][i][url][num][3] = "[Checked]"
  72. # But what if it's not? Now we need to check
  73. # them 1 by one.
  74. else:
  75. dates[date][i][url][num][3] = "[Un-Checked]"
  76. task = checklist.get_task_by_path(checkcashe[path]["subtasks"],dates[date][i][url][num][4])
  77. # Now if the task is not found. We delete the schedule
  78. if task:
  79. if task["fraction"] == 1.0:
  80. dates[date][i][url][num][3] = "[Checked]"
  81. else:
  82. delete.append([date,i,url,dates[date][i][url][num]])
  83. except:
  84. delete.append([date,i,url,dates[date][i][url][num]])
  85. # Deleting all kinds of buddah
  86. for i in delete:
  87. q,w,e,r = i
  88. if r in dates[q][w][e]:
  89. dates[q][w][e].remove(r)
  90. return dates
  91. def get_schedules(dates):
  92. ############################################################################
  93. # This function will parse the dates data from the analytics. And get only
  94. # the schedules. In the format {date {url, [list of schedules]}}
  95. ############################################################################
  96. newdates = {}
  97. for date in dates:
  98. if date not in newdates:
  99. newdates[date] = {}
  100. for i in ["assets", "scenes", "files"]:
  101. if i in dates[date]:
  102. for url in dates[date][i]:
  103. for num, entry in enumerate(dates[date][i][url]):
  104. if entry[1] == "schedule":
  105. if url not in newdates[date]:
  106. newdates[date][url] = []
  107. if [entry, num] not in newdates[date][url]:
  108. newdates[date][url].append([entry, num])
  109. return newdates
  110. def draw(outlayer, win):
  111. x = 10
  112. y = 70
  113. width = win.current["w"] / 4 - 20
  114. height = win.current["h"] - 80
  115. # At the top above the schedules. In the bar. i want to put 2
  116. # buttons. One will hide done schedules, the other will
  117. # hide everything scheduled for other users.
  118. if "schedule_layer_settings" not in win.current:
  119. win.current["schedule_layer_settings"] = {
  120. "checked":False,
  121. "multiuser":False
  122. }
  123. for num, button in enumerate(win.current["schedule_layer_settings"]):
  124. if win.current["schedule_layer_settings"][button]:
  125. UI_color.set(outlayer, win, "progress_time")
  126. UI_elements.roundrect(outlayer, win,
  127. 20+width-(40*num)-60,
  128. 15,
  129. 40,
  130. 40,
  131. 10)
  132. def do():
  133. win.current["schedule_layer_settings"][button] = not win.current["schedule_layer_settings"][button]
  134. UI_elements.roundrect(outlayer, win,
  135. 20+width-(40*num)-60,
  136. 15,
  137. 40,
  138. 40,
  139. 10,
  140. do,
  141. button)
  142. # Now let's make a layer.
  143. # Making the layer
  144. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  145. layer = cairo.Context(surface)
  146. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  147. # Clip
  148. UI_elements.roundrect(layer, win,
  149. 0,
  150. 0,
  151. width,
  152. height,
  153. 10,
  154. fill=False)
  155. layer.clip()
  156. # Getting various screadule data
  157. schedules = get_schedules(win.analytics["dates"])
  158. new_date_format = "%Y/%m/%d"
  159. today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
  160. UI_elements.text(outlayer, win, "current_date_setting",
  161. width-100-(width-6*40),
  162. 15,
  163. (width-6*40),
  164. 40,
  165. set_text=win.current["date"])
  166. if win.text["current_date_setting"]["text"] != win.current["date"]\
  167. and analytics.ifdate(win.text["current_date_setting"]["text"]):
  168. def do():
  169. win.current["date"] = win.text["current_date_setting"]["text"]
  170. win.textactive = ""
  171. UI_elements.roundrect(outlayer, win,
  172. width-140,
  173. 15,
  174. 40,
  175. 40,
  176. 10,
  177. button=do,
  178. icon="ok",
  179. tip=talk.text("checked"))
  180. elif win.current["date"] != today:
  181. def do():
  182. win.current["date"] = today
  183. win.text["current_date_setting"]["text"] = today
  184. win.textactive = ""
  185. UI_elements.roundrect(outlayer, win,
  186. width-140,
  187. 15,
  188. 40,
  189. 40,
  190. 10,
  191. button=do,
  192. icon="cancel",
  193. tip=talk.text("cancel"))
  194. slist = []
  195. for date in schedules:
  196. if win.current["date"] != today and date != win.current["date"]:
  197. continue
  198. for item in schedules[date]:
  199. if win.cur in item or not win.cur or win.cur == "/set":
  200. slist.append([date, item, schedules[date][item]])
  201. # Selection magic
  202. if "schedule_task_selected" not in win.current:
  203. win.current["schedule_task_selected"] = False
  204. # Let's draw them to the screen in some way
  205. if "schedule" not in win.scroll:
  206. win.scroll["schedule"] = 0
  207. current_Y = 0
  208. for entry in slist:
  209. for thing in entry[2]:
  210. # Parsing the cur to get name and type
  211. name = entry[1][entry[1].rfind("/")+1:]
  212. acur = entry[1].replace(name, "").replace("/", "")
  213. if entry[1].count("/") > 1:
  214. tmp = entry[1].replace("/","",1).split("/")
  215. acur, name = tmp[0], tmp[1]
  216. else:
  217. name = entry[1][entry[1].rfind("/")+1:]
  218. acur = ""
  219. fullurl = ""
  220. for e in thing[0][4][:-1]:
  221. fullurl = fullurl+e+" > "
  222. if acur in ["chr", "veh", "loc","obj"]:
  223. itemtype = "assets"
  224. elif not acur:
  225. itemtype = "files"
  226. else:
  227. itemtype = "scenes"
  228. try:
  229. if not win.analytics["dates"][entry[0]][itemtype]\
  230. [entry[1]]:
  231. continue
  232. except:
  233. continue
  234. UI_color.set(layer, win, "node_background")
  235. # If not all users show only current user's tasks
  236. if not win.current["schedule_layer_settings"]["multiuser"]\
  237. and thing[0][-1] != win.settings["Username"]:
  238. continue
  239. # If the task is checked
  240. if thing[0][3] == "[Checked]":
  241. if not win.current["schedule_layer_settings"]["checked"]:
  242. continue
  243. UI_color.set(layer, win, "node_blendfile") # The Green
  244. elif entry[0] != "1997/07/30":
  245. if entry[0] < today:
  246. UI_color.set(layer, win, "node_badfile") # The Red
  247. elif entry[0] > today:
  248. UI_color.set(layer, win, "node_asset") # The Purple
  249. UI_elements.roundrect(layer, win,
  250. 0,
  251. win.scroll["schedule"] + current_Y,
  252. width,
  253. 75,
  254. 10)
  255. # Selection button
  256. def do():
  257. if win.current["tool"] == "selection":
  258. if win.current["schedule_task_selected"] != [ thing, entry[1] ]:
  259. win.current["schedule_task_selected"] = [ thing, entry[1] ]
  260. else:
  261. win.current["schedule_task_selected"] = False
  262. # Clearing the text
  263. try:
  264. del win.text["schedule_username_setting"]
  265. del win.text["schedule_date_setting"]
  266. del win.text["schedule_time_setting"]
  267. except:
  268. pass
  269. UI_elements.roundrect(layer, win,
  270. 0,
  271. win.scroll["schedule"] + current_Y,
  272. width,
  273. 75,
  274. 10,
  275. button=do,
  276. tip=entry[1]+" : "+fullurl+thing[0][4][-1],
  277. offset=[x,y],
  278. fill=False)
  279. layer.stroke()
  280. ############# GRABBING FOR RE-SCHEDULING ##############
  281. # Grab
  282. if win.current["LMB"]\
  283. and int(win.current["LMB"][0]) in range(int(x), int(x+width))\
  284. and int(win.current["LMB"][1]) in range(int(y+win.scroll["schedule"] + current_Y), int(y+win.scroll["schedule"] + current_Y+75))\
  285. and win.current["tool"] == "selection"\
  286. and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-2), int(win.current["mx"]+2))\
  287. and int(win.current["LMB"][1]) not in range(int(win.current["my"]-2), int(win.current["my"]+2)):
  288. try:
  289. pop = win.analytics["dates"][entry[0]][itemtype]\
  290. [entry[1]].pop(thing[1])
  291. dev = ""
  292. if itemtype == "assets":
  293. dev = "/dev"
  294. if itemtype == "scenes":
  295. dev = "/rnd"
  296. win.current["tool"] = "schedule"
  297. win.current["grab_data"] = [dev+entry[1]+thing[0][2], win.url, entry[1], pop[4], pop[-1]]
  298. win.url = "analytics"
  299. except:
  300. pass
  301. # If you leave it here.
  302. if win.current["tool"] == "schedule" and not win.current["LMB"]:
  303. path, back, cur, schedulepath, username = win.current["grab_data"].copy()
  304. path = path.replace(win.project, "")
  305. path = path[path.find(cur)+len(cur):]
  306. tname = cur[cur.rfind("/")+1:]
  307. tacur = cur.replace(name, "").replace("/", "")
  308. if tacur in ["chr", "veh", "loc","obj"]:
  309. itemtype = "assets"
  310. elif not tacur:
  311. itemtype = "files"
  312. else:
  313. itemtype = "scenes"
  314. theday = win.current["date"]
  315. if theday not in win.analytics["dates"]:
  316. win.analytics["dates"][theday] = {}
  317. if itemtype not in win.analytics["dates"][theday]:
  318. win.analytics["dates"][theday][itemtype] = {}
  319. if cur not in win.analytics["dates"][theday][itemtype]:
  320. win.analytics["dates"][theday][itemtype][cur] = []
  321. win.analytics["dates"][theday][itemtype][cur].append(
  322. ["00:00:00",
  323. "schedule",
  324. path,
  325. "[Un-Checked]",
  326. schedulepath,
  327. username]
  328. )
  329. # RETURNING BACK TO NORMAL
  330. win.url = back
  331. win.current["tool"] = "selection"
  332. analytics.save(win.project, win.analytics)
  333. win.analytics = analytics.load(win.project)
  334. win.checklists = {}
  335. # Multiuser sycning
  336. win.multiuser["request"] = "analytics"
  337. #########################################################
  338. # ICON
  339. UI_elements.image(layer, win,
  340. "settings/themes/"+win.settings["Theme"]+"/icons/schedule.png",
  341. 5, win.scroll["schedule"] + current_Y+5, 40, 40)
  342. # MAIN TASK
  343. UI_color.set(layer, win, "text_normal")
  344. layer.set_font_size(20)
  345. layer.move_to(
  346. 50,
  347. win.scroll["schedule"] + current_Y+25,
  348. )
  349. layer.show_text(thing[0][4][-1])
  350. # TASK URL INSIDE THE CHECKLIST
  351. if fullurl:
  352. layer.set_font_size(10)
  353. layer.move_to(
  354. 60+len(thing[0][4][-1])*12,
  355. win.scroll["schedule"] + current_Y+25,
  356. )
  357. layer.show_text(fullurl)
  358. # NAME OF THE ASSET / SHOT
  359. if acur in ["chr", "veh", "loc", "obj"]:
  360. assetname = talk.text(acur)+": "+name
  361. else:
  362. assetname = entry[1].replace("/set", "")
  363. layer.set_font_size(15)
  364. layer.move_to(
  365. 50,
  366. win.scroll["schedule"] + current_Y+45,
  367. )
  368. layer.show_text(assetname)
  369. # DATE
  370. if entry[0] != "1997/07/30" and entry[0] != today:
  371. layer.set_font_size(15)
  372. layer.move_to(
  373. width-130,
  374. win.scroll["schedule"] + current_Y+45,
  375. )
  376. layer.show_text(entry[0])
  377. # TIME
  378. layer.set_font_size(15)
  379. layer.move_to(
  380. width-130,
  381. win.scroll["schedule"] + current_Y+65,
  382. )
  383. layer.show_text(thing[0][0])
  384. # USERNAME
  385. layer.set_font_size(15)
  386. layer.move_to(
  387. 20,
  388. win.scroll["schedule"] + current_Y+65,
  389. )
  390. layer.show_text(talk.text("user_schedules")+" "+thing[0][-1])
  391. # IF SELECTED THERE WILL BE MORE STUFF
  392. if win.current["schedule_task_selected"] == [ thing, entry[1] ]:
  393. UI_color.set(layer, win, "text_normal")
  394. UI_elements.roundrect(layer, win,
  395. 1,
  396. win.scroll["schedule"] + current_Y,
  397. width-2,
  398. 75,
  399. 10,
  400. fill=False)
  401. layer.stroke()
  402. current_Y = current_Y + 85
  403. # If it's a task from an asset or a scene. There should be a link
  404. # to it. But first let's check that's it's infect an asset.
  405. # because...
  406. if entry[1] and entry[1] != "/set":
  407. goto = "script"
  408. itemtype = "scenes"
  409. # If asset:
  410. if acur in ["chr", "veh", "loc","obj"]:
  411. # ICON
  412. UI_elements.image(layer, win,
  413. "settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png",
  414. 25, win.scroll["schedule"] + current_Y+5, 40, 40)
  415. goto = "assets"
  416. itemtype = "assets"
  417. elif not acur:
  418. itemtype = "files"
  419. else:
  420. goto = "script"
  421. itemtype = "scenes"
  422. if goto == "script":
  423. # ICON
  424. UI_elements.image(layer, win,
  425. "settings/themes/"+win.settings["Theme"]+"/icons/scene.png",
  426. 25, win.scroll["schedule"] + current_Y+5, 40, 40)
  427. # Here comes the link button
  428. def do():
  429. win.url = goto
  430. win.cur = entry[1]
  431. win.current["asset_left_panel"] = "schedule"
  432. UI_elements.roundrect(layer, win,
  433. 20,
  434. win.scroll["schedule"] + current_Y+5,
  435. width-20,
  436. 40,
  437. 10,
  438. button=do,
  439. tip=entry[1]+" : "+fullurl+thing[0][4][-1],
  440. offset=[x,y],
  441. fill=False)
  442. layer.stroke()
  443. #Title
  444. UI_color.set(layer, win, "text_normal")
  445. layer.set_font_size(20)
  446. layer.move_to(
  447. 80,
  448. win.scroll["schedule"] + current_Y+30,
  449. )
  450. layer.show_text(assetname)
  451. current_Y = current_Y + 50
  452. # Next thing! Let's show the most editable values. I think the first
  453. # one. For the director will be important. Is to edit the USER.
  454. # or in other words. Who is doing the task.
  455. # In the end It will be a drop down menu. Like all of it. But for this
  456. # I need to implement MULTIUSER first. And it's not a priority right
  457. # now. So a simple text editor could be okay.
  458. # ICON
  459. UI_elements.image(layer, win,
  460. "settings/themes/"+win.settings["Theme"]+"/icons/user.png",
  461. 25, win.scroll["schedule"] + current_Y+5, 40, 40)
  462. UI_elements.text(layer, win, "schedule_username_setting",
  463. 80,
  464. win.scroll["schedule"] + current_Y+5,
  465. width-80,
  466. 40,
  467. set_text=thing[0][-1],
  468. tip=talk.text("username"),
  469. offset=[x,y])
  470. if win.text["schedule_username_setting"]["text"] != thing[0][-1]:
  471. def do():
  472. thing[0][-1] = win.text["schedule_username_setting"]["text"]
  473. analytics.save(win.project, win.analytics)
  474. # Multiuser sycning
  475. win.multiuser["request"] = "analytics"
  476. UI_elements.roundrect(layer, win,
  477. width-40,
  478. win.scroll["schedule"] + current_Y+5,
  479. 40,
  480. 40,
  481. 10,
  482. button=do,
  483. icon="ok",
  484. tip=talk.text("checked"),
  485. offset=[x,y])
  486. current_Y = current_Y + 50
  487. # Multiuser now gave a way to give who ever does the assingments
  488. # a tool to select the name from a drop down menu. Rather then
  489. # type it by hand. I will still have the ability to type. What
  490. # if that someone is not logged in currently.
  491. if win.textactive == "schedule_username_setting":
  492. # So when you clicked to edit a drop down menu will appear.
  493. for user in win.multiuser["users"]:
  494. # It's going to be simple buttons with user icons and
  495. # the username.
  496. def do():
  497. thing[0][-1] = win.multiuser["users"][user]["username"]
  498. analytics.save(win.project, win.analytics)
  499. win.text["schedule_username_setting"]["text"] = thing[0][-1]
  500. win.textactive = ""
  501. # Multiuser sycning
  502. win.multiuser["request"] = "analytics"
  503. UI_elements.roundrect(layer, win,
  504. 80,
  505. win.scroll["schedule"] + current_Y+5,
  506. width-80,
  507. 40,
  508. 10,
  509. button=do,
  510. icon="user",
  511. tip=win.multiuser["users"][user]["username"],
  512. offset=[x,y])
  513. UI_color.set(layer, win, "text_normal")
  514. layer.set_font_size(20)
  515. layer.move_to(
  516. 130,
  517. win.scroll["schedule"] + current_Y+30,
  518. )
  519. layer.show_text(win.multiuser["users"][user]["username"])
  520. current_Y = current_Y + 50
  521. # DATE : TIME
  522. UI_elements.image(layer, win,
  523. "settings/themes/"+win.settings["Theme"]+"/icons/schedule.png",
  524. 25, win.scroll["schedule"] + current_Y+5, 40, 40)
  525. UI_elements.text(layer, win, "schedule_date_setting",
  526. 80,
  527. win.scroll["schedule"] + current_Y+5,
  528. (width-80)/2-20,
  529. 40,
  530. set_text=entry[0],
  531. tip=talk.text("username"),
  532. offset=[x,y])
  533. ### DELETE KEY ###
  534. if acur in ["chr", "veh", "loc","obj"]:
  535. itemtype = "assets"
  536. elif not acur:
  537. itemtype = "files"
  538. else:
  539. itemtype = "scenes"
  540. if 65535 in win.current["keys"]:
  541. pop = win.analytics["dates"][entry[0]][itemtype]\
  542. [entry[1]].pop(thing[1])
  543. win.current["keys"] = []
  544. analytics.save(win.project, win.analytics)
  545. win.analytics = analytics.load(win.project)
  546. if win.text["schedule_date_setting"]["text"] != entry[0]\
  547. and analytics.ifdate(win.text["schedule_date_setting"]["text"]):
  548. def do():
  549. pop = win.analytics["dates"][entry[0]][itemtype]\
  550. [entry[1]].pop(thing[1])
  551. if win.text["schedule_date_setting"]["text"] not in win.analytics["dates"]:
  552. win.analytics["dates"][win.text["schedule_date_setting"]["text"]] = {}
  553. if itemtype not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]]:
  554. win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype] = {}
  555. if entry[1] not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]:
  556. win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype][entry[1]] = []
  557. win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]\
  558. [entry[1]].append(pop)
  559. analytics.save(win.project, win.analytics)
  560. win.analytics = analytics.load(win.project)
  561. # Multiuser sycning
  562. win.multiuser["request"] = "analytics"
  563. UI_elements.roundrect(layer, win,
  564. (width-80)/2+20,
  565. win.scroll["schedule"] + current_Y+5,
  566. 40,
  567. 40,
  568. 10,
  569. button=do,
  570. icon="ok",
  571. tip=talk.text("checked"),
  572. offset=[x,y])
  573. # TIME
  574. UI_elements.text(layer, win, "schedule_time_setting",
  575. 80+(width-80)/2,
  576. win.scroll["schedule"] + current_Y+5,
  577. (width-80)/2,
  578. 40,
  579. set_text=thing[0][0],
  580. tip=talk.text("username"),
  581. offset=[x,y])
  582. if win.text["schedule_time_setting"]["text"] != thing[0][0]\
  583. and analytics.iftime(win.text["schedule_time_setting"]["text"]):
  584. def do():
  585. thing[0][0] = win.text["schedule_time_setting"]["text"]
  586. analytics.save(win.project, win.analytics)
  587. win.analytics = analytics.load(win.project)
  588. # Multiuser sycning
  589. win.multiuser["request"] = "analytics"
  590. UI_elements.roundrect(layer, win,
  591. width-40,
  592. win.scroll["schedule"] + current_Y+5,
  593. 40,
  594. 40,
  595. 10,
  596. button=do,
  597. icon="ok",
  598. tip=talk.text("checked"),
  599. offset=[x,y])
  600. current_Y = current_Y + 70
  601. else:
  602. current_Y = current_Y + 85
  603. # Outputting the layer
  604. outlayer.set_source_surface(surface, x, y)
  605. outlayer.paint()
  606. # Scroll
  607. UI_elements.scroll_area(outlayer, win, "schedule",
  608. x+0,
  609. y+50,
  610. width,
  611. height-50,
  612. current_Y,
  613. bar=True,
  614. mmb=True)