summary refs log tree commit diff stats
path: root/config/accounts.conf
blob: a25c64e8fe9cb423936b9a38239b442f95ec755d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#
# aerc accounts configuration
#
# This file configures each of the available mail accounts.

# You may add an arbitrary number of sections like so:
#
# [Personal]
# source=imaps://username[:password]@hostname[:port]
#
# [Work]
# source=imaps://username[:password]@hostname[:port]
# folders=INBOX,Sent,Archives
# default=Archives
#
# Each supported protocol may have some arbitrary number of extra configuration
# options. See aerc-[protocol](5) for details (i.e. aerc-imap).
href='#n208'>208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
import unicode
import options

import layout/box
import html/tags
import html/dom
import css/values
import utils/twtstr
import io/term

func cells_in(l: CSSLength, state: LayoutState, d: int, p: Option[int], o: bool): int =
  return cells(l, d, state.term.width_px, state.term.height_px, p, o)

func cells_w(l: CSSLength, state: LayoutState, p: int): int =
  return l.cells_in(state, state.term.ppc, p.some, true)

func cells_h(l: CSSLength, state: LayoutState, p: Option[int]): int =
  return l.cells_in(state, state.term.ppl, p, false)

func cells_h(l: CSSLength, state: LayoutState, p: int): int =
  return l.cells_in(state, state.term.ppl, p.some, false)

func newInlineContext*(): InlineContext =
  new(result)
  result.whitespace = true
  result.ws_initial = true

func newBlockContext(): BlockContext =
  new(result)

proc putRows(icontext: InlineContext) =
  var i = 0
  while i < icontext.rows.len:
    icontext.rows[i].bottom = icontext.fromy
    inc i

proc flushRows(icontext: InlineContext) =
  if icontext.thisrow.len == 0: return
  icontext.putRows()
  var y = 0
  var re = false
  y = icontext.thisrow[0].bottom
  var i = 1
  while i < icontext.thisrow.len:
    let ry = icontext.thisrow[i].bottom
    if y != ry:
      re = true
      if y < ry:
        y = ry
    inc i

  if re:
    i = 0
    while i < icontext.thisrow.len:
      icontext.thisrow[i].y = y + icontext.thisrow[i].y - icontext.thisrow[i].bottom
      inc i
  icontext.rows.add(icontext.thisrow)
  icontext.thisrow.setLen(0)

proc flushConty(box: CSSBox) =
  box.icontext.flushRows()
  box.icontext.fromx = box.x
  inc box.icontext.fromy
  inc box.bcontext.fromy
  box.icontext.conty = false
  box.icontext.whitespace = true
  box.icontext.ws_initial = true

proc flushMargins(box: CSSBox) =
  box.icontext.fromy += box.bcontext.margin_todo
  box.bcontext.margin_done += box.bcontext.margin_todo
  box.bcontext.margin_todo = 0

proc applyBlockStart(state: LayoutState, box, parent: CSSBox, vals: CSSSpecifiedValues) =
  parent.flushMargins()
  box.bcontext = newBlockContext()
  box.x += vals{"margin-left"}.cells_w(state, parent.bcontext.width)

  let mtop = vals{"margin-top"}.cells_h(state, parent.bcontext.width)
  if mtop > parent.bcontext.margin_done or mtop < 0:
    let diff = mtop - parent.bcontext.margin_done
    parent.icontext.fromy += diff
    parent.bcontext.margin_done += diff

  box.y = parent.icontext.fromy
  box.bcontext.margin_done = parent.bcontext.margin_done

  let pwidth = vals{"width"}
  if pwidth.auto:
    box.bcontext.width = parent.bcontext.width
  else:
    box.bcontext.width = pwidth.cells_w(state, parent.bcontext.width)

  let pheight = vals{"height"}
  if not pheight.auto:
    if pheight.unit != UNIT_PERC or parent.bcontext.height.issome:
      box.bcontext.height = pheight.cells_h(state, parent.bcontext.height).some

  box.cssvalues = vals

func newBlockBox(state: var LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSBlockBox =
  new(result)
  result.t = BLOCK
  if parent.icontext.conty:
    parent.flushConty()
  result.x = parent.x

  state.applyBlockStart(result, parent, vals)
  result.icontext = newInlineContext()
  result.icontext.fromy = result.y
  result.icontext.fromx = result.x

func newInlineBlockBox*(state: LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSInlineBlockBox =
  new(result)
  result.t = INLINE_BLOCK
  result.x = parent.icontext.fromx

  state.applyBlockStart(result, parent, vals)
  result.icontext = newInlineContext()
  result.icontext.fromy = result.y
  result.icontext.fromx = result.x

func newInlineBox*(state: LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSInlineBox =
  new(result)
  result.t = INLINE
  result.x = parent.x
  result.y = parent.icontext.fromy

  result.icontext = parent.icontext
  result.bcontext = parent.bcontext
  result.cssvalues = vals
  result.icontext.fromx += vals{"margin-left"}.cells_w(state, parent.bcontext.width)

type InlineState = object
  icontext: InlineContext
  bcontext: BlockContext
  rowbox: CSSRowBox
  word: seq[Rune]
  ww: int
  skip: bool
  nodes: seq[Node]
  cssvalues: CSSSpecifiedValues
  x: int

proc newRowBox(state: var InlineState) =
  state.rowbox = CSSRowBox()
  state.rowbox.x = state.icontext.fromx
  state.rowbox.y = state.icontext.fromy
  state.rowbox.bottom = state.rowbox.y

  let cssvalues = state.cssvalues
  state.rowbox.color = cssvalues{"color"}
  state.rowbox.fontstyle = cssvalues{"font-style"}
  state.rowbox.fontweight = cssvalues{"font-weight"}
  state.rowbox.textdecoration = cssvalues{"text-decoration"}
  state.rowbox.nodes = state.nodes

proc addRowBox(state: var InlineState) =
  state.icontext.thisrow.add(state.rowbox)

proc inlineWrap(state: var InlineState) =
  state.addRowBox()
  state.icontext.flushRows()
  inc state.icontext.fromy
  state.icontext.fromx = state.x
  if state.word.len == 0:
    state.icontext.whitespace = true
    state.icontext.ws_initial = true
    state.icontext.conty = false
  else:
    if state.word[^1] == Rune(' '):
      state.icontext.whitespace = true
      state.icontext.ws_initial = false
    state.icontext.conty = true
  state.newRowBox()

proc addWord(state: var InlineState) =
  state.rowbox.str &= $state.word
  state.rowbox.width += state.ww
  state.word.setLen(0)
  state.ww = 0

proc wrapNormal(state: var InlineState, r: Rune) =
  if state.icontext.fromx + state.rowbox.width + state.ww == state.bcontext.width and r == Rune(' '):
    state.addWord()
  if state.word.len == 0:
    if r == Rune(' '):
      state.skip = true
  elif state.word[0] == Rune(' '):
    state.word = state.word.substr(1)
    dec state.ww
  state.inlineWrap()
  if not state.skip and r == Rune(' '):
    state.icontext.whitespace = true
    state.icontext.ws_initial = false

proc checkWrap(state: var InlineState, r: Rune) =
  if state.cssvalues{"white-space"} in {WHITESPACE_NOWRAP, WHITESPACE_PRE}:
    return
  case state.cssvalues{"word-break"}
  of WORD_BREAK_NORMAL:
    if state.icontext.fromx + state.rowbox.width > state.x and
        state.icontext.fromx + state.rowbox.width + state.ww + r.width() > state.x + state.bcontext.width:
      state.wrapNormal(r)
  of WORD_BREAK_BREAK_ALL:
    if state.icontext.fromx + state.rowbox.width + state.ww + r.width() > state.x + state.bcontext.width:
      var pl: seq[Rune]
      var i = 0
      var w = 0
      while i < state.word.len and
          state.icontext.fromx + state.rowbox.width + w <
            state.bcontext.width:
        pl &= state.word[i]
        w += state.word[i].width()
        inc i

      if pl.len > 0:
        state.rowbox.str &= $pl
        state.rowbox.width += w
        state.word = state.word.substr(pl.len)
        state.ww = state.word.width()
      if r == Rune(' '):
        state.skip = true
      state.inlineWrap()
  of WORD_BREAK_KEEP_ALL:
    if state.icontext.fromx + state.rowbox.width > state.x and
        state.icontext.fromx + state.rowbox.width + state.ww + r.width() > state.x + state.bcontext.width:
      state.wrapNormal(r)

proc preWrap(state: var InlineState) =
  state.inlineWrap()
  state.icontext.whitespace = false
  state.icontext.ws_initial = true
  state.skip = true

proc processInlineText(str: string, icontext: InlineContext,
                       bcontext: BlockContext, cssvalues: CSSSpecifiedValues,
                       x: int, nodes: seq[Node]) =
  var state: InlineState
  state.icontext = icontext
  state.bcontext = bcontext
  state.cssvalues = cssvalues
  state.x = x
  state.nodes = nodes

  var i = 0
  state.newRowBox()

  var r: Rune
  while i < str.len:
    var rw = 0
    case str[i]
    of ' ', '\n', '\t':
      rw = 1
      r = Rune(str[i])
      inc i
      state.addWord()

      case state.cssvalues{"white-space"}
      of WHITESPACE_NORMAL, WHITESPACE_NOWRAP:
        if state.icontext.whitespace:
          if state.icontext.ws_initial:
            state.icontext.ws_initial = false
            state.skip = true
          else:
            state.skip = true
        state.icontext.whitespace = true
      of WHITESPACE_PRE_LINE:
        if state.icontext.whitespace:
          state.skip = true
        state.icontext.ws_initial = false
        if r == Rune('\n'):
          state.preWrap()
      of WHITESPACE_PRE, WHITESPACE_PRE_WRAP:
        state.icontext.ws_initial = false
        if r == Rune('\n'):
          state.preWrap()
      r = Rune(' ')
    else:
      state.icontext.whitespace = false
      fastRuneAt(str, i, r)
      rw = r.width()

    # TODO a better line wrapping algorithm would be nice... especially because
    # this one doesn't even work
    if rw > 1 or state.cssvalues{"word-break"} == WORD_BREAK_BREAK_ALL:
      state.addWord()

    state.checkWrap(r)

    if state.skip:
      state.skip = false
      continue

    state.word &= r
    state.ww += rw

  state.addWord()

  if state.rowbox.str.len > 0:
    state.addRowBox()
    state.icontext.fromx += state.rowbox.width
    state.icontext.conty = true

  state.bcontext.margin_todo = 0
  state.bcontext.margin_done = 0

proc processInlineContext(ibox: CSSInlineBox, str: string, nodes: seq[Node]) =
  processInlineText(str, ibox.icontext, ibox.bcontext, ibox.cssvalues, ibox.x, nodes)

proc processInlineBox(state: var LayoutState, parent: CSSBox, str: string): CSSInlineBox =
  if str.len == 0:
    return nil

  #TODO this doesn't really belong in here
  parent.flushMargins()

  if parent of CSSInlineBox:
    let ibox = CSSInlineBox(parent)
    ibox.processInlineContext(str, state.nodes)
    return nil

  let ibox = state.newInlineBox(parent, parent.cssvalues.inheritProperties())
  ibox.processInlineContext(str, state.nodes)
  return ibox

proc applyBlockEnd(state: var LayoutState, parent, box: CSSBox) =
  box.flushMargins()

  let mbot = box.cssvalues{"margin-bottom"}.cells_h(state, parent.bcontext.width)
  parent.bcontext.margin_todo += mbot

  parent.bcontext.margin_done = box.bcontext.margin_done
  parent.bcontext.margin_todo = max(parent.bcontext.margin_todo - box.bcontext.margin_done, 0)

  if box.bcontext.height.isnone:
    parent.icontext.fromy = box.icontext.fromy
  else:
    parent.icontext.fromy += box.bcontext.height.get

proc add(state: var LayoutState, parent: CSSBox, box: CSSBlockBox) =
  parent.icontext.fromx = parent.x
  if box.icontext.conty:
    box.flushConty()

  state.applyBlockEnd(parent, box)
  parent.children.add(box)

proc add(state: var LayoutState, parent: CSSBox, box: CSSInlineBox) =
  parent.icontext.fromx += box.cssvalues{"margin-right"}.cells_w(state, parent.bcontext.width)
  parent.icontext.fromy = box.icontext.fromy

  parent.children.add(box)

proc add(state: var LayoutState, parent: CSSBox, box: CSSInlineBlockBox) =
  parent.icontext.fromx = max(box.icontext.fromx, box.bcontext.width)
  parent.icontext.fromx += box.cssvalues{"margin-right"}.cells_w(state, parent.bcontext.width)
  parent.icontext.conty = box.icontext.conty

  box.icontext.putRows()
  parent.icontext.thisrow.add(box.icontext.rows)
  parent.icontext.thisrow.add(box.icontext.thisrow)
  box.icontext.rows.setLen(0)
  box.icontext.thisrow.setLen(0)

  state.applyBlockEnd(parent, box)
  parent.children.add(box)

proc add(state: var LayoutState, parent: CSSBox, box: CSSBox) =
  case box.t
  of BLOCK: state.add(parent, CSSBlockBox(box))
  of INLINE: state.add(parent, CSSInlineBox(box))
  of INLINE_BLOCK: state.add(parent, CSSInlineBlockBox(box))

proc processComputedValueBox(state: var LayoutState, parent: CSSBox, values: CSSSpecifiedValues): CSSBox =
  case values{"display"}
  of DISPLAY_BLOCK:
    result = state.newBlockBox(parent, values)
  of DISPLAY_INLINE_BLOCK:
    result = state.newInlineBlockBox(parent, values)
  of DISPLAY_INLINE:
    result = state.newInlineBox(parent, values)
  of DISPLAY_LIST_ITEM:
    result = state.newBlockBox(parent, values)
  of DISPLAY_NONE:
    return nil
  else:
    return nil

proc processBr(state: var LayoutState, parent: CSSBox, vals: CSSSpecifiedValues) =
  if vals{"display"} == DISPLAY_INLINE:
    if parent.icontext.conty:
      parent.flushConty()
    else:
      inc parent.bcontext.fromy
      inc parent.icontext.fromy
    parent.icontext.fromx = parent.x

proc processElemBox(state: var LayoutState, parent: CSSBox, elem: Element): CSSBox =
  if elem.tagType == TAG_BR:
    state.processBr(parent, elem.css)

  result = state.processComputedValueBox(parent, elem.css)
  if result != nil:
    result.node = elem

proc processElemChildren(state: var LayoutState, parent: CSSBox, elem: Element)

proc processNode(state: var LayoutState, parent: CSSBox, node: Node): CSSBox =
  case node.nodeType
  of ELEMENT_NODE:
    let elem = Element(node)
    result = state.processElemBox(parent, Element(node))
    if result == nil:
      return

    state.processElemChildren(result, elem)
  of TEXT_NODE:
    let text = Text(node)
    result = state.processInlineBox(parent, text.data)
    if result != nil:
      result.node = node
  else: discard

proc processBeforePseudoElem(state: var LayoutState, parent: CSSBox, elem: Element) =
  if elem.pseudo[PSEUDO_BEFORE] != nil:
    let box = state.processComputedValueBox(parent, elem.pseudo[PSEUDO_BEFORE])
    if box == nil: return
    box.node = elem

    let text = elem.pseudo[PSEUDO_BEFORE]{"content"}
    var inline = state.processInlineBox(box, $text)
    if inline != nil:
      inline.node = elem
      state.add(box, inline)

    state.add(parent, box)

proc processAfterPseudoElem(state: var LayoutState, parent: CSSBox, elem: Element) =
  if elem.pseudo[PSEUDO_AFTER] != nil:
    let box = state.processComputedValueBox(parent, elem.pseudo[PSEUDO_AFTER])
    if box == nil: return
    box.node = elem

    let text = elem.pseudo[PSEUDO_AFTER]{"content"}
    var inline = state.processInlineBox(box, $text)
    if inline != nil:
      inline.node = elem
      state.add(box, inline)

    state.add(parent, box)

proc processMarker(state: var LayoutState, parent: CSSBox, elem: Element) =
  if elem.css{"display"} == DISPLAY_LIST_ITEM:
    var ordinalvalue = 1
    if elem.tagType == TAG_LI:
      ordinalvalue = HTMLLIElement(elem).ordinalvalue

    let text = elem.css{"list-style-type"}.listMarker(ordinalvalue)
    let tlen = text.width()
    parent.icontext.fromx -= tlen
    let marker = state.processInlineBox(parent, text)
    if marker != nil:
      state.add(parent, marker)

proc processNodes(state: var LayoutState, parent: CSSBox, nodes: seq[Node]) =
  for node in nodes:
    let box = state.processNode(parent, node)
    if box != nil:
      state.add(parent, box)

proc processElemChildren(state: var LayoutState, parent: CSSBox, elem: Element) =
  state.nodes.add(elem)

  state.processBeforePseudoElem(parent, elem)
  state.processMarker(parent, elem)
  state.processNodes(parent, elem.childNodes)
  state.processAfterPseudoElem(parent, elem)

  discard state.nodes.pop()

proc alignBoxes*(document: Document, term: TermAttributes): CSSBox =
  var state: LayoutState
  state.term = term
  var rootbox = CSSBlockBox(x: 0, y: 0)
  rootbox.cssvalues = rootProperties()
  rootbox.bcontext = newBlockContext()
  rootbox.icontext = newInlineContext()
  rootbox.bcontext.width = term.width
  state.nodes.add(document.root)
  state.processElemChildren(rootbox, document.root)
  return rootbox