github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/GUI/messages/messages.go (about)

     1  package messages
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/therecipe/qt/core"
     7  
     8  	"github.com/benoitkugler/goACVE/client/GUI/basic"
     9  	"github.com/benoitkugler/goACVE/client/controllers"
    10  	dm "github.com/benoitkugler/goACVE/server/core/datamodel"
    11  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    12  	"github.com/therecipe/qt/widgets"
    13  )
    14  
    15  const style = `
    16  	.QListWidget {
    17  		background: transparent;
    18  	}
    19  
    20  	QFrame#message {
    21  		border: 1px solid transparent;
    22  		border-radius: 10px;
    23  	}
    24  `
    25  
    26  type messageRow struct {
    27  	*widgets.QFrame
    28  	contenu widgets.QWidget_ITF
    29  	kind    rd.MessageKind
    30  
    31  	OnMarqueVu      func(bool)
    32  	OnEdit          func(allGroupe bool)
    33  	OnDelete        func(allGroupe bool)
    34  	OnMessageGroupe func()
    35  }
    36  
    37  func newMessageRow(ct *controllers.SuiviDossiers, message controllers.PseudoMessage) *messageRow {
    38  	m := messageRow{QFrame: basic.Frame(), kind: message.Kind}
    39  	lay := widgets.NewQVBoxLayout2(nil)
    40  	lay.SetContentsMargins(5, 5, 5, 5)
    41  	m.SetLayout(lay)
    42  	m.SetObjectName("message")
    43  	m.SetStyleSheet(m.StyleSheet() + fmt.Sprintf("QFrame#message { background: %s; }", message.Color.AHex()))
    44  
    45  	modifie := ""
    46  	if !message.Modified.Time().IsZero() {
    47  		modifie = fmt.Sprintf("modifié le %s", message.Modified)
    48  	}
    49  	infos := basic.Label(fmt.Sprintf("%s <i>%s</i>", message.Created, modifie))
    50  	infos.SetMinimumHeight(25)
    51  
    52  	title := basic.Label(fmt.Sprintf("<b>%s</b>", message.Label))
    53  	title.SetMinimumHeight(25)
    54  
    55  	buttonF := basic.Frame()
    56  	buttons := widgets.NewQHBoxLayout2(buttonF)
    57  	buttons.SetContentsMargins(0, 0, 0, 0)
    58  	isInGroupe := len(message.Groupe) > 0
    59  	if isInGroupe {
    60  		view := widgets.NewQToolButton(nil)
    61  		view.SetIcon(basic.Icons.Mails)
    62  		view.SetToolTip("Répondre au groupe")
    63  		view.ConnectClicked(func(vu bool) { m.OnMessageGroupe() })
    64  		buttons.AddWidget(view, 1, core.Qt__AlignRight)
    65  	}
    66  	if message.IsEditable {
    67  		view := widgets.NewQToolButton(nil)
    68  		view.SetIcon(basic.Icons.Edit)
    69  		view.SetToolTip("Modifier le message...")
    70  		if isInGroupe {
    71  			menu := widgets.NewQMenu(nil)
    72  			view.SetMenu(menu)
    73  			view.SetPopupMode(widgets.QToolButton__InstantPopup)
    74  			modifGroupe := menu.AddAction(fmt.Sprintf("Modifier les %d messages du groupe", len(message.Groupe)))
    75  			modifGroupe.ConnectTriggered(func(_ bool) {
    76  				m.OnEdit(true)
    77  			})
    78  			modifSolo := menu.AddAction("Modifier uniquement ce message")
    79  			modifSolo.ConnectTriggered(func(_ bool) {
    80  				m.OnEdit(false)
    81  			})
    82  		} else {
    83  			view.ConnectClicked(func(vu bool) { m.OnEdit(false) })
    84  		}
    85  		buttons.AddWidget(view, 1, core.Qt__AlignRight)
    86  	}
    87  
    88  	if message.IsDeletable {
    89  		view := widgets.NewQToolButton(nil)
    90  		view.SetIcon(basic.Icons.Delete)
    91  		view.SetToolTip("Supprimer le message")
    92  		if isInGroupe {
    93  			menu := widgets.NewQMenu(nil)
    94  			view.SetMenu(menu)
    95  			view.SetPopupMode(widgets.QToolButton__InstantPopup)
    96  			modifGroupe := menu.AddAction(fmt.Sprintf("Supprimer les %d messages du groupe", len(message.Groupe)))
    97  			modifGroupe.ConnectTriggered(func(_ bool) {
    98  				m.OnDelete(true)
    99  			})
   100  			modifSolo := menu.AddAction("Supprimer uniquement ce message")
   101  			modifSolo.ConnectTriggered(func(_ bool) {
   102  				m.OnDelete(false)
   103  			})
   104  		} else {
   105  			view.ConnectClicked(func(vu bool) { m.OnDelete(false) })
   106  		}
   107  		buttons.AddWidget(view, 1, core.Qt__AlignRight)
   108  	}
   109  
   110  	if message.IsViewable {
   111  		view := widgets.NewQToolButton(nil)
   112  		view.SetIcon(basic.Icons.Star)
   113  		view.SetCheckable(true)
   114  		view.SetChecked(message.Vu)
   115  		tooltip := "Marquer comme vu"
   116  		if message.Vu {
   117  			tooltip = "Marquer comme non vu"
   118  		}
   119  		view.SetToolTip(tooltip)
   120  		view.ConnectClicked(func(vu bool) { m.OnMarqueVu(vu) })
   121  		if !message.Vu {
   122  			buttons.AddWidget(basic.Label("<b>Nouveau</b>"), 2, core.Qt__AlignRight)
   123  		}
   124  		buttons.AddWidget(view, 1, core.Qt__AlignRight)
   125  	}
   126  
   127  	header := widgets.NewQHBoxLayout()
   128  	infos.SetSizePolicy2(widgets.QSizePolicy__Ignored, widgets.QSizePolicy__Expanding)
   129  	title.SetSizePolicy2(widgets.QSizePolicy__Ignored, widgets.QSizePolicy__Expanding)
   130  	buttonF.SetSizePolicy2(widgets.QSizePolicy__Ignored, widgets.QSizePolicy__Expanding)
   131  
   132  	header.AddWidget(infos, 3, 0)
   133  	header.AddWidget(title, 2, 0)
   134  	header.AddWidget(buttonF, 1, core.Qt__AlignRight)
   135  
   136  	m.contenu = initContenu(ct, message.PseudoMessage)
   137  
   138  	lay.AddLayout(header, 1)
   139  	if m.contenu != nil {
   140  		lay.AddWidget(m.contenu, 3, 0)
   141  	}
   142  	return &m
   143  }
   144  
   145  // dispatch suivant le type de message
   146  func initContenu(ct *controllers.SuiviDossiers, message dm.PseudoMessage) widgets.QWidget_ITF {
   147  	switch message.Kind {
   148  	case rd.MResponsable, rd.MCentre:
   149  		return newContenuPerso(message.Contenu.(dm.ContenuPerso))
   150  	case rd.MFacture, rd.MFactureAcquittee:
   151  		ac := ct.Base.NewFacture(message.IdFacture)
   152  		isRappel := ac.GetEtat(nil, nil).IsFactureRappel(message)
   153  		w := newContenuFacture(message.Kind == rd.MFactureAcquittee, isRappel, message.Modified)
   154  		w.onShow = func() {
   155  			ct.Onglet.ShowPreviewFacture(ac)
   156  		}
   157  		return w
   158  	case rd.MDocuments:
   159  		w := newContenuDocument(ct.Base, message.Contenu.(dm.ContenuDocument))
   160  		w.onGoToCamp = func(id int64) {
   161  			ct.Main().Controllers.Camps.SelectCamp(id)
   162  		}
   163  		return w
   164  	case rd.MAttestationPresence:
   165  		w := newContenuAttestation(message.Modified)
   166  		w.onShow = func() {
   167  			path := ct.RenderAttestation()
   168  			if path != "" {
   169  				basic.ShowFile(path)
   170  			}
   171  		}
   172  		return w
   173  	case rd.MSondage:
   174  		w := newContenuSondage(ct.Base, message.Contenu.(dm.ContenuSondage))
   175  		w.onGoToCamp = func(id int64) {
   176  			ct.Main().Controllers.Camps.SelectCamp(id)
   177  		}
   178  		return w
   179  	case rd.MPlaceLiberee:
   180  		w := newContenuPlaceLiberee(ct.Base, message.Contenu.(dm.ContenuPlaceLiberee))
   181  		w.onGoToParticipant = func(id int64) {
   182  			ct.Main().Controllers.Camps.SelectParticipant(ct.Base.NewParticipant(id))
   183  		}
   184  		return w
   185  	case rd.MPaiement:
   186  		w := newContenuPaiement(ct.Base, message.Id)
   187  		w.onGoToPaiement = func(id int64) {
   188  			ct.Main().Controllers.Paiements.SelectPaiement(id)
   189  		}
   190  		return w
   191  	default:
   192  		return nil
   193  	}
   194  }
   195  
   196  type List struct {
   197  	*widgets.QListWidget
   198  	msgs []*messageRow // a synchroniser
   199  
   200  	controller *controllers.SuiviDossiers
   201  
   202  	scrollPositions map[int64]int
   203  
   204  	OnMessageGroupe func(rd.Ids)
   205  }
   206  
   207  func NewList(ct *controllers.SuiviDossiers) *List {
   208  	out := &List{QListWidget: widgets.NewQListWidget(nil)}
   209  	out.controller = ct
   210  	out.scrollPositions = map[int64]int{}
   211  	out.SetStyleSheet(out.StyleSheet() + style)
   212  	out.SetVerticalScrollMode(widgets.QAbstractItemView__ScrollPerPixel)
   213  	out.SetSelectionMode(widgets.QAbstractItemView__NoSelection)
   214  	out.SetSpacing(1)
   215  	out.VerticalScrollBar().ConnectValueChanged(out.storeScrollPosition)
   216  	return out
   217  }
   218  
   219  func (m List) storeScrollPosition(value int) {
   220  	id := m.controller.Etat.FactureCurrent
   221  	if id == nil {
   222  		return
   223  	}
   224  	m.scrollPositions[id.Int64()] = value
   225  }
   226  
   227  func (m List) restoreScrollPosition() (int, bool) {
   228  	id := m.controller.Etat.FactureCurrent
   229  	if id != nil {
   230  		s, has := m.scrollPositions[id.Int64()]
   231  		return s, has
   232  	}
   233  	return 0, false
   234  }
   235  
   236  func (m List) applyScroll(scroll int, hasScroll bool) {
   237  	if hasScroll {
   238  		m.VerticalScrollBar().SetValue(scroll)
   239  	} else {
   240  		m.ScrollToBottom()
   241  	}
   242  }
   243  
   244  // SetData reset la liste et affiche les messages.
   245  func (m *List) SetData(messages []controllers.PseudoMessage) {
   246  	// save scroll value before modifying
   247  	scroll, hasScroll := m.restoreScrollPosition()
   248  
   249  	// on ajoute les lignes manquantes
   250  	for i := m.Count(); i < len(messages); i++ {
   251  		li := widgets.NewQListWidgetItem(nil, 0)
   252  		m.AddItem2(li)
   253  	}
   254  	// on supprime les lignes en trop
   255  	maxI := m.Count()
   256  	for i := len(messages); i < maxI; i++ {
   257  		//attention TakeItem redimensionne la liste
   258  		m.TakeItem(m.Count() - 1)
   259  	}
   260  	m.msgs = make([]*messageRow, len(messages))
   261  	for i, message := range messages {
   262  		message := message // closure in loop
   263  		wg := newMessageRow(m.controller, message)
   264  		wg.SetMaximumWidth(m.Width())
   265  
   266  		wg.OnMarqueVu = func(b bool) {
   267  			m.controller.ToggleMessageVu(message.Id, b)
   268  		}
   269  		wg.OnMessageGroupe = func() {
   270  			m.OnMessageGroupe(message.Groupe)
   271  		}
   272  		wg.OnEdit = func(allGroupe bool) {
   273  			ids := rd.Ids{message.Id}
   274  			if allGroupe {
   275  				ids = message.Groupe
   276  			}
   277  			contenu, ok := EditMessage(string(message.Contenu.(dm.ContenuPerso)), false, len(ids))
   278  			if !ok {
   279  				m.controller.Main().ShowStandard("Edition annulée.", false)
   280  				return
   281  			}
   282  			m.controller.EditMessageCentre(ids, contenu)
   283  		}
   284  		wg.OnDelete = func(allGroupe bool) {
   285  			ids := rd.Ids{message.Id}
   286  			if allGroupe {
   287  				ids = message.Groupe
   288  			}
   289  			m.controller.SupprimeMessages(ids)
   290  		}
   291  		m.set(i, wg)
   292  		m.msgs[i] = wg
   293  	}
   294  	m.applyScroll(scroll, hasScroll)
   295  }
   296  
   297  func (m *List) set(i int, wg *messageRow) {
   298  	item := m.Item(i)
   299  	item.SetSizeHint(wg.SizeHint())
   300  	m.SetItemWidget(item, wg)
   301  }
   302  
   303  // ToogleContent montre/masque le contenu de tous les messages
   304  func (m *List) ToogleContent(visible bool) {
   305  	for i, wg := range m.msgs {
   306  		if wg.contenu == nil {
   307  			continue
   308  		}
   309  		wg.contenu.QWidget_PTR().SetVisible(visible)
   310  		m.set(i, wg)
   311  	}
   312  }
   313  
   314  // ShowDeleted montre/masque les message supprimés
   315  func (m *List) ShowDeleted(show bool) {
   316  	for i, msg := range m.msgs {
   317  		m.SetRowHidden(i, !show && msg.kind == rd.MSupprime)
   318  	}
   319  }