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 }