checklist.py 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import cairo
  5. #UI modules
  6. from UI import UI_elements
  7. from UI import UI_color
  8. #settings
  9. from settings import talk
  10. #studio
  11. from studio import analytics
  12. from studio import history
  13. def get_list(filepath):
  14. # This fucntion converts text documents. (.progress) into a more machine
  15. # friendly recursive dict.
  16. # In the original organizer evaluation of the checklist was done separatelly
  17. # in a different function. Which made it very messy for recursive stuff.
  18. # I will attemp to combine it. And make it more clean as a result.
  19. checklist = {
  20. "fraction":0.0, # The percentage of a checklist. From 0.0 to 1.0
  21. "string":filepath,
  22. "editing":False,# Whether the string is during editing. UI.
  23. "selected":False,# Whether the current task is selected.
  24. "open":True, # Whether to draw the suptasks. UI.
  25. "subtasks":[] # List of subtastks. (In the same format as the checklist)
  26. }
  27. data = open(filepath)
  28. data = data.read()
  29. data = data.split("\n")
  30. # Let's filter out all the comments. Lines starting with #. For some reason
  31. # in the old organizer. I just thought it wasn't important. LOL. And just
  32. # started reading from the 10th line.
  33. # Here is an example of the first 9 lines.
  34. # 1 #### Blender orgainizer checklist format
  35. # 2 #### INDINTATION (4 SPACES LONG)
  36. # 3 #### STR means Start date of the ASSET
  37. # 4 #### FIN means Finish deadline of the asset
  38. # 5 #### [ ] means that task is on list
  39. # 6 #### [V] means that tast is finished
  40. # 7 #### DO NOT USE EMPTY LINES
  41. # 8 STR 24/02/2020
  42. # 9 FIN 01/05/2021
  43. # You can see a trace from a very long time ago. From the first versions
  44. # of the blender organizer. The STR and FIN values. Which are start and
  45. # deadline of the project.
  46. # I guess we need to filter it out a bit differently. Checking whether a
  47. # given line starts with a [ or with a number of spaces and [. This will
  48. # make the file a little more open to editing by hand. Without too much
  49. # worrying that something will break.
  50. cleandata = []
  51. for line in data:
  52. # So not to mangle the line.
  53. tmp = line
  54. while tmp.startswith(" "):
  55. tmp = tmp[1:]
  56. # Checking
  57. if tmp.startswith("[ ]") or tmp.startswith("[V]"):
  58. cleandata.append(line)
  59. # Now since we have the cleandata. We can try to parse it somehow into a
  60. # checklist thing. For this we need a reqursion. I gonna use a method from
  61. # the blender-organizer. By running the function with in itself. It's not
  62. # very wise. I know. In python3 it will give you no more then 996 recursions
  63. # untill it will declare an error. BUT. It's 996 layers of a subtaks.
  64. # For now I don't see a need in so many subtasks. Let's think of layers of
  65. # subtasks as of memory. This laptop has only 8 GB of RAM. I can't put more
  66. # data into it even if I wanted.
  67. def convert(part, indent=0):
  68. # If a thing have subtasks it should not even be a checkbox. So trying
  69. # To evaluate it's fraction by whether it's a [ ] or [V] shoun't be
  70. # done.
  71. subtask = []
  72. for num, line in enumerate(part):
  73. # Let's get the NEXT line. I'm not kidding. We gonna work with the
  74. # next line to see whether to count this lines [V]
  75. if line[indent:].startswith("["):
  76. thisline = {
  77. "fraction":0.0,
  78. "string":line[line.find("]")+2:],
  79. "editing":False,
  80. "selected":False,
  81. "open":False,
  82. "subtasks":[]
  83. }
  84. try:
  85. nextline = part[num+1]
  86. except:
  87. nextline = ""
  88. if not line[line.find("]")+1] == ".":
  89. thisline["open"] = True
  90. if nextline.find("[")-1 <= indent:
  91. if line[indent:].startswith("[V]"):
  92. thisline["fraction"] = 1.0
  93. else:
  94. subpart = []
  95. subdent = indent
  96. for n, l in enumerate(part[num+1:]):
  97. if n == 0:
  98. subdent = l.find("[")
  99. if l.find("[")-1 <= indent:
  100. break
  101. else:
  102. subpart.append(l)
  103. #print(subpart)
  104. thisline["subtasks"] = convert(subpart, subdent)
  105. fracs = []
  106. for task in thisline["subtasks"]:
  107. if not task["string"].startswith("#"):
  108. fracs.append(task["fraction"])
  109. try:
  110. thisline["fraction"] = sum(fracs) / len(fracs)
  111. except:
  112. thisline["fraction"] = 0.0
  113. # Sometime it was showing 99% when infect it's 100%
  114. if thisline["fraction"] == 0.9999:
  115. thisline["fraction"] = 1.0
  116. subtask.append(thisline)
  117. return subtask
  118. checklist["subtasks"] = convert(cleandata)
  119. fracs = []
  120. for task in checklist["subtasks"]:
  121. if not task["string"].startswith("#"):
  122. fracs.append(task["fraction"])
  123. try:
  124. checklist["fraction"] = sum(fracs) / len(fracs)
  125. except:
  126. checklist["fraction"] = 0.0
  127. # Sometime it was showing 99% when infect it's 100%
  128. if checklist["fraction"] == 0.9999:
  129. checklist["fraction"] = 1.0
  130. return checklist
  131. def get_fraction(win, path):
  132. ############################################################################
  133. # This function will return a fraction of a given checklist. It will ignore
  134. # complitelly what is the asset / shot / project the checklist is from.
  135. # For sake of making this function actually somewhat useful, aka not bloated
  136. # I will use it to cashe the whole checklist data objects. So they could be
  137. # easily accesable later on.
  138. # This is usefull so I would not need to create 2 cash data structures. One
  139. # for fractions and for the checklists them selves.
  140. ############################################################################
  141. if path not in win.checklists:
  142. # Let's check if path exists in the project first.
  143. if os.path.exists(win.project+"/"+path):
  144. win.checklists[path] = get_list(win.project+"/"+path)
  145. else:
  146. win.checklists[path] = get_list(path)
  147. # Let's now return back the fraction
  148. return win.checklists[path]["fraction"]
  149. def get_task_by_path(tasks, path, p=[]):
  150. ############################################################################
  151. # This function will give the information about a given task from a non
  152. # recursive URL such as ["Task", "Sub-task", "Sub-task2"]. This will look
  153. # the recursive list and output the given task. In this case "Sub-task2"
  154. # with all the information inside it.
  155. ############################################################################
  156. for task in tasks:
  157. pa = p.copy()
  158. pa.append(" "+task["string"])
  159. if pa == path:
  160. return task
  161. if task["subtasks"]:
  162. t = get_task_by_path(task["subtasks"], path, pa)
  163. if t:
  164. return t
  165. return False
  166. def filter_tasks(data):
  167. ############################################################################
  168. # This function going to remove all selections and all edits from a given
  169. # checklist. This is nessesary for being able to select one task without
  170. # manually deselcting the other. And since the checklist is recursive it's
  171. # not a trivial task.
  172. ############################################################################
  173. data["selected"] = False
  174. data["editing"] = False
  175. if data["subtasks"]:
  176. for i in data["subtasks"]:
  177. filter_tasks(i)
  178. def save(path, data):
  179. ############################################################################
  180. # This funtion will save the checklist into a .progress file. Formatted
  181. # similarly as in the old Blender-Organizer. But as easy to understand.
  182. #
  183. # NOTE: I will remove some stuff from the file. Mainly the comments in the
  184. # beginning and the STR and END data. Which are no longer needed in a
  185. # VCStudio project.
  186. #
  187. # But since some part of Legacy Organizer relies on those, it's not adviced
  188. # to open the projects that you are planning to work on with Blender-Organizer
  189. # in VCStudio.
  190. ############################################################################
  191. indent = [0]
  192. lines = []
  193. def writetasks(tasks):
  194. for task in tasks:
  195. if task["fraction"] and not task["subtasks"]:
  196. v = "[V]"
  197. else:
  198. v = "[ ]"
  199. if task["open"]:
  200. o = " "
  201. else:
  202. o = "."
  203. lines.append(" "*indent[0]+v+o+task["string"])
  204. if task["subtasks"]:
  205. indent[0] = indent[0] + 4
  206. writetasks(task["subtasks"])
  207. indent[0] = indent[0] - 4
  208. writetasks(data["subtasks"])
  209. # Writting to file
  210. w = open(path, "w")
  211. for i in lines:
  212. w.write(i+"\n")
  213. w.close()
  214. def draw(outlayer, win, path, back="story_editor"):
  215. ############################################################################
  216. # This function will draw the checklists to the screen.
  217. # You probably noticed that there is no x, y, width or height values that
  218. # you can input. This due to one idea that I have in mind.
  219. # I'm planning to make scheduling a kind of interactive process rather then
  220. # a boring date selection. Something that's going to be somewhat fun to do.
  221. # The idea is to grab the task and to drug it outside the checklist. Which
  222. # will call for an analytics window to show up, where you can put the
  223. # schedules into a given date.
  224. # For this reason the checklist should always be in the same spot on the
  225. # screen. Which is a (window width) / 4 at the right side.
  226. ############################################################################
  227. if path not in win.checklists:
  228. # Let's check if path exists in the project first.
  229. if os.path.exists(win.project+"/"+path):
  230. win.checklists[path] = get_list(win.project+"/"+path)
  231. elif os.path.exists(win.project+"/rnd"+path):
  232. win.checklists[path] = get_list(win.project+"/rnd"+path)
  233. else:
  234. win.checklists[path] = get_list(path)
  235. x = win.current["w"] / 4 * 3 + 10
  236. y = 10
  237. width = win.current["w"] / 4 - 20
  238. height = win.current["h"] - 20
  239. # Now let's make a layer.
  240. # Making the layer
  241. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  242. layer = cairo.Context(surface)
  243. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  244. # Clip
  245. UI_elements.roundrect(layer, win,
  246. 0,
  247. 0,
  248. width,
  249. height,
  250. 10,
  251. fill=False)
  252. layer.clip()
  253. # Checklist icon.
  254. UI_color.set(layer, win, "node_background")
  255. UI_elements.roundrect(layer, win,
  256. 0,
  257. 0,
  258. width,
  259. 50,
  260. 10)
  261. UI_elements.image(layer, win,
  262. "settings/themes/"+win.settings["Theme"]+"/icons/checklist.png",
  263. 5, 5, 40, 40)
  264. # Fraction
  265. fraction = win.checklists[path]["fraction"]
  266. UI_color.set(layer, win, "progress_background")
  267. UI_elements.roundrect(layer, win,
  268. 50,
  269. 17,
  270. width - 60,
  271. 0,
  272. 7)
  273. UI_color.set(layer, win, "progress_active")
  274. UI_elements.roundrect(layer, win,
  275. 50,
  276. 17,
  277. (width - 60 )*fraction,
  278. 0,
  279. 7)
  280. # Clip
  281. UI_elements.roundrect(layer, win,
  282. 0,
  283. 60,
  284. width,
  285. height,
  286. 10,
  287. fill=False)
  288. layer.clip()
  289. ###########################################################################
  290. # NOW THIS IS THE HARD RECURSION PART! THERE IS GOING TO BE SOME REDIC!
  291. ###########################################################################
  292. tileX = 0
  293. current_Y = 70
  294. # There is some bullshit regarding the Global variables. I gonna do a bit
  295. # trickier. Since lists are only links to lists. Let's do this.
  296. cXY = [tileX, current_Y]
  297. if "checklist" not in win.scroll:
  298. win.scroll["checklist"] = 0
  299. if "moving_task_now" not in win.current:
  300. win.current["moving_task_now"] = False
  301. def draw_tasks(tasks, cXY, schedulep):
  302. #########################################
  303. # #
  304. # THIS IS THE RECURSIVE FUNCTION #
  305. # #
  306. #########################################
  307. # I will try to explain what is going on here. But it will require me
  308. # understanding the code myself. And it's a bit redic. There was a hell
  309. # of a lot of copy-paste, edit value, continue.
  310. for num , task in enumerate(tasks):
  311. # This is the code that will be done for each task in the list. Or
  312. # subtask if a task have them.
  313. # Let's get a schedule path. A folder like structure.
  314. # Exacmple:
  315. # Instead of:
  316. # Task
  317. # Sub-Task
  318. # Sub-Task 2
  319. # We get:
  320. # [" Task", " Sub-Task", " Sub-Task 2"]
  321. schedulepath = schedulep.copy()
  322. schedulepath.append(" "+task["string"])
  323. ###### SCHEDULING STUFF BACKWARD ######
  324. # This is a set of stuff to make schedules work with checklists.
  325. if "schedule_task_selected" not in win.current:
  326. win.current["schedule_task_selected"] = False
  327. if win.current["schedule_task_selected"] and win.cur == win.current["schedule_task_selected"][-1]\
  328. or win.current["schedule_task_selected"] and win.cur == "/set" and win.current["schedule_task_selected"][-1] == "":
  329. csl = win.current["schedule_task_selected"][0][0][4]
  330. if " "+task["string"] in csl and not task["open"]:
  331. task["open"] = True
  332. if schedulepath == csl and not task["selected"]:
  333. filter_tasks(win.checklists[path])
  334. task["selected"] = True
  335. win.scroll["checklist"] = 0 - cXY[1] + height/2
  336. #### DELETE SHORT KEY ####
  337. # There is probably a reason to put it here. Probably something
  338. # breaks if you put it anywhere else. IDK actually. Test it. I
  339. # don't remember
  340. if 65535 in win.current["keys"] and task["selected"] and not win.current["schedule_task_selected"]:
  341. del tasks[num]
  342. win.current["keys"] = []
  343. # Saving
  344. save(path, win.checklists[path])
  345. win.checklists = {}
  346. win.assets = {}
  347. win.analytics = analytics.load(win.project)
  348. win.multiuser["last_request"] = ""
  349. #### THE GRABBING FUNCTION ####
  350. # This is the activation of the grab tool. It uses the same
  351. # win.current["tool"] = "grab" as the story editor. Because it makes
  352. # sense to reuse it if it already exists. For sake of conviniense.
  353. # It doesn't mean that it uses the same code. It's a bit different.
  354. # due to the nature of lists vs free positioning items.
  355. # Now let's set up a few positional variables. So I could move the tasks
  356. # while moving the currently selected task. And in the same time will not
  357. # screw the data.
  358. # So I copy the X and the Y locations like this.
  359. sx = cXY[0]
  360. sy = win.scroll["checklist"] + cXY[1]
  361. grabY = 40
  362. if task["subtasks"]:
  363. grabY = 60
  364. # Grab
  365. if win.current["LMB"]\
  366. and int(win.current["LMB"][0]) in range(int(x+sx), int(x+sx+width))\
  367. and int(win.current["LMB"][1]) in range(int(y+sy), int(y+sy+grabY))\
  368. and win.current["tool"] == "selection"\
  369. and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-2), int(win.current["mx"]+2))\
  370. and int(win.current["LMB"][1]) not in range(int(win.current["my"]-2), int(win.current["my"]+2)):
  371. # So here we set up the grab tool and creating a little variable.
  372. # This variable (moving_task_now) will consist of 2 parts on release.
  373. # 1. The entire task ( with subtasks ) using pop. Which is not simply
  374. # a copy. But also removing the task from the checklist's list.
  375. # 2. The current frame of poping. So to offset insertion for 1 frame.
  376. # This insures that you insert the task in the correct spot.
  377. filter_tasks(win.checklists[path])
  378. task["selected"] = True
  379. win.current["schedule_task_selected"] = False
  380. win.current["tool"] = "grab"
  381. win.current["moving_task_now"] = False
  382. # Now let's actually setup the pop. So when the mouse is now pressed, but
  383. # was on a previous framae, and our current tool is grab.
  384. if not win.current["LMB"] and win.previous["LMB"] and win.current["tool"] == "grab"\
  385. and task["selected"]:
  386. # We remove the task from the list. And write it to the variable. With
  387. # the current frame.
  388. win.current["moving_task_now"] = [tasks.pop(num), win.current["frame"]]
  389. # Now I can touch the sx and sy without screwing up the cXY or scroll.
  390. thismoving = False # This is going to be True is this is the task that's
  391. # moving currently
  392. somemoving = False # This is going to be True if any task is moving
  393. someXY = [0,0] # And this is the mouse position translated to location
  394. # of the checklist. (x, y coordinates of the whole
  395. # widget). Or in other words position of the task.
  396. # Then comes some logic to determen those 3 values.
  397. if win.current["tool"] == "grab":
  398. # Okay so here. If grab first we assume that it's some other
  399. # task. And now currently selected one.
  400. somemoving = True
  401. someXY = [
  402. win.current["mx"]-x,
  403. win.current["my"]-y
  404. ]
  405. # Now this is the editing of the sx and sy values. To be at
  406. # the position of the mouse.
  407. if task["selected"]:
  408. # Calculating so the mouse will end up about in the centre
  409. # of the task while the task is at motion.
  410. sx = win.current["mx"] - x - (width - cXY[0])/2
  411. sy = win.current["my"] - y - 10
  412. task["open"] = False
  413. thismoving = True
  414. somemoving = False
  415. # Now if the mouse is outside the frame of the checklist
  416. # it will activate the scheduling.
  417. ############################################################
  418. # SCHEDULING PART #
  419. ############################################################
  420. if win.current["mx"] < x:
  421. win.url = "analytics"
  422. win.current["grab_data"] = [path, back, win.cur, schedulepath, win.settings["Username"]]
  423. win.current["tool"] = "schedule"
  424. #
  425. #
  426. #
  427. # SEE studio/studio_analyticsLayer.py
  428. # for more details about scheduling
  429. #
  430. # And if back into frame it will return to normal grab mode.
  431. elif win.current["tool"] == "schedule" and win.current["mx"] > x:
  432. win.url = back
  433. win.current["tool"] = "grab"
  434. ####################################################################
  435. # SCHEDULING PART END #
  436. ####################################################################
  437. inside = False
  438. between = False
  439. if sy-10 < someXY[1] < sy+10 and somemoving and not task["selected"]:
  440. if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
  441. tasks.insert(num, win.current["moving_task_now"][0])
  442. win.current["tool"] = "selection"
  443. win.current["LMB"] = False
  444. win.previous["LMB"] = False
  445. # Saving
  446. save(path, win.checklists[path])
  447. win.checklists = {}
  448. win.assets = {}
  449. win.analytics = analytics.load(win.project)
  450. win.multiuser["last_request"] = ""
  451. elif win.current["LMB"]:
  452. for line in range(int(cXY[0]/20)):
  453. line = line * 20 + 10
  454. UI_color.set(layer, win, "node_background")
  455. layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
  456. layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
  457. layer.stroke()
  458. cXY[1] = cXY[1] + 50
  459. sx = cXY[0]
  460. sy = win.scroll["checklist"] + cXY[1]
  461. between = True
  462. somemoving = False
  463. elif sy < someXY[1] < sy + 40 and somemoving and not task["selected"]:
  464. if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
  465. task["subtasks"].append(win.current["moving_task_now"][0])
  466. win.current["tool"] = "selection"
  467. win.current["LMB"] = False
  468. win.previous["LMB"] = False
  469. # Saving
  470. save(path, win.checklists[path])
  471. win.checklists = {}
  472. win.assets = {}
  473. win.analytics = analytics.load(win.project)
  474. win.multiuser["last_request"] = ""
  475. elif win.current["LMB"]:
  476. inside = True
  477. # Selection
  478. if win.textactive != "editing_task" and win.current["tool"] == "selection":
  479. def do():
  480. ed = task["selected"] and not task["editing"]
  481. filter_tasks(win.checklists[path])
  482. win.current["schedule_task_selected"] = False
  483. task["selected"] = True
  484. if ed:
  485. task["editing"] = True
  486. UI_elements.roundrect(layer, win,
  487. sx+40,
  488. sy,
  489. width - cXY[0]-40,
  490. grabY,
  491. 10,
  492. button=do,
  493. offset=[x,y],
  494. fill=False)
  495. layer.stroke()
  496. # Background
  497. if not task["subtasks"]:
  498. UI_color.set(layer, win, "node_background")
  499. if task["string"].startswith("#"):
  500. UI_color.set(layer, win, "dark_overdrop")
  501. UI_elements.roundrect(layer, win,
  502. sx,
  503. sy,
  504. width - cXY[0],
  505. 40,
  506. 10)
  507. if inside:
  508. UI_color.set(layer, win, "progress_background")
  509. UI_elements.roundrect(layer, win,
  510. sx,
  511. sy,
  512. width - cXY[0],
  513. 40,
  514. 10,
  515. fill=False)
  516. layer.stroke()
  517. if task["selected"]:
  518. UI_color.set(layer, win, "text_normal")
  519. UI_elements.roundrect(layer, win,
  520. sx,
  521. sy,
  522. width - cXY[0],
  523. 40,
  524. 10,
  525. fill=False)
  526. layer.stroke()
  527. # Line to see how deep you are in
  528. for line in range(int(cXY[0]/20)):
  529. line = line * 20 + 10
  530. UI_color.set(layer, win, "node_background")
  531. layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
  532. layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
  533. layer.stroke()
  534. if not task["string"].startswith("#"):
  535. if task["fraction"]:
  536. im = "checked"
  537. else:
  538. im = "unchecked"
  539. # CHECK BUTTON
  540. def do():
  541. if task["fraction"]:
  542. task["fraction"] = 0.0
  543. history.record(win, path, "[Un-Checked]", schedulepath)
  544. else:
  545. task["fraction"] = 1.0
  546. history.record(win, path, "[Checked]", schedulepath)
  547. # Saving
  548. save(path, win.checklists[path])
  549. win.checklists = {}
  550. win.assets = {}
  551. win.multiuser["last_request"] = ""
  552. win.analytics = analytics.load(win.project)
  553. UI_elements.roundrect(layer, win,
  554. sx,
  555. sy,
  556. 40,
  557. 40,
  558. 10,
  559. button=do,
  560. icon=im,
  561. offset=[x,y])
  562. else:
  563. UI_color.set(layer, win, "node_background")
  564. if task["string"].startswith("#"):
  565. UI_color.set(layer, win, "dark_overdrop")
  566. UI_elements.roundrect(layer, win,
  567. sx,
  568. sy,
  569. width - cXY[0],
  570. 60,
  571. 10)
  572. if inside:
  573. UI_color.set(layer, win, "progress_background")
  574. UI_elements.roundrect(layer, win,
  575. sx,
  576. sy,
  577. width - cXY[0],
  578. 60,
  579. 10,
  580. fill=False)
  581. layer.stroke()
  582. if task["selected"]:
  583. UI_color.set(layer, win, "text_normal")
  584. UI_elements.roundrect(layer, win,
  585. sx,
  586. sy,
  587. width - cXY[0],
  588. 60,
  589. 10,
  590. fill=False)
  591. layer.stroke()
  592. # Line to see how deep you are in
  593. for line in range(int(cXY[0]/20)):
  594. line = line * 20 + 10
  595. UI_color.set(layer, win, "node_background")
  596. layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
  597. layer.line_to(line, win.scroll["checklist"] + cXY[1]+60)
  598. layer.stroke()
  599. # Fraction
  600. if not task["string"].startswith("#"):
  601. fraction = task["fraction"]
  602. UI_color.set(layer, win, "progress_background")
  603. UI_elements.roundrect(layer, win,
  604. sx+10,
  605. sy+45,
  606. width-20 - cXY[0],
  607. 0,
  608. 5)
  609. UI_color.set(layer, win, "progress_active")
  610. UI_elements.roundrect(layer, win,
  611. sx+10,
  612. sy+45,
  613. (width -20 - cXY[0])*fraction,
  614. 0,
  615. 5)
  616. if task["open"]:
  617. im = "open"
  618. else:
  619. im = "closed"
  620. def do():
  621. task["open"] = not task["open"]
  622. win.current["schedule_task_selected"] = False
  623. # Saving
  624. save(path, win.checklists[path])
  625. win.multiuser["last_request"] = ""
  626. UI_elements.roundrect(layer, win,
  627. sx,
  628. sy,
  629. 40,
  630. 60,
  631. 10,
  632. button=do,
  633. icon=im,
  634. offset=[x,y])
  635. # TEXT
  636. # ECS
  637. if 65307 in win.current["keys"] and task["editing"]:
  638. # It's here because Text entry has it's own ESC
  639. win.textactive = ""
  640. task["editing"] = False
  641. del win.text["editing_task"]
  642. win.current["keys"] = []
  643. # Saving
  644. save(path, win.checklists[path])
  645. win.checklists = {}
  646. win.assets = {}
  647. win.analytics = analytics.load(win.project)
  648. win.multiuser["last_request"] = ""
  649. if not task["editing"]:
  650. layer.set_font_size(20)
  651. layer.move_to(
  652. sx+50,
  653. sy+25
  654. )
  655. if task["string"].startswith("#"):
  656. UI_color.set(layer, win, "progress_background")
  657. if not task["subtasks"]:
  658. layer.move_to(
  659. sx+10,
  660. sy+25
  661. )
  662. layer.show_text(task["string"][1:])
  663. else:
  664. UI_color.set(layer, win, "text_normal")
  665. layer.show_text(task["string"])
  666. else:
  667. UI_elements.text(layer, win, "editing_task",
  668. sx+39,
  669. sy,
  670. width - cXY[0] - 40,
  671. 40,
  672. set_text=task["string"],
  673. offset=[x,y])
  674. win.textactive = "editing_task"
  675. # Assigning the text
  676. def do():
  677. task["string"] = win.text["editing_task"]["text"]
  678. win.textactive = ""
  679. filter_tasks(win.checklists[path])
  680. del win.text["editing_task"]
  681. # Saving
  682. save(path, win.checklists[path])
  683. win.multiuser["last_request"] = ""
  684. def button():
  685. do()
  686. win.checklists = {}
  687. win.assets = {}
  688. UI_elements.roundrect(layer, win,
  689. sx+39 + width - cXY[0] - 80,
  690. sy,
  691. 40,
  692. 40,
  693. 10,
  694. button=button,
  695. icon="ok",
  696. tip=talk.text("checked"),
  697. offset=[x,y])
  698. # Enter
  699. if 65293 in win.current["keys"]:
  700. button()
  701. win.current["keys"] = []
  702. # Tab
  703. if 65289 in win.current["keys"]:
  704. do()
  705. tasks.append(
  706. {
  707. "fraction":0.0,
  708. "string":"",
  709. "editing":True,
  710. "selected":True,
  711. "open":False,
  712. "subtasks":[]
  713. }
  714. )
  715. win.current["keys"] = []
  716. # RECURSION
  717. if not thismoving:
  718. if not task["subtasks"]:
  719. cXY[1] = cXY[1] + 50
  720. else:
  721. cXY[1] = cXY[1] + 70
  722. cXY[0] = cXY[0] + 20
  723. # THERE IS YOUR RECURSION
  724. if task["open"]:
  725. draw_tasks(task["subtasks"], cXY, schedulepath)
  726. cXY[0] = cXY[0] - 20
  727. # Adding subtasks
  728. if ((task["subtasks"] and task["open"] and task["selected"])\
  729. or (task["selected"] and not task["subtasks"]))\
  730. and win.textactive != "editing_task"\
  731. and win.current["tool"] == "selection":
  732. cXY[0] = cXY[0] + 20
  733. def do():
  734. task["open"] = True
  735. filter_tasks(win.checklists[path])
  736. task["subtasks"].append(
  737. {
  738. "fraction":0.0,
  739. "string":"",
  740. "editing":True,
  741. "selected":True,
  742. "open":False,
  743. "subtasks":[]
  744. }
  745. )
  746. UI_elements.roundrect(layer, win,
  747. cXY[0],
  748. win.scroll["checklist"] + cXY[1],
  749. width - cXY[0],
  750. 40,
  751. 10,
  752. button=do,
  753. icon="new",
  754. offset=[x,y])
  755. UI_color.set(layer, win, "progress_background")
  756. UI_elements.roundrect(layer, win,
  757. cXY[0],
  758. win.scroll["checklist"] + cXY[1],
  759. width - cXY[0],
  760. 40,
  761. 10,
  762. fill=False)
  763. layer.stroke()
  764. layer.set_font_size(20)
  765. layer.move_to(
  766. cXY[0]+50,
  767. win.scroll["checklist"] + cXY[1]+25
  768. )
  769. layer.show_text(talk.text("add_new_subtask"))
  770. for line in range(int(cXY[0]/20)):
  771. line = line * 20 + 10
  772. UI_color.set(layer, win, "node_background")
  773. layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
  774. layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
  775. layer.stroke()
  776. cXY[0] = cXY[0] - 20
  777. cXY[1] = cXY[1] + 50
  778. schedulepath = []
  779. draw_tasks(win.checklists[path]["subtasks"], cXY, schedulepath)
  780. # Go to the bottom.
  781. if win.current["tool"] == "grab"\
  782. and win.current["my"] - y > win.scroll["checklist"] + cXY[1]:
  783. if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
  784. win.checklists[path]["subtasks"].append(win.current["moving_task_now"][0])
  785. win.current["tool"] = "selection"
  786. win.current["LMB"] = False
  787. win.previous["LMB"] = False
  788. # Saving
  789. save(path, win.checklists[path])
  790. win.checklists = {}
  791. win.assets = {}
  792. win.analytics = analytics.load(win.project)
  793. win.multiuser["last_request"] = ""
  794. # Adding a task.
  795. if win.textactive != "editing_task"\
  796. and win.current["tool"] == "selection":
  797. def do():
  798. filter_tasks(win.checklists[path])
  799. win.checklists[path]["subtasks"].append(
  800. {
  801. "fraction":0.0,
  802. "string":"",
  803. "editing":True,
  804. "selected":True,
  805. "open":False,
  806. "subtasks":[]
  807. }
  808. )
  809. UI_elements.roundrect(layer, win,
  810. cXY[0],
  811. win.scroll["checklist"] + cXY[1],
  812. width - cXY[0],
  813. 40,
  814. 10,
  815. button=do,
  816. icon="new",
  817. offset=[x,y])
  818. UI_color.set(layer, win, "progress_background")
  819. UI_elements.roundrect(layer, win,
  820. cXY[0],
  821. win.scroll["checklist"] + cXY[1],
  822. width - cXY[0],
  823. 40,
  824. 10,
  825. fill=False)
  826. layer.stroke()
  827. layer.set_font_size(20)
  828. layer.move_to(
  829. cXY[0]+50,
  830. win.scroll["checklist"] + cXY[1]+25
  831. )
  832. layer.show_text(talk.text("add_new_task"))
  833. tileX, current_Y = cXY
  834. # So there would not be jumps of stuff. Let's add heigh to the scroll while
  835. # in grab.
  836. if win.current["tool"] == "grab":
  837. current_Y = current_Y + height
  838. # Outputting the layer
  839. outlayer.set_source_surface(surface, x, y)
  840. outlayer.paint()
  841. # Scroll
  842. UI_elements.scroll_area(outlayer, win, "checklist",
  843. x+0,
  844. y+50,
  845. width,
  846. height-50,
  847. current_Y,
  848. bar=True,
  849. mmb=True)